- 论坛徽章:
- 0
|
对于用户态栈的扩展,最终是在do_page_falut中完成的:
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if (error_code & 4) {
/*
* Accessing the stack below %esp is always a bug.
* The large cushion allows instructions like enter
* and pusha to work. ("enter $65535,$31" pushes
* 32 pointers and then decrements %esp by 65535.)
*/
if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)
goto bad_area;
}
if (expand_stack(vma, address))
goto bad_area; |
代码注释以及前景分析以及论坛上此前的帖子对address为什么要+ 65536 + 32 * sizeof(unsigned long)都做了解释,即pushall以及enter指令会去访问位于esp以下的地址。
我的疑惑是: 根据intel文档,pushall指令操作过程为:
IF 64-bit Mode
THEN #UD
FI;
IF OperandSize = 32 (* PUSHAD instruction *)
THEN
Temp ← (ESP);
Push(EAX);
Push(ECX);
Push(EDX);
Push(EBX);
Push(Temp) |
Push(EBP);
Push(ESI);
Push(EDI);
ELSE (* OperandSize = 16, PUSHA instruction *)
Temp ← (SP);
Push(AX);
Push(CX);
Push(DX);
Push(BX);
Push(Temp);
Push(BP);
Push(SI);
Push(DI);
FI |
push的操作:
ELSE IF StackAddrSize = 32
THEN
IF OperandSize = 32
THEN
ESP ← (ESP − 4);
IF (SRC is FS or GS)
THEN
TEMP = ZeroExtend32(SRC);
ELSE IF (SRC is IMMEDIATE)
TEMP = SignExtend32(SRC); FI;
ELSE
TEMP = SRC;
FI;
SS:ESP ← TEMP; (* Push doubleword *)
ELSE (* OperandSize = 16*)
ESP ← (ESP − 2);
SS:ESP ← SRC; (* Push word *)
FI |
从其中可以看出,pushall的操作相当于多个push;而push指令是先执行esp的减操作,然后才存实际的数据。而触发pagefault的,应该是后面的存数据的动作,这是esp已经减小。
现在假如执行一条pushall指令时发生了page fault, 这条pushall指令的地址address被保存在了CR2中,esp被硬件自动保存在了内核栈,这时的esp应该是减小后的esp。进入do_page_fault后,address的值肯定是大于硬件保存的esp值。也就是说,上面的if判断中+32似乎是多余的啊。
如果我的理解有问题,那应该就在于上面红色的那句,也许硬件并不是这么实现的,比如可能整个push指令是一个原子操作,当后面存数据时发生pagefault时esp自动恢复到之前的值了。 |
|