[PATCH v4 00/14] RELRO link maps

Kevin Brodsky kevin.brodsky@arm.com
Thu Feb 6 13:28:31 GMT 2025


On 05/02/2025 12:18, Florian Weimer wrote:
> * 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.)

I would say so, yes. If the regular stack is used, the kernel writing
the signal frame is no different from the program calling some function
writing to its own stack frame. If the alternate signal stack is used,
that location has been explicitly set up via sigaltstack() and can be
considered somewhat trusted.

>> 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.

I see your point. Regular code should only operate on the keys it
allocated, leaving the permissions unchanged for the other keys. However
as you also point out, this assumption may not always hold: special code
(let's say generated code in a managed runtime) may be further
restricted so that access to specific keys only is granted.

Relying on permission inheritance in signal handlers essentially forbids
any code from revoking access to default keys, which I think is an
important sandboxing mechanism.

> [...]
>
>> 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.

Indeed. I had not considered the first approach, its simplicity is
appealing. It seems like a plausible solution to me, though I can see
two potential issues:

1. There is no way to change the "signal permissions" for a pkey that is
already allocated.

2. As Dave Hansen pointed out in the original thread, this is specific
to pkeys by design, but other extensions may call for a similar
mechanism (specifying the value of a register on signal entry).

The approach I hinted at, which is something like
prctl(PR_SET_SIGNAL_REG, REG_PKEY, ...) would address 1. and be more
flexible. However, a significant issue is that the pkey register format
is completely arch-specific. We could define a generic format and have
each architecture translate it as needed, but this is clearly more
hassle for both userspace and the kernel.

If there is sufficient consensus that the approach you proposed in [5]
is satisfactory, I am happy to help with the arm64 implementation.

- Kevin

[5]
https://lore.kernel.org/linux-mm/5fee976a-42d4-d469-7058-b78ad8897219@redhat.com/


More information about the Libc-alpha mailing list