xv6 COW Lab中碰到的bug
-
内核地址0x80090000地址作为(struct run*)结构体即空闲内存结构体时下一个(->next)指向了一个非法地址0x7fXXXXXX(因为<0x80000000内核代码地址地址,这下面的地址和操作系统无关)。
为什么会这样呢?
因为数组写越界了。这里为了统计每个页表的引用数,声明了一个数组
static uint8 pgrefcount[(PHYSTOP - KERNBASE) >> PGSHIFT] = {0};
注意这里PHYSTOP - KERNBASE
。Here is the problem. 更新数组时没有减去KERNBASE
。本来应该是pgrefcount[(pa- KERNBASE) >> PGSHIFT] += (uint8)n;
,我写成了pgrefcount[pa>> PGSHIFT] += (uint8)n;
,这个数组大概在0x8008XXXX的位置,xv6初始化时kfreerange()
会调用kfree()
把kernel最后的地址和PHYSTOP中间的内存串成空闲内存链,同时kfree()
会逐个更新每个页对应pgrefcount
。因为没有减去KERNBASE
,所以数组写越界了,后面的pgrefcount
元素直接把0x80090000数据给覆盖掉了。怎么找到的原因?
问题出在
kalloc()
返回了一个非法地址0x7fXXXXXX,这很奇怪,如果没有空闲内存应该返回0才对。他的上一个空闲内存是0x80090000(即((struct run *)0x80090000)->next
指向0x7fXXXXXX),而且不管我怎么改代码,永远是0x80090000出事,因此我在kfree()
中插入
if ((uint64)(((struct run *)0x80090000)->next) > 0x7f000000 && (uint64)(((struct run *)0x80090000)->next) < 0x80000000){
printf("(uint64)(((struct run *)0x80090000)->next): %p ", (uint64)(((struct run *)0x80090000)->next));
panic("dead.\n");
}
发现kfreerange()
串链子的时候会把0x80090000的下个空闲内存指向0x7fXXXXXX,由此猜测数组越界,done。(中间过程比这复杂亿点点)
threetest()
处理Store/AMO page fault异常时报错:fault_vaddr:0x0000000000000000, page: 0x0000000000000000, pte: 0x0000000021fd0c5b, sepc: 0x000000008000204e pid: 6
意思就是stval为0。
为什么会这样呢?
并发原因。threetest()一共三个进程,如果同时触发Store/AMO page fault异常,就会造成这个问题(中间具体是什么原因,我确实也不明白,还是要学习一个)。
怎么解决?请看VCR
} else if (r_scause() == 15){
// Store/AMO page fault
if(killed(p))
exit(-1);
uint64 fault_vaddr = r_stval();
intr_on();
storepagefault(fault_vaddr);
} else if((which_dev = devintr()) != 0){
把intr_on();
放在uint64 fault_vaddr = r_stval();
后面,因为r_stval()
会把stval
中的值取出来,之后管你并发把stval变成什么。如果放在uint64 fault_vaddr = r_stval();
前面,就会触发上面的bug。done