Re: Syscall changes registers beyond %eax, on linux-i386

From: dvorak (
Date: Thu Sep 19 2002 - 12:59:41 EST

On Thu, Sep 19, 2002 at 01:22:35PM -0400, Richard B. Johnson wrote:
> On Thu, 19 Sep 2002, Brian Gerst wrote:
> > Richard B. Johnson wrote:
> > > On Thu, 19 Sep 2002, dvorak wrote:
> > >
> > >
> > >>Hi,
> > >>
> > >>recently i came across a situation were on linux-i386 not only %eax was
> > >>altered after a syscall but also %ebx. I tracked this problem down, to
> > >>gcc re-using a variable passed to a function.
> > >>
> > >>This was found on a debian system with a 2.4.17 kernel compiled with gcc
> > >>2.95.2 and verified on another system, kernel 2.4.18 compiled with 2.95.4
> > >>Attached is small program to test for this 'bug'
> > >>
<SNIP part of the explanation>

> > >>It seems that gcc in certain cases optimizes in such a way that it changes
> > >>the variable ufds as placed on the stack directly. Which results in saved(ebx)
> > >>being overwritten and thus in a changed %ebx on return from the system call.
> > >>
> > >
> > >
> > > The 'C' compiler must make room on the stack for any local
> > > variables except register types. If it was doing as you state, you
> > > couldn't even execute a "hello world" program. Further, the local
> > > variables are after the return address. It would screw up the return
> > > address and you'd go off into hyper-space upon return.

The problem is it uses one of the _arguments_ passed to the function,
that argument gets modified, normally this happens on a copy, but there
is no 'garantue' that is doesn't modify the original argument as
putted on the stack by the calling function.

> > > No. Various 'C' implementers have standardized calling methods even
> > > though it's not part of the 'C' standard. gcc and others assume that
> > > a called procedure is not going to change any segments or index registers.
> > > There are various optimization things, like "-fcaller-saves" where the
> > > called procedure can destroy anything. You may be using something that
> > > was wrongly compiled using that switch.
This is not what happens here, what happens is that one of the _arguments_
placed on the stack is being modified, normally a calling function discards
these values after use (addl $0x10, %esp or similar) but in this case they
are reused. (in the RESTORE_ALL call)

> >
> > The bug is only with _some_ syscalls, and getpid() is not one of them,
> > so your example is flawed. It happens when a syscall modifies one of
> > it's parameter values. The solution is to assign the parameter to a
> > local variable before modifying it.
> >
and only with _some_ compiler + kernel combinations.

> int main()
> {
> struct termios t;
> __asm__ __volatile__("movl $0xdeadface, %ebx\n");
> (void)ioctl(0, TCGETS, &t);
> (void)getpid();
> __asm__ __volatile__("cmpl $0xdeadface, %ebx\n"
> "jnz barf\n");
> return 0;
> }

> Until you can show the syscall that doesn't follow the correct
> rules, then my example is not flawed. In fact a modified example can
> be used to find any broken calls.
I putted in some assembler code in my original post that uses the sys_poll
syscall of which i _know_ it modifies one of it's arguments, to be more
specific, it's first argument, which is %ebx on passing in:
asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
      for(i=0; i < (int)nfds; i++, ufds++, fds1++) {

and in fact we saw that the change in %ebx is proportional to the nfds
as passed to sys_poll.

now however for sys_ioctl:
it's first argument, fd (%ebx on passing) is never modifed in the code
nowhere is there an fd++ or similar, so again this 'example' of yours is


P.S. i think my original was quite clear and INCLUDED example code that can
easily be checked by someone who reads asm, i attach an extra copy which
explains all the asm in there for easier reference.

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to
More majordomo info at
Please read the FAQ at

This archive was generated by hypermail 2b29 : Mon Sep 23 2002 - 22:00:27 EST