Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sat, 25 Feb 2012 01:56:13 -0500
From: Rich Felker <dalias@...ifal.cx>
To: musl@...ts.openwall.com
Subject: Re: tough choice on thread pointer initialization issue

On Thu, Feb 09, 2012 at 09:58:25PM -0500, Rich Felker wrote:
> the bug i've found occurs when the thread pointer happens to get
> initialized in code that runs from a signal handler. this is a rare
> situation that will only happen if the program is avoiding
> async-signal-unsafe functions in the main flow of execution so that
> it's free to use them in a signal handler, but despite being rare,
> it's perfectly legal, and right now musl crashes on such programs, for
> example:
> [...]
> the issue is that when a signal handler returns, all registers,
> including the thread-pointer registers (%gs or %fs on x86 or x86_64)
> are reset to the values they had in the code the signal interrupted.
> thus, musl thinks the thread pointer is valid at this point, but it's
> actually null.
> [...]
> before i make a decision, i'd like to hear if anyone from the
> community has strong opinions one way or the other. i've almost ruled
> out approach #1 and i'm leaning towards #3, with the idea that
> simplicity is worth more than a couple trivial syscalls.

PING.

Any more thoughts on this issue?

I've looked into the "solution 4" that Solar proposed, which simply
put is making __pthread_self() check for a clobbered thread register
and restore it. While this sounds simple, there are a few issues I've
run into whereby it starts to get ugly...

1. The proper value of the %gs register (for i386) or %fs register
(for x86_64) needs to be saved somewhere global so that __pthread_self
can restore it. This seems to be solvable but requires putting a
global 16-bit var in __set_thread_area.s and getting the asm right to
make it PIC-safe, and might call for some extra weak symbol hackery to
avoid pulling in excess code.

2. On x86_64, it seems that 0 is a valid value for %fs, so we can't
really tell if it's uninitialized! I don't have a machine to test
with, but from reading the kernel sources, it looks like %fs is 0 and
a hidden 64-bit offset is stored in a privileged register accessible
only by the kernel (one which hopefully would not be clobbered by
sigreturn, but I'm not sure...) when the thread pointer does not fit
in 32 bits, and the old i386 method (LDT entry and non-zero %fs
selector value) is used when the thread pointer fits in 32 bits. This
might be a show-stopper, because if we can't tell if the thread
pointer is valid, we can't restore it (and the value of %fs might
actually need to differ per-thread if some threads are below the 4gb
boundary and others are located above).

3. The code in __pthread_self() to test %gs and call out to external
code when it needs to be restored is larger than I would like,
especially since the inline asm block uses no clobbers, thus requiring
all registers to be saved across a call from the asm.

With these issues in mind, Solar's solution is seeming less appealing
and maybe not even feasible. Any ideas for salvaging it, or should I
fall back to one of the other options? (My original leaning was
towards always setting up the thread pointer prior to program entry.)

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.