misc/177624: Swapcontext can get compiled incorrectly

Bruce Evans brde at optusnet.com.au
Thu Apr 4 13:50:01 UTC 2013

The following reply was made to PR misc/177624; it has been noted by GNATS.

From: Bruce Evans <brde at optusnet.com.au>
To: Brian Demsky <bdemsky at uci.edu>
Cc: freebsd-gnats-submit at FreeBSD.org, freebsd-bugs at FreeBSD.org
Subject: Re: misc/177624: Swapcontext can get compiled incorrectly
Date: Fri, 5 Apr 2013 00:46:32 +1100 (EST)

   This message is in MIME format.  The first part should be readable text,
   while the remaining parts are likely unreadable without MIME-aware tools.
 Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
 Content-Transfer-Encoding: QUOTED-PRINTABLE
 On Thu, 4 Apr 2013, Brian Demsky wrote:
 >> Description:
 > Here is the code for swap context:
 > int
 > swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
 > {
 >      int ret;
 >      if ((oucp =3D=3D NULL) || (ucp =3D=3D NULL)) {
 >              errno =3D EINVAL;
 >              return (-1);
 >      }
 >      oucp->uc_flags &=3D ~UCF_SWAPPED;
 >      ret =3D getcontext(oucp);
 >      if ((ret =3D=3D 0) && !(oucp->uc_flags & UCF_SWAPPED)) {
 >              oucp->uc_flags |=3D UCF_SWAPPED;
 >              ret =3D setcontext(ucp);
 >      }
 >      return (ret);
 > }
 > On the OS X port of libc in Mac OSX 10.7.5, this gets compiled as:
 > ...
 > 0x00007fff901e870b <swapcontext+89>:    pop    %rbx
 > 0x00007fff901e870c <swapcontext+90>:    pop    %r14
 > 0x00007fff901e870e <swapcontext+92>:    jmpq   0x7fff90262855 <setcontext=
 > The problem is that rbx is callee saved by compiled version of swapcontex=
 t and then reused before getcontext is called.  Getcontext then stores the =
 wrong value for rbx and setcontext later restores the wrong value for rbx. =
  If the caller had any value in rbx, it has been trashed at this point.
 Later you wrote:
 > The analysis is a little wrong about the problem.  Ultimately, the tail c=
 all to set context trashes the copies of bx and r14 on the stack=85.
 The bug seems to be in setcontext().  It must preserve the callee-saved
 registers, not restore them.  This would happen automatically if more
 were written in C.  But setcontext() can't be written entirely in C,
 since it must save all callee-saved registers including ones not used
 and therefore not normally saved by any C function that it might be in,
 and possibly also including callee-saved registers for nonstandard or
 non-C ABIs.  In FreeBSD, it is apparently always a syscall.
 In FreeBSD, this bug doesn't occur on at least amd64 or i386 because the
 C version of swapcontext() has never been used on these arches.
 swapcontext() is a syscall too.
 If setcontext() is a syscall, then it has a minor problem even knowing
 what the ABI's callee-saved registers are.  At least the FreeBSD amd64
 version doesn't know anything about this.  It uses much the same code
 as for asynchronous signal handling, so it just restores all registers,
 including scratch ones that don't need to be preserved.  It even
 restores the return register to the trap frame, although it can't
 return this to userland.  This can probably be fixed a library wrapper.
 In FreeBSD on amd64, getcontext(), setcontext() and swapcontext() are
 all syscalls, but their documenation is misplaced in a section 3 man
 page.  swapcontext() is misplaced together with makecontext(), which
 actually is library function.  Oops, not quite.  getcontext() is
 actually a small wrapper around an undocumented setcontext() syscall
 (this is needed to adjust the instruction pointer register).  Only
 makecontext() is in C, and not a wrapper.  I don't count the wrappers
 that just make a syscall as library functions, since the corresponding
 syscalls can be made easily without using the C library and this should
 be documented.

More information about the freebsd-bugs mailing list