From f2efe30a06e07682b2dd4762d1c1733bebd71534 Mon Sep 17 00:00:00 2001 From: ckt1i Date: Fri, 11 Oct 2024 21:01:52 +0800 Subject: [PATCH 1/6] modified: Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 09d790cf63..73188b5f12 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ AS = $(TOOLPREFIX)gas LD = $(TOOLPREFIX)ld OBJCOPY = $(TOOLPREFIX)objcopy OBJDUMP = $(TOOLPREFIX)objdump -CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer +CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -fno-stack-protector -fno-pie -no-pie -Wno-error CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) ASFLAGS = -m32 -gdwarf-2 -Wa,-divide # FreeBSD ld wants ``elf_i386_fbsd'' @@ -219,7 +219,7 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ ifndef CPUS CPUS := 2 endif -QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA) +QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 -nographic $(QEMUEXTRA) qemu: fs.img xv6.img $(QEMU) -serial mon:stdio $(QEMUOPTS) From ee1398c737937dbc3dfd13d4ab091e7d164fd553 Mon Sep 17 00:00:00 2001 From: ckt1i Date: Sat, 12 Oct 2024 00:10:31 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=AF=B9=E7=A8=8B=E5=BA=8F=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=88=9D=E6=AD=A5=E4=BF=AE=E6=94=B9=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BA=86=E4=B8=80=E4=B8=AA=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=EF=BC=8C=E4=BB=A5=E5=AE=9E=E7=8E=B0=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20=09modified:=20=20=20Makefile=20=09modifie?= =?UTF-8?q?d:=20=20=20defs.h=20=09modified:=20=20=20exec.c=20=09modified:?= =?UTF-8?q?=20=20=20memlayout.h=20=09modified:=20=20=20proc.c=20=09modifie?= =?UTF-8?q?d:=20=20=20proc.h=20=09modified:=20=20=20syscall.c=20=09new=20f?= =?UTF-8?q?ile:=20=20=20testcase.c=20=09modified:=20=20=20trap.c=20=09modi?= =?UTF-8?q?fied:=20=20=20vm.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + defs.h | 2 +- exec.c | 19 +++++---- memlayout.h | 3 ++ proc.c | 2 +- proc.h | 1 + syscall.c | 14 +++---- testcase.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++ trap.c | 25 ++++++++++++ vm.c | 21 +++++++++- 10 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 testcase.c diff --git a/Makefile b/Makefile index 73188b5f12..51cfab62af 100644 --- a/Makefile +++ b/Makefile @@ -181,6 +181,7 @@ UPROGS=\ _usertests\ _wc\ _zombie\ + _testcase\ fs.img: mkfs README $(UPROGS) ./mkfs fs.img README $(UPROGS) diff --git a/defs.h b/defs.h index 82fb982837..91448d9939 100644 --- a/defs.h +++ b/defs.h @@ -180,7 +180,7 @@ int deallocuvm(pde_t*, uint, uint); void freevm(pde_t*); void inituvm(pde_t*, char*, uint); int loaduvm(pde_t*, char*, struct inode*, uint, uint); -pde_t* copyuvm(pde_t*, uint); +pde_t* copyuvm(pde_t*, uint, uint); void switchuvm(struct proc*); void switchkvm(void); int copyout(pde_t*, uint, void*, uint); diff --git a/exec.c b/exec.c index b40134f352..31a8ed6e66 100644 --- a/exec.c +++ b/exec.c @@ -12,7 +12,7 @@ exec(char *path, char **argv) { char *s, *last; int i, off; - uint argc, sz, sp, ustack[3+MAXARG+1]; + uint argc, sz, stackbase, sp, ustack[3+MAXARG+1]; struct elfhdr elf; struct inode *ip; struct proghdr ph; @@ -60,19 +60,18 @@ exec(char *path, char **argv) end_op(); ip = 0; - // Allocate two pages at the next page boundary. - // Make the first inaccessible. Use the second as the user stack. - sz = PGROUNDUP(sz); - if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0) +// Allocate user stack at the top of the address space. + stackbase = USERTOP - 2*PGSIZE; + if((allocuvm(pgdir, stackbase, USERTOP)) == 0) goto bad; - clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); - sp = sz; + clearpteu(pgdir, (char*)(stackbase)); + sp = USERTOP; // Push argument strings, prepare rest of stack in ustack. for(argc = 0; argv[argc]; argc++) { if(argc >= MAXARG) goto bad; - sp = (sp - (strlen(argv[argc]) + 1)) & ~3; + sp = (sp - (strlen(argv[argc]) + 1)) & ~3; // 4字节对齐 if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) goto bad; ustack[3+argc] = sp; @@ -99,6 +98,10 @@ exec(char *path, char **argv) curproc->sz = sz; curproc->tf->eip = elf.entry; // main curproc->tf->esp = sp; + + // Set the stack base for the process. + curproc->stackbase = stackbase; + switchuvm(curproc); freevm(oldpgdir); return 0; diff --git a/memlayout.h b/memlayout.h index d1615f7abc..ff8ec6c711 100644 --- a/memlayout.h +++ b/memlayout.h @@ -8,6 +8,9 @@ #define KERNBASE 0x80000000 // First kernel virtual address #define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked +// Define the top of the user address space. This is the highest user address +#define USERTOP 0x80000000 // 用户地址空间的顶部 + #define V2P(a) (((uint) (a)) - KERNBASE) #define P2V(a) ((void *)(((char *) (a)) + KERNBASE)) diff --git a/proc.c b/proc.c index 806b1b184b..d330a9501f 100644 --- a/proc.c +++ b/proc.c @@ -190,7 +190,7 @@ fork(void) } // Copy process state from proc. - if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz)) == 0){ + if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz, curproc->stackbase)) == 0){ kfree(np->kstack); np->kstack = 0; np->state = UNUSED; diff --git a/proc.h b/proc.h index 1647114179..5c248a1475 100644 --- a/proc.h +++ b/proc.h @@ -49,6 +49,7 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) + uint stackbase; // Base of the stack }; // Process memory is laid out contiguously, low addresses first: diff --git a/syscall.c b/syscall.c index ee85261602..e16b1dfff1 100644 --- a/syscall.c +++ b/syscall.c @@ -19,8 +19,8 @@ fetchint(uint addr, int *ip) { struct proc *curproc = myproc(); - if(addr >= curproc->sz || addr+4 > curproc->sz) - return -1; +/* if(addr >= curproc->sz || addr+4 > curproc->sz) + return -1; */ *ip = *(int*)(addr); return 0; } @@ -34,8 +34,8 @@ fetchstr(uint addr, char **pp) char *s, *ep; struct proc *curproc = myproc(); - if(addr >= curproc->sz) - return -1; +/* if(addr >= curproc->sz) + return -1; */ *pp = (char*)addr; ep = (char*)curproc->sz; for(s = *pp; s < ep; s++){ @@ -49,7 +49,7 @@ fetchstr(uint addr, char **pp) int argint(int n, int *ip) { - return fetchint((myproc()->tf->esp) + 4 + 4*n, ip); + return fetchint((myproc()->tf->esp) + 4 + 4 * n, ip); } // Fetch the nth word-sized system call argument as a pointer @@ -63,8 +63,8 @@ argptr(int n, char **pp, int size) if(argint(n, &i) < 0) return -1; - if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz) - return -1; + /*if(size < 0 || (uint)i >= curproc->sz || (uint)i+size > curproc->sz) + return -1; */ *pp = (char*)i; return 0; } diff --git a/testcase.c b/testcase.c new file mode 100644 index 0000000000..80b8f6bca6 --- /dev/null +++ b/testcase.c @@ -0,0 +1,115 @@ +#include "types.h" +#include "stat.h" +#include "user.h" + +// 获取当前栈指针 +uint get_stack_pointer() { + uint sp; + asm volatile("movl %%esp, %0" : "=r" (sp)); + return sp; +} + +// 测试栈是否位于堆之上 +void test_stack_position() { + printf(1, "\n=== Test 1: Stack Position ===\n"); + uint sp = get_stack_pointer(); + uint heap_end = (uint)sbrk(0); + + printf(1, "Current stack pointer: 0x%x\n", sp); + printf(1, "Heap end: 0x%x\n", heap_end); + + if (sp > heap_end) { + printf(1, "Test passed: Stack is above heap.\n"); + } else { + printf(1, "Test failed: Stack is not correctly positioned.\n"); + } +} + +// 测试堆和栈是否分离 +void test_heap_stack_separation() { + printf(1, "\n=== Test 2: Heap and Stack Separation ===\n"); + + uint heap_start = (uint)sbrk(0); + sbrk(4096 * 10); // 增加10页堆 + uint heap_end = (uint)sbrk(0); + uint sp = get_stack_pointer(); + + printf(1, "Heap range: 0x%x - 0x%x\n", heap_start, heap_end); + printf(1, "Stack pointer: 0x%x\n", sp); + + if (sp > heap_end) { + printf(1, "Test passed: Stack is above heap after sbrk.\n"); + } else { + printf(1, "Test failed: Stack and heap overlap.\n"); + } +} + +// 递归调用以强制栈增长 +void recursive_call(int depth) { + char buffer[1024]; // 在栈上分配空间,逐步增长栈 + printf(1, "Recursion depth: %d, buffer address: 0x%x\n", depth, buffer); + + // 递归调用,强制栈增长 + if (depth < 100) { + recursive_call(depth + 1); + } +} + +// 测试栈增长 +void test_stack_growth() { + printf(1, "\n=== Test 3: Stack Growth ===\n"); + printf(1, "Testing stack growth with recursion...\n"); + recursive_call(1); // 初始调用 + + printf(1, "Test completed.\n"); +} + +// 深度递归调用以测试更大规模的栈增长 +void deep_recursion(int depth) { + char buffer[4096]; // 分配4KB的空间,确保每次递归占用整页 + printf(1, "Recursion depth: %d, buffer address: 0x%x\n", depth, buffer); + + // 在深度达到一定值之前递归 + if (depth < 200) { + deep_recursion(depth + 1); + } +} + +// 测试深度递归引发栈增长 +void test_deep_stack_growth() { + printf(1, "\n=== Test 4: Deep Stack Growth ===\n"); + printf(1, "Starting deep recursion test...\n"); + + deep_recursion(1); // 初始递归调用,测试栈增长 + + printf(1, "Deep recursion test completed.\n"); +} + +// 强制堆增长以接近栈区域 +void force_stack_heap_collision() { + // 通过增加堆的大小来接近栈区域 + sbrk((KERNBASE - (uint)sbrk(0)) / 2); // 增加到接近栈的区域 + + char buffer[1024]; // 在栈上分配空间 + printf(1, "Allocated stack buffer at: 0x%x\n", buffer); +} + +// 测试堆与栈的冲突 +void test_stack_heap_collision() { + printf(1, "\n=== Test 5: Stack-Heap Collision ===\n"); + + force_stack_heap_collision(); // 强制堆增长,接近栈 + + printf(1, "Test completed.\n"); +} + +int main() { + // 执行所有测试 + test_stack_position(); // 测试栈位置 + test_heap_stack_separation(); // 测试堆与栈的分离 + test_stack_growth(); // 测试栈增长 + test_deep_stack_growth(); // 测试深度递归栈增长 + test_stack_heap_collision(); // 测试堆与栈的冲突 + + exit(); +} diff --git a/trap.c b/trap.c index 41c66ebf9a..5c895b0e4c 100644 --- a/trap.c +++ b/trap.c @@ -77,6 +77,31 @@ trap(struct trapframe *tf) cpuid(), tf->cs, tf->eip); lapiceoi(); break; + + // 新增触发页错误(Page Fault)的情况 + case T_PGFLT: + // 条件检查:确保页面错误地址在用户栈范围内。 + if (rcr2() < USERTOP && rcr2() >= myproc()->stackbase - PGSIZE) { + + //打印调试信息:页面错误地址和当前栈位置 + cprintf("page fault at %x\n", rcr2()); + cprintf("current stack position: %x\n", myproc()->stackbase); + + // 分配新的用户栈页面 + // 尝试分配新的栈页 + if (allocuvm(myproc()->pgdir, myproc()->stackbase - PGSIZE, myproc()->stackbase) == 0) { + myproc()->killed = 1; // 如果分配失败,标记进程为被杀死。 + } + else{ + // 更新栈基地址并打印。 + myproc()->stackbase -= PGSIZE; + cprintf("allocated new stack page at %x\n", myproc()->stackbase); + } + return; + } else { + myproc()->killed = 1; + break; + } //PAGEBREAK: 13 default: diff --git a/vm.c b/vm.c index 7134cfffbe..4ac21fd379 100644 --- a/vm.c +++ b/vm.c @@ -313,7 +313,7 @@ clearpteu(pde_t *pgdir, char *uva) // Given a parent process's page table, create a copy // of it for a child. pde_t* -copyuvm(pde_t *pgdir, uint sz) +copyuvm(pde_t *pgdir, uint sz, uint stackbase) { pde_t *d; pte_t *pte; @@ -339,6 +339,25 @@ copyuvm(pde_t *pgdir, uint sz) } return d; + // Copy the stack + for(i = stackbase; i < USERTOP; i += PGSIZE){ + if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) + panic("copyuvm: pte should exist"); + if(!(*pte & PTE_P)) + panic("copyuvm: page not present"); + pa = PTE_ADDR(*pte); + flags = PTE_FLAGS(*pte); + if((mem = kalloc()) == 0) + goto bad; + memmove(mem, (char*)P2V(pa), PGSIZE); + if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) { + kfree(mem); + goto bad; + } + } + + return d; + bad: freevm(d); return 0; From 70083e0f30cb531850943010d5065fab04c7506c Mon Sep 17 00:00:00 2001 From: ckt1i Date: Sat, 12 Oct 2024 20:03:34 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E9=83=A8=E5=88=86=E4=BF=AE=E6=94=B9=20=09m?= =?UTF-8?q?odified:=20=20=20exec.c=20=09modified:=20=20=20memlayout.h=20?= =?UTF-8?q?=09modified:=20=20=20testcase.c=20=09modified:=20=20=20vm.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exec.c | 9 +++++---- memlayout.h | 5 +++-- testcase.c | 3 ++- vm.c | 3 +-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/exec.c b/exec.c index 31a8ed6e66..68635457bb 100644 --- a/exec.c +++ b/exec.c @@ -61,11 +61,12 @@ exec(char *path, char **argv) ip = 0; // Allocate user stack at the top of the address space. - stackbase = USERTOP - 2*PGSIZE; - if((allocuvm(pgdir, stackbase, USERTOP)) == 0) + sz = PGROUNDUP(sz); + if((stackbase = allocuvm(pgdir, KERNBASE - 2 * PGSIZE, KERNBASE - PGSIZE)) == 0) goto bad; - clearpteu(pgdir, (char*)(stackbase)); - sp = USERTOP; +// clearpteu(pgdir, (char*)(stackbase)); + sp = stackbase; + stackbase -= PGSIZE; // Push argument strings, prepare rest of stack in ustack. for(argc = 0; argv[argc]; argc++) { diff --git a/memlayout.h b/memlayout.h index ff8ec6c711..0bd29fd0de 100644 --- a/memlayout.h +++ b/memlayout.h @@ -8,8 +8,9 @@ #define KERNBASE 0x80000000 // First kernel virtual address #define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked -// Define the top of the user address space. This is the highest user address -#define USERTOP 0x80000000 // 用户地址空间的顶部 +// User address space layout +#define USERTOP 0x7F000000 // User address space top, leaving room for stack growth +#define STACKPAGES 8 // Number of pages initially allocated for stack #define V2P(a) (((uint) (a)) - KERNBASE) #define P2V(a) ((void *)(((char *) (a)) + KERNBASE)) diff --git a/testcase.c b/testcase.c index 80b8f6bca6..698695fa16 100644 --- a/testcase.c +++ b/testcase.c @@ -1,6 +1,7 @@ #include "types.h" #include "stat.h" #include "user.h" +#include "memlayout.h" // 获取当前栈指针 uint get_stack_pointer() { @@ -50,7 +51,7 @@ void recursive_call(int depth) { printf(1, "Recursion depth: %d, buffer address: 0x%x\n", depth, buffer); // 递归调用,强制栈增长 - if (depth < 100) { + if (depth < 200) { recursive_call(depth + 1); } } diff --git a/vm.c b/vm.c index 4ac21fd379..ad4bada380 100644 --- a/vm.c +++ b/vm.c @@ -337,10 +337,9 @@ copyuvm(pde_t *pgdir, uint sz, uint stackbase) goto bad; } } - return d; // Copy the stack - for(i = stackbase; i < USERTOP; i += PGSIZE){ + for(i = stackbase; i < KERNBASE - PGSIZE; i += PGSIZE){ if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) panic("copyuvm: pte should exist"); if(!(*pte & PTE_P)) From 2dc521cb9993ed30bd53c5af80890630fd748985 Mon Sep 17 00:00:00 2001 From: ckt1i Date: Sun, 13 Oct 2024 17:13:03 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E5=9F=BA=E7=A1=80=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=95=99=E6=A1=A3=EF=BC=8C=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E6=AD=A5=E8=BF=9B=E8=A1=8C=E6=A0=88=E5=A0=86=E8=B0=83=E6=8E=A7?= =?UTF-8?q?=20=09modified:=20=20=20exec.c=20=09modified:=20=20=20memlayout?= =?UTF-8?q?.h=20=09modified:=20=20=20proc.c=20=09modified:=20=20=20syscall?= =?UTF-8?q?.c=20=09modified:=20=20=20trap.c=20=09modified:=20=20=20vm.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exec.c | 4 ++-- memlayout.h | 2 +- proc.c | 4 ++-- syscall.c | 4 ++-- trap.c | 40 +++++++++++++++++++--------------------- vm.c | 13 ++++++++----- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/exec.c b/exec.c index 68635457bb..c3bedc31e1 100644 --- a/exec.c +++ b/exec.c @@ -61,8 +61,8 @@ exec(char *path, char **argv) ip = 0; // Allocate user stack at the top of the address space. - sz = PGROUNDUP(sz); - if((stackbase = allocuvm(pgdir, KERNBASE - 2 * PGSIZE, KERNBASE - PGSIZE)) == 0) + sz = PGROUNDUP(sz); //页对齐 + if((stackbase = allocuvm(pgdir, KERNBASE - 2 * PGSIZE, KERNBASE - PGSIZE)) == 0) //为用户栈分配空间,从KERNBASE-2*PGSIZE到KERNBASE-PGSIZE goto bad; // clearpteu(pgdir, (char*)(stackbase)); sp = stackbase; diff --git a/memlayout.h b/memlayout.h index 0bd29fd0de..743636c383 100644 --- a/memlayout.h +++ b/memlayout.h @@ -9,7 +9,7 @@ #define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked // User address space layout -#define USERTOP 0x7F000000 // User address space top, leaving room for stack growth +#define USERTOP 0x7FFFF000 // User address space top, leaving room for stack growth #define STACKPAGES 8 // Number of pages initially allocated for stack #define V2P(a) (((uint) (a)) - KERNBASE) diff --git a/proc.c b/proc.c index d330a9501f..0a2d869948 100644 --- a/proc.c +++ b/proc.c @@ -159,9 +159,9 @@ int growproc(int n) { uint sz; - struct proc *curproc = myproc(); + struct proc *curproc = myproc(); //当前进程 - sz = curproc->sz; + sz = curproc->sz; //当前进程的大小 if(n > 0){ if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) return -1; diff --git a/syscall.c b/syscall.c index e16b1dfff1..3c0c8c5c3e 100644 --- a/syscall.c +++ b/syscall.c @@ -17,7 +17,7 @@ int fetchint(uint addr, int *ip) { - struct proc *curproc = myproc(); +// struct proc *curproc = myproc(); /* if(addr >= curproc->sz || addr+4 > curproc->sz) return -1; */ @@ -59,7 +59,7 @@ int argptr(int n, char **pp, int size) { int i; - struct proc *curproc = myproc(); +// struct proc *curproc = myproc(); if(argint(n, &i) < 0) return -1; diff --git a/trap.c b/trap.c index 5c895b0e4c..d822f5d53c 100644 --- a/trap.c +++ b/trap.c @@ -80,29 +80,27 @@ trap(struct trapframe *tf) // 新增触发页错误(Page Fault)的情况 case T_PGFLT: - // 条件检查:确保页面错误地址在用户栈范围内。 - if (rcr2() < USERTOP && rcr2() >= myproc()->stackbase - PGSIZE) { - - //打印调试信息:页面错误地址和当前栈位置 - cprintf("page fault at %x\n", rcr2()); - cprintf("current stack position: %x\n", myproc()->stackbase); - - // 分配新的用户栈页面 - // 尝试分配新的栈页 - if (allocuvm(myproc()->pgdir, myproc()->stackbase - PGSIZE, myproc()->stackbase) == 0) { - myproc()->killed = 1; // 如果分配失败,标记进程为被杀死。 - } - else{ - // 更新栈基地址并打印。 - myproc()->stackbase -= PGSIZE; - cprintf("allocated new stack page at %x\n", myproc()->stackbase); - } - return; - } else { + + if (rcr2() < USERTOP)// 条件检查:确保页面错误地址在用户栈范围内。 + { + cprintf("page error %x ",rcr2()); + cprintf("stack pos : %x\n", myproc()->stackbase); + // 为用户栈分配新的页面 + if ((myproc()->stackbase = allocuvm(myproc()->pgdir, myproc()->stackbase - 1 * PGSIZE, + myproc()->stackbase)) == 0) + { myproc()->killed = 1; - break; } - + myproc()->stackbase-=PGSIZE; // 更新用户栈的栈顶位置 + cprintf("create a new page %x\n", myproc()->stackbase); + //clearpteu(myproc()->pgdir, (char *) (myproc()->stackbase - PGSIZE)); + return; + } + else + { + myproc()->killed = 1; + break; + } //PAGEBREAK: 13 default: if(myproc() == 0 || (tf->cs&3) == 0){ diff --git a/vm.c b/vm.c index ad4bada380..43fa7e6195 100644 --- a/vm.c +++ b/vm.c @@ -224,21 +224,24 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) char *mem; uint a; + //边界检查:确保新的进程大小不超过KERNBASE if(newsz >= KERNBASE) return 0; if(newsz < oldsz) return oldsz; - a = PGROUNDUP(oldsz); - for(; a < newsz; a += PGSIZE){ - mem = kalloc(); + a = PGROUNDUP(oldsz); // 页对齐 + for(; a < newsz; a += PGSIZE) // 按页分配内存 + { + mem = kalloc(); // 为每页分配物理内存 if(mem == 0){ cprintf("allocuvm out of memory\n"); deallocuvm(pgdir, newsz, oldsz); return 0; } - memset(mem, 0, PGSIZE); - if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){ + memset(mem, 0, PGSIZE); //清空原有数据 + if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0) // 映射 + { cprintf("allocuvm out of memory (2)\n"); deallocuvm(pgdir, newsz, oldsz); kfree(mem); From 52bf0f8c10bf8469a87baa11b0a3628dc59682f9 Mon Sep 17 00:00:00 2001 From: ckt1i Date: Thu, 17 Oct 2024 14:50:46 +0800 Subject: [PATCH 5/6] Changes to be committed: new file: Lab5_log.md modified: README --- Lab5_log.md | 438 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README | 54 +------ 2 files changed, 443 insertions(+), 49 deletions(-) create mode 100644 Lab5_log.md diff --git a/Lab5_log.md b/Lab5_log.md new file mode 100644 index 0000000000..a4cbe78fbc --- /dev/null +++ b/Lab5_log.md @@ -0,0 +1,438 @@ +# 配置环境 + +## 实验环境 + +虚拟虚拟化平台:Unraid 6.12.13 远程运行 + +操作系统:Ubuntu 22.04.4 LTS (GNU/Linux 6.8.0-40-generic x86_64) + +小型操作系统内核:xv6 + +## gcc配置 + +输入`sudo apt update`更新软件包列表 + +输入`sudo apt-get install -y build-essential git gcc-multilib `配置gcc + +输入`objdump -i`检查是否支持64位: +``` +user@Lab:~$ objdump -i +BFD header file version (GNU Binutils for Ubuntu) 2.38 +elf64-x86-64 +``` + +输入`gcc -m32 -print-libgcc-file-name `查看32位gcc库文件路径: +``` +user:~$ gcc -m32 -print-libgcc-file-name +/usr/lib/gcc/x86_64-linux-gnu/11/32/libgcc.a +``` + +出现上述输出,gcc环境已配置好 + +## 安装qemu以及xv6 + +输入`sudo apt-get install qemu-system`安装qemu + +输入`qemu-system-i386 --version`查看qemu版本: +``` +user@Lab:~$ qemu-system-i386 --version +QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.22) +Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers +``` + +出现上述输出,qemu环境已配置好 + +输入`git clone https://github.com/mit-pdos/xv6-public.git`下载xv6系统 + +文件最后出现输出,说明操作系统镜像文件已准备好: +``` ++0 records in ++0 records out +512 bytes (512 B) copied, 0.000543844 s, 938 kB/s +dd if=kernal of=xf6.img seek=1 conv=notrunc +393+1 records in +393+1 records out +``` + +## 启动qemu + +输入`~/xv6$ echo "add-auto-load-safe-path $HOME/xv6/.gdbinit" > ~/.gdbinit` 配置gdb + +输入`make qemu`启动qemu,出现如下报错: + +``` +qemu-system-i386 -serial mon:stdio -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 -m 512 +gtk initialization failed +make: *** [Makefile:225: qemu] Error 1 +``` + +经查询,其原因通常与 QEMU 的 GTK 版本有关,这可能是由于缺少 GTK 库或没有正确配置显示环境引起的。在此处我们选择采用VNC后端启动QEMU,通过修改其中的`QEMUOPTS`变量如下,修改启动方式: + +``` +QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 -nographic $(QEMUEXTRA) +``` + +命令行中输入`make qemu`启动qemu,进入qemu界面: + +``` +SeaBIOS (version 1.15.0-1) + + +iPXE (https://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+1FF8B4A0+1FECB4A0 CA00 + + + +Booting from Hard Disk..xv6... +cpu0: starting 0 +sb: size 1000 nblocks 941 ninodes 200 nlog 30 logstart 2 inodestart 32 bmap sta8 +init: starting sh +$ +``` + +# xv6功能分析 + +## xv6的内存布局 + +在xv6中,内存布局分为内核空间和用户空间, + +内核位于内存空间的高地址部分,从一个固定点 (KERNBASE,通常是 0x80000000) 开始。包含内核代码和数据、I/O 内存映射、内核栈、物理内存映射 + +用户空间位于地址空间的低地址部分(低于 KERNBASE),主要包括文本段、数据段、堆段和栈段。其中栈段由中间的某个地址开始向上增长,本次实验的目的就是讲栈的分配方式由从下往上修改为从上往下。具体的内存分布见下图: + +## 相关函数 + +以下是与xv6内存布局和管理相关的文件及其简要功能描述: + +### 1. `vm.c` +负责虚拟内存管理,包括设置内核页表、分配和释放用户内存、复制进程内存等。关键函数包括 `setupkvm()`、`allocuvm()`、`deallocuvm()` 和 `copyuvm()`。 + +### 2. `proc.c` +管理进程状态,包括创建新进程、扩展内存(`growproc()`)、执行新程序(`exec()`)等,使用 `copyuvm()` 复制父进程的内存布局。 + +### 3. `trap.c` +处理异常和中断,包括页面错误(Page Fault)。在 `trap()` 函数中处理用户栈不足时的错误,调用 `allocuvm()` 分配新页面。 + +### 4. `exec.c` +加载可执行文件和初始化进程的内存布局,使用 `exec()` 加载 ELF 文件,分配代码段、数据段和用户栈。 + +### 5. `syscall.c` +处理系统调用,调用相应的内存管理功能,如 `fork`、`exec`、`wait` 等,负责在用户空间和内核空间之间切换。 + +### 6. `memlayout.h` +定义内存布局的常量,包括内核空间和用户空间的起始和结束地址,如 `KERNBASE`、`PHYSTOP` 和 `USERTOP`。 + +### 7. `mmu.h` +定义分页机制的宏和结构,描述页表项格式、页大小等,如 `PGSIZE` 和各种页表项标志(PTE)。 + +### 8. `kalloc.c` +实现物理内存分配,提供 `kalloc()` 和 `kfree()` 函数,负责从物理内存中分配和释放页面。 + +### 9. `trapasm.S` +汇编代码,处理从用户态进入内核态时的上下文切换,设置陷阱帧并跳转到 `trap()` 函数。 + +### 10. `kernel.ld` +链接脚本,定义内核的加载地址及各部分的内存布局,确保内核和用户进程的内存正确映射。 + +这些文件和函数共同实现了xv6的内存管理和布局,确保系统在运行时能够有效地分配和使用内存。 + +# 代码修改 + +## 具体步骤: + +1. 重新定义用户地址空间布局。 +2. 修改 `exec.c` 以在高地址处分配栈。 +3. 调整 `proc` 结构,以正确跟踪用户栈和堆的边界。 +4. 修改与内存管理相关的函数,确保它们能正确处理栈和堆的分配及地址验证。 +5. 实现栈增长的处理 + +## 调整用户地址空间布局 + +核心文件是 `memlayout.h`,它定义了内核和用户内存空间的布局。在 `memlayout.h` 中,`KERNBASE` 定义了用户地址空间的上限: +``` +// Key addresses for address space layout (see kmap in vm.c for layout) +#define KERNBASE 0x80000000 // First kernel virtual address +``` +在这个空间内修改栈的位置。 + +## 修改`exec.c`中的栈分配逻辑 + +`exec.c` 负责加载用户程序并初始化用户地址空间。它使用 `allocuvm()` 为程序分配内存,加载代码段和数据段,并为栈分配一页内存。以下是栈分配的步骤 + +在`exec.c` 中,找到分配用户栈的代码,将栈分配到代码和堆的末尾,并分配两页(`2*PGSIZE`)大小的栈空间; +修改栈的位置到 `KERNBASE - *PGSIZE` 开始 + +``` +// Allocate user stack at the top of the address space. + uint stackbase = USERTOP - *PGSIZE; + if((allocuvm(pgdir, stackbase, USERTOP)) == 0) + goto bad; + clearpteu(pgdir, (char*)(stackbase)); + sp = USERTOP; +``` + +## 调整`proc`结构修改 `proc->sz`的管理 + +当前 `proc->sz` 跟踪整个用户地址空间的大小,包括代码段、堆和栈。在修改后,`proc->sz` 只用于跟踪代码段和堆的大小,需要额外的字段来跟踪栈的起始位置。 +故在`proc.h`中对proc结构设置新的结构项: + +``` +// Per-process state +struct proc { + uint sz; // Size of process memory (bytes) + pde_t* pgdir; // Page table + char *kstack; // Bottom of kernel stack for this process + enum procstate state; // Process state + int pid; // Process ID + struct proc *parent; // Parent process + struct trapframe *tf; // Trap frame for current syscall + struct context *context; // swtch() here to run process + void *chan; // If non-zero, sleeping on chan + int killed; // If non-zero, have been killed + struct file *ofile[NOFILE]; // Open files + struct inode *cwd; // Current directory + char name[16]; // Process name (debugging) + uint stackbase; // Base of the stack +}; +``` + +## 修改内存管理的相关函数 + +在`proc.c`中,修改`fork`函数以传递 `stackbase` 参数给 `copyuvm` 函数。 + +``` +if((np->pgdir = copyuvm(curproc->pgdir, curproc->sz, curproc->stackbase)) == 0) +``` + +在`vm.c`中修改 `copyuvm` 函数,以确保在 fork 时正确地拷贝栈。: +``` +copyuvm(pde_t *pgdir, uint sz, uint stackbase) +{ + ... + // Copy the stack + for(i = USERTOP - PGSIZE; i > stackbase i -= PGSIZE){ + if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) + panic("copyuvm: pte should exist"); + if(!(*pte & PTE_P)) + panic("copyuvm: page not present"); + pa = PTE_ADDR(*pte); + flags = PTE_FLAGS(*pte); + if((mem = kalloc()) == 0) + goto bad; + memmove(mem, (char*)P2V(pa), PGSIZE); + if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) { + kfree(mem); + goto bad; + } + } + + return d; + ... +} +``` + +## 实现栈增长后的处理 + +在`trap.c` 中,中处理页面错误,检测是否是栈溢出,并分配新的栈页: +``` +// 新增触发页错误(Page Fault)的情况 +case T_PGFLT: + + if (rcr2() < USERTOP)// 条件检查:确保页面错误地址在用户栈范围内。 + { + cprintf("page error %x ",rcr2()); + cprintf("stack pos : %x\n", myproc()->stackbase); + // 为用户栈分配新的页面 + if ((myproc()->stackbase = allocuvm(myproc()->pgdir, myproc()->stackbase - 1 * PGSIZE, + myproc()->stackbase)) == 0) + { + myproc()->killed = 1; + } + myproc()->stackbase-=PGSIZE; // 更新用户栈的栈顶位置 + cprintf("create a new page %x\n", myproc()->stackbase); + //clearpteu(myproc()->pgdir, (char *) (myproc()->stackbase - PGSIZE)); + return; + } + else + { + myproc()->killed = 1; + break; + } +``` + +在`syscall.c`中修改所有引用sz的地方,以反映新的栈位置。 + +# 进行实验 + +## 编写测试程序`testcase.c` + +### 主要结构 + +``` +#include "types.h" +#include "stat.h" +#include "user.h" + +// 获取当前栈指针 +uint get_stack_pointer() { + ... +} + +// 深度递归调用以测试更大规模的栈增长 +void recursion(int depth) { + ... +} + +// 测试深度递归引发栈增长 +void test_stack_growth() { + ... +} + +// 测试堆与栈的冲突 +void test_stack_heap_collision() { + ... +} + +int main(int argc, char *argv[]) { + test_stack_growth(); + test_stack_heap_collision(); + exit(); +} +``` + +### 测试栈的增长以及缺页分配 +编写如下程序: +其中,`get_stack_pointer`用于获取栈指针,`recursion`函数用于递归调用,`test_stack_growth`函数用于测试栈的增长。对于每次递归调用,输出当前栈指针。 +``` +// 获取当前栈指针 +uint get_stack_pointer() { + uint sp; + asm volatile("movl %%esp, %0" : "=r" (sp)); + return sp; +} + +// 深度递归调用以测试更大规模的栈增长 +void recursion(int depth) { + char buffer[4096]; // 分配4KB的空间,确保每次递归占用整页 + memset(buffer, 1 , 4096); // 进行操作避免被优化掉 + uint sp = get_stack_pointer(); + printf(1, "Recursion depth: %d, stack address: 0x%x\n", depth , sp ); + + // 在深度达到一定值之前递归 + if (depth < 100) { + recursion(depth + 1); + } +} + +// 测试深度递归引发栈增长 +void test_stack_growth() { + printf(1, "\n=== Test: Stack Growth ===\n"); + printf(1, "Starting recursion test...\n"); + + recursion(1); // 初始递归调用,测试栈增长 + + printf(1, "Stack recursion test completed.\n"); +} +``` + +## 测试堆与栈的冲突 +在测试栈增长的基础上,添加test_stack_heap_collision函数,通过分配大量堆内存,测试堆与栈的冲突。 +``` +// 测试堆与栈的冲突 +void test_stack_heap_collision() { + printf(1, "\n=== Test: Stack-Heap Collision ===\n"); + printf(1, "Starting stack-heap collision test...\n"); + printf(1, "Allocating 222MB of memory on the heap...\n"); + int * p = (int *)malloc(222*1024*1024 - 512*1024); + *p = 1; + + printf(1, "Making recursions .\n"); + recursion(1); // 初始递归调用,测试栈增长 + printf(1, "Stack recursion test completed.\n"); +} +``` + +## 运行测试程序 + +编译并运行测试程序: +``` +$ make qemu-gdb +``` +在`Makefile`中添加编译`testcase.c`的命令: +``` +UPROGS=\ + _testcase\ +``` + + +运行`make qemu`启动QEMU,并在QEMU中运行测试程序,观察输出,得出结论: +``` +$ testcase +``` + +## 实验结果 + +### 测试栈的增长 + +``` +=== Test: Stack Growth === +Starting recursion test... +page error 7fffdf90 stack pos : 7fffe000 +create a new page 7fffd000 +page error 7fffcf90 stack pos : 7fffd000 +create a new page 7fffc000 +page error 7fffbf90 stack pos : 7fffc000 +create a new page 7fffb000 +Recursion depth: 1, stack address: 0x7FFFBF90,buffer address: 0x7FFFBF90 +Recursion depth: 2, stack address: 0x7FFFBF90,buffer address: 0x7FFFCF90 +Recursion depth: 3, stack address: 0x7FFFBF90,buffer address: 0x7FFFDF90 +page error 7fffaf70 stack pos : 7fffb000 +... +page error 7ff9ab70 stack pos : 7ff9b000 +create a new page 7ff9a000 +page error 7ff99b70 stack pos : 7ff9a000 +create a new page 7ff99000 +page error 7ff98b70 stack pos : 7ff99000 +create a new page 7ff98000 +Recursion depth: 100, stack address: 0x7FF98B70,buffer address: 0x7FF98B70 +Stack recursion test completed +``` +从输出中可以看出,随着递归深度的增加,由于栈空间不足,系统会不断分配新的页,并且对于每次递归,系统都会输出栈帧地址和站上的数据,我们可以看到栈地址逐渐从高地址向低地址移动,说明我们的栈是向下增长的。 + +### 测试堆与栈的冲突 + +``` + +=== Test: Stack-Heap Collision === +Starting stack-heap collision test... +Allocating 222MB of memory on the heap... +Making recursions . +page error 7fffdf90 stack pos : 7fffe000 +create a new page 7fffd000 +page error 7fffcf90 stack pos : 7fffd000 +create a new page 7fffc000 +page error 7fffbf90 stack pos : 7fffc000 +create a new page 7fffb000 +Recursion depth: 1, stack address: 0x7FFFBF80 +Recursion depth: 2, stack address: 0x7FFFBF80 +Recursion depth: 3, stack address: 0x7FFFBF80 +page error 7fffaf70 stack pos : 7fffb000 +create a new page 7fffa000 +page error 7fff9f70 stack pos : 7fffa000 +create a new page 7fff9000 +... +page error 7ffe2e70 stack pos : 7ffe3000 +create a new page 7ffe2000 +page error 7ffe1e70 stack pos : 7ffe2000 +create a new page 7ffe1000 +page error 7ffe0e70 stack pos : 7ffe1000 +create a new page 7ffe0000 +Recursion depth: 28, stack address: 0x7FFE0E60 +Recursion depth: 29, stack address: 0x7FFE0E60 +Recursion depth: 30, stack address: 0x7FFE0E60 +page error 7ffdfe50 stack pos : 7ffe0000 +allocuvm out of memory +The stack has grown into the heap space. +create a new page fffff000 +stack is allocated in wrong place, End the process! +``` +从输出中可以看出,当递归深度达到28时,此时栈已经进入堆空间。由于我们的程序在出现这种情况时选择重新分配栈空间,而在此时,由于无空间可分,最终栈被分入错误的地址,导致程序崩溃。 \ No newline at end of file diff --git a/README b/README index 923e0a4821..a3b00d5ec3 100644 --- a/README +++ b/README @@ -1,51 +1,7 @@ -NOTE: we have stopped maintaining the x86 version of xv6, and switched -our efforts to the RISC-V version -(https://github.com/mit-pdos/xv6-riscv.git) +# BIT网安操作系统Lab5实验文件 -xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix -Version 6 (v6). xv6 loosely follows the structure and style of v6, -but is implemented for a modern x86-based multiprocessor using ANSI C. +author: ckti -ACKNOWLEDGMENTS - -xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer -to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14, -2000)). See also https://pdos.csail.mit.edu/6.828/, which -provides pointers to on-line resources for v6. - -xv6 borrows code from the following sources: - JOS (asm.h, elf.h, mmu.h, bootasm.S, ide.c, console.c, and others) - Plan 9 (entryother.S, mp.h, mp.c, lapic.c) - FreeBSD (ioapic.c) - NetBSD (console.c) - -The following people have made contributions: Russ Cox (context switching, -locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin -Clements. - -We are also grateful for the bug reports and patches contributed by Silas -Boyd-Wickizer, Anton Burtsev, Cody Cutler, Mike CAT, Tej Chajed, eyalz800, -Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, Peter -Froehlich, Yakir Goaron,Shivam Handa, Bryan Henry, Jim Huang, Alexander -Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller, Eddie Kohler, Austin -Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi Mitake, Carmi -Merimovich, Mark Morrissey, mtasm, Joel Nider, Greg Price, Ayan Shafqat, -Eldar Sehayek, Yongming Shen, Cam Tenny, tyfkda, Rafael Ubal, Warren -Toomey, Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas -Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang Wei. - -The code in the files that constitute xv6 is -Copyright 2006-2018 Frans Kaashoek, Robert Morris, and Russ Cox. - -ERROR REPORTS - -We don't process error reports (see note on top of this file). - -BUILDING AND RUNNING XV6 - -To build xv6 on an x86 ELF machine (like Linux or FreeBSD), run -"make". On non-x86 or non-ELF machines (like OS X, even on x86), you -will need to install a cross-compiler gcc suite capable of producing -x86 ELF binaries (see https://pdos.csail.mit.edu/6.828/). -Then run "make TOOLPREFIX=i386-jos-elf-". Now install the QEMU PC -simulator and run "make qemu". \ No newline at end of file +## Documents and links: + - [Logs](Lab5_log.md) + - [Project_main_Reposistories](https://github.com/EnjunDu/operating_system) \ No newline at end of file From f3ede6466dcfb5afadd1ac8dd0ea896fb9d5f791 Mon Sep 17 00:00:00 2001 From: ckt1i Date: Fri, 18 Oct 2024 14:16:59 +0800 Subject: [PATCH 6/6] Changes to be committed: modified: testcase.c modified: trap.c modified: vm.c --- testcase.c | 112 ++++++++++++----------------------------------------- trap.c | 6 +++ vm.c | 1 + 3 files changed, 31 insertions(+), 88 deletions(-) diff --git a/testcase.c b/testcase.c index 698695fa16..ed2b04346f 100644 --- a/testcase.c +++ b/testcase.c @@ -1,7 +1,6 @@ #include "types.h" #include "stat.h" #include "user.h" -#include "memlayout.h" // 获取当前栈指针 uint get_stack_pointer() { @@ -10,107 +9,44 @@ uint get_stack_pointer() { return sp; } -// 测试栈是否位于堆之上 -void test_stack_position() { - printf(1, "\n=== Test 1: Stack Position ===\n"); - uint sp = get_stack_pointer(); - uint heap_end = (uint)sbrk(0); - - printf(1, "Current stack pointer: 0x%x\n", sp); - printf(1, "Heap end: 0x%x\n", heap_end); - - if (sp > heap_end) { - printf(1, "Test passed: Stack is above heap.\n"); - } else { - printf(1, "Test failed: Stack is not correctly positioned.\n"); - } -} - -// 测试堆和栈是否分离 -void test_heap_stack_separation() { - printf(1, "\n=== Test 2: Heap and Stack Separation ===\n"); - - uint heap_start = (uint)sbrk(0); - sbrk(4096 * 10); // 增加10页堆 - uint heap_end = (uint)sbrk(0); - uint sp = get_stack_pointer(); - - printf(1, "Heap range: 0x%x - 0x%x\n", heap_start, heap_end); - printf(1, "Stack pointer: 0x%x\n", sp); - - if (sp > heap_end) { - printf(1, "Test passed: Stack is above heap after sbrk.\n"); - } else { - printf(1, "Test failed: Stack and heap overlap.\n"); - } -} - -// 递归调用以强制栈增长 -void recursive_call(int depth) { - char buffer[1024]; // 在栈上分配空间,逐步增长栈 - printf(1, "Recursion depth: %d, buffer address: 0x%x\n", depth, buffer); - - // 递归调用,强制栈增长 - if (depth < 200) { - recursive_call(depth + 1); - } -} - -// 测试栈增长 -void test_stack_growth() { - printf(1, "\n=== Test 3: Stack Growth ===\n"); - printf(1, "Testing stack growth with recursion...\n"); - recursive_call(1); // 初始调用 - - printf(1, "Test completed.\n"); -} - // 深度递归调用以测试更大规模的栈增长 -void deep_recursion(int depth) { - char buffer[4096]; // 分配4KB的空间,确保每次递归占用整页 - printf(1, "Recursion depth: %d, buffer address: 0x%x\n", depth, buffer); +void recursion(int depth) { + char buffer[4096]; // 分配4KB的空间,确保每次递归占用整页 + memset(buffer, 1 , 4096); // 进行操作避免被优化掉 + uint sp = get_stack_pointer(); + printf(1, "Recursion depth: %d, stack address: 0x%x\n", depth , sp ); // 在深度达到一定值之前递归 - if (depth < 200) { - deep_recursion(depth + 1); + if (depth < 100) { + recursion(depth + 1); } } // 测试深度递归引发栈增长 -void test_deep_stack_growth() { - printf(1, "\n=== Test 4: Deep Stack Growth ===\n"); - printf(1, "Starting deep recursion test...\n"); - - deep_recursion(1); // 初始递归调用,测试栈增长 - - printf(1, "Deep recursion test completed.\n"); -} +void test_stack_growth() { + printf(1, "\n=== Test: Stack Growth ===\n"); + printf(1, "Starting recursion test...\n"); -// 强制堆增长以接近栈区域 -void force_stack_heap_collision() { - // 通过增加堆的大小来接近栈区域 - sbrk((KERNBASE - (uint)sbrk(0)) / 2); // 增加到接近栈的区域 + recursion(1); // 初始递归调用,测试栈增长 - char buffer[1024]; // 在栈上分配空间 - printf(1, "Allocated stack buffer at: 0x%x\n", buffer); + printf(1, "Stack recursion test completed.\n"); } // 测试堆与栈的冲突 void test_stack_heap_collision() { - printf(1, "\n=== Test 5: Stack-Heap Collision ===\n"); + printf(1, "\n=== Test: Stack-Heap Collision ===\n"); + printf(1, "Starting stack-heap collision test...\n"); + printf(1, "Allocating 222MB of memory on the heap...\n"); + int * p = (int *)malloc(222*1024*1024 - 512*1024); + *p = 1; - force_stack_heap_collision(); // 强制堆增长,接近栈 - - printf(1, "Test completed.\n"); + printf(1, "Making recursions .\n"); + recursion(1); // 初始递归调用,测试栈增长 + printf(1, "Stack recursion test completed.\n"); } -int main() { - // 执行所有测试 - test_stack_position(); // 测试栈位置 - test_heap_stack_separation(); // 测试堆与栈的分离 - test_stack_growth(); // 测试栈增长 - test_deep_stack_growth(); // 测试深度递归栈增长 - test_stack_heap_collision(); // 测试堆与栈的冲突 - +int main(int argc, char *argv[]) { + test_stack_growth(); + test_stack_heap_collision(); exit(); -} +} \ No newline at end of file diff --git a/trap.c b/trap.c index d822f5d53c..0ae4df0f08 100644 --- a/trap.c +++ b/trap.c @@ -94,6 +94,12 @@ trap(struct trapframe *tf) myproc()->stackbase-=PGSIZE; // 更新用户栈的栈顶位置 cprintf("create a new page %x\n", myproc()->stackbase); //clearpteu(myproc()->pgdir, (char *) (myproc()->stackbase - PGSIZE)); + if (myproc()->stackbase > KERNBASE - 1) + { + myproc()->killed = 1; + cprintf("There is no space for the stack\n"); + exit(); + } return; } else diff --git a/vm.c b/vm.c index 43fa7e6195..5deb6b5283 100644 --- a/vm.c +++ b/vm.c @@ -236,6 +236,7 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) mem = kalloc(); // 为每页分配物理内存 if(mem == 0){ cprintf("allocuvm out of memory\n"); + cprintf("The stack have grown into the heap\n"); deallocuvm(pgdir, newsz, oldsz); return 0; }