[PATCH v4 00/14] RELRO link maps
Florian Weimer
fweimer@redhat.com
Wed Feb 5 11:18:45 GMT 2025
* Kevin Brodsky:
> Hi Florian,
>
> [For folks I've just added in Cc, here is the full series:
> https://inbox.sourceware.org/libc-alpha/cover.1738530302.git.fweimer@redhat.com/]
>
> On 04/02/2025 15:55, Florian Weimer wrote:
>>> [...]
>> This means that AArch64 implements the x86-64 protection key semantics
>> for signal handling, and not the POWER semantics: all access rights are
>> revoked in signal handlers. (Signal handlers on POWER inherit the
>> access rights from the interrupted context.)
>
> I was not aware that POWER handled its pkey register in this way during
> signal delivery. For context, I recently changed the way arm64 handles
> its pkey register during signal delivery [1]. Before elaborating, let me
> first clarify that there are two pkey register values to consider here:
>
> 1. The value used while the kernel writes the signal frame (impacting
> uaccess routines like put_user())
> 2. The value set by the kernel just before invoking the signal handler
Interesting, I had not considered the first case.
> The second value remains unchanged: full access to pkey 0 and no access
> to other pkeys (in line with x86). What [1] did is to remove any
> restriction to the first value, so that pkeys never prevent the kernel
> from writing the signal frame. This enables the use of an alternate
> signal stack mapped with a pkey that isn't accessible to regular code.
> That change aligned arm64 with x86, where a similar series landed
> earlier [2]. Hopefully POWER does the same thing for that first value -
> I don't think pkeys restriction are useful in that situation.
Huh. This change surprises me. I had to think about it for a bit.
Is this considered save because the target address for the signal frame
write comes from the alternate signal address range tracked within the
kernel, so the kernel knows for certain this isn't scribbling over
random memory? (I'm not worried about exploit mitigation here, but
randomly scribbling over authoritative database transaction logs.)
> Now focusing on the second value, which is the one userspace actually
> sees, when the signal handler is invoked. Simply inheriting they pkey
> register from the interrupted context seems undesirable to me:
> considering that an asynchronous signal may interrupt any arbitrary
> application code, how could a glibc handler possibly rely on that
> context's pkey register value (which could be anything)? A signal
> handler most likely needs a well-defined pkey configuration, regardless
> of what triggers it.
If the application changes the pkey register for protection keys it has
not allocated and calls into glibc (or any unrelated library it does not
control, really), this is already broken because the library may have
expectations how its own allocated protection keys are used throughout
the system. This is why we have pkey_alloc, so that unrelated libraries
can avoid interacting unexpectedly. Signal handlers calling
async-signal-safe library functions just add more such problematic calls
in a rather indirect fashion.
On the other hand, it may be desirable to support an instruction
sequence which sets the pkey registers to a single key around a specific
memory access, to assert that this particular memory location has a
particular protection key. (This might be a bit tricky to do properly
without invalidating instruction fetches as well.) To make this
reliable without also disabling signals around the pkey register change,
the POWER approach to pkey register inheritance is insufficient.
Changing the signal mask around the access sequence may be too expensive
in practice.
>> We could reset the access rights to the expected values at the start
>> of _dl_fixup, _dl_find_object and other dynamic linker functions used
>> in signal handlers. This should allow us [to avoid] crashes in glibc
>> code. However, it will not help with applications that keep around
>> [a] link map pointer (like the l_name string) and use it from signal
>> handlers, or use _r_debug from signal handlers.
>
> This is indeed a concern. This currently requires using some sort of
> trampoline (on arm64/x86) to reset the pkey register to the desired
> value before invoking the actual signal handler - I appreciate this may
> not be an option for arbitrary application code.
Trampolines are really hard here because we cannot easily switch the
SA_* flags and the signal handler atomically (if we just have one
function pointer per signal, for the real handler). This has come up in
a different context:
Wrapping Signal Handlers
<https://sourceware.org/glibc/wiki/SignalHandlerWrapper>
Some level of kernel support (like a closure pointer that is available
to the handler) would make this much easier to implement, but I don't
know how applications would react to the extra stack frame. That
wouldn't necessarily be relevant to changing the pkey register because
we don't need to run code on exit from the handler, we could just
tail-call into the handler. Anyway, this is probably a digression.
> Introducing a new kernel-user API to configure the pkey register for
> signal handlers does feel desirable, but no consensus has emerged on
> what that API should actually look like. A sigaltstack() flag has been
> suggested [3], but as noted in [4] this may not be appropriate given
> that the alternate signal stack is not the only protected mapping that
> could be accessed from a signal handler, and your series proves exactly
> this point. An alternative would be a new (generic) prctl command, or
> maybe a whole new syscall, if that's really warranted.
I proposed two different approaches:
pkeys: Support setting access rights for signal handlers
<https://lore.kernel.org/all/5fee976a-42d4-d469-7058-b78ad8897219@redhat.com/>
[PATCH] mm, x86: pkeys: Introduce PKEY_ALLOC_SIGNALINHERIT and change
signal semantics
<https://lore.kernel.org/all/360ef254-48bc-aee6-70f9-858f773b8693@redhat.com/>
I prefer the first approach. It allows userspace to write
pkey_alloc(PKEY_ALLOC_SETSIGNAL, PKEY_DISABLE_WRITE);
instructing the kernel to set the initial access rights in signal
handlers *for the key being allocated* to PKEY_DISABLE_WRITE, and read
access in signal handlers is not revoked for this particular key. I
think it solves the problem for single-key access sequences mentioned
earlier. The second approach (basically switching to the POWER pkey
inheritance model and make this discoverable from userspace) would not
cover that.
Thanks,
Florian
More information about the Libc-alpha
mailing list