Messages in this thread | | | From | Andy Lutomirski <> | Date | Wed, 25 Mar 2015 08:12:59 -0700 | Subject | Re: [PATCH] x86: vdso32/syscall.S: do not load __USER32_DS to %ss |
| |
On Wed, Mar 25, 2015 at 7:55 AM, Denys Vlasenko <dvlasenk@redhat.com> wrote: > On 03/24/2015 10:40 PM, Andy Lutomirski wrote: >> The syscall and sysenter stuff is IMO really nasty. Here's how I'd >> like it to work: >> >> When you do "call __kernel_vsyscall", I want the net effect to be that >> your eax, ebx, ecx, edx, esi, edi, and ebp at the time of the call end >> up *verbatim* in pt_regs. Your eip and rsp should be such that, if we >> iret normally using pt_regs, we end up returning correctly to >> userspace. I want this to be true *regardless* of whether we're doing >> a fast-path or slow-path system call. >> >> This means that we have, literally (see below for why ret $4): >> >> int $0x80 >> ret $4 <-- regs->eip points here >> >> Then we add an opportunistic return trampoline: if a special ti flag >> is set (which we set on entry here) and the return eip and regs are >> appropriate, then we change the return at the last minute to vdso code >> that looks like: >> >> popl $ecx >> popl $edx >> ret > > I don't fully understand your intent.
The idea would be that syscall and sysenter would each be exactly equivalent to a different sequence of instructions, each culminating in a jmp to an int80, except that they would enable opportunistic exit optimizations. I think I made some mistakes below, though.
> >> The vdso code would be something like (so untested it's not even funny): >> >> __kernel_vsyscall: >> ALTERNATIVE_2(something or other) >> >> __kernel_vsyscall_for_intel: >> pushl $edx >> pushl $ecx >> sysenter >> hlt <-- just for clarity >> >> __kernel_vsyscall_for_amd: >> pushl $ecx >> syscall >> __vsyscall_after_syscall_insn: >> ret $4 <-- for binary tracers only > > This ret would use former ecx value as return address? >
Nope. The idea is that syscall32 would be close enough to equivalent to "mov (%esp),%ecx; int $0x80" that binary tracers would do the right thing. But I could easily be off a bit. If I were to implement it, that ret instruction would be the very last thing I added, since it would depend on everything else.
> >> __kernel_vsyscall_for_int80: >> int $0x80 <-- regs->eip points here during *all* vsyscalls >> >> __kernel_vsyscall_slow_ret: >> ret $4 > > After returning, this will pop an extra word from __kernel_vsyscall() caller. > They don't expect that. >
Whoops. I did say this was completely untested :). I guess it would be more like:
__kernel_vsyscall_for_int80: pushl $eax /* dummy */ pushl $eax /* dummy */ int $0x80 <-- regs->eip points here during *all* vsyscalls
__kernel_vsyscall_slow_ret: ret $8
since having the amount of extra scratch stack space vary by entry type is probably unnecessarily confusing.
> >> __kernel_vsyscall_sysretl_target: >> popl $ecx >> ret >> >> There is no sysexit. Take that, Intel. >> >> On sysenter, we copy regs->cx and regs->dx from user memory and then >> we increment regs->sp by 4 and point regs->eip to >> __kernel_vsyscall_for_int80. On syscall, we copy regs->cx from user >> memory and point regs->eip to __kernel_vsyscall_for_int80. >> >> On opportunistic sysretl, we do: >> >> *regs->sp = regs->cx; /* put_user or whatever */ >> regs->eip = __kernel_vsyscall_sysretl_target >> ... >> sysretl >> >> We never do sysexit or sysretl in any other code path. That is, there >> is no really fast path anymore. > > I still don't understand the purpose those "ret 4" insns. > They don't look right.
They were wrong. It's the idea that counts, I hope :)
--Andy
| |