[PATCH v2 2/3] nptl: Add libc allocated shadow stack for new threads

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Thu Sep 4 12:51:59 GMT 2025



On 03/09/25 17:46, Mark Brown wrote:
> On Wed, Sep 03, 2025 at 04:46:47PM -0300, Adhemerval Zanella Netto wrote:
> 
>> There is currently no logic to resize the thread cache; it only adjusts the
>> guarded control stack as needed. Remapping would defeat the cache’s main
>> benefit, since allocating a new stack is typically faster (and, in cases
>> where virtual memory area constraints are significant, users can already
>> limit the cache size).
> 
> Note that you need a token on the shadow stack so reuse of an already
> used one is perhaps more trouble to arrange than it's worth for glibc.
> The security requirements do result in some overhead.

Wouldn't be possible to kernel setup the guard shadow token stack during 
clone3?

And the security overhead is expected, it is just that if we could allocate
both the stack and the guard shadow region as a single block it would simplify
userland management.

> 
>> As a side-note, things would be a lot more straightforward if userland could
>> simply allocate a region such as [stack + guard control stack], and the kernel 
>> offered an API (e.g., via `madvise`) to establish the control-guarded region. 
> 
> That was discussed as part of the API design when the x86 shadow stack
> support was being merged, from what I remember of reading the discussion
> people weren't very happy with the security issues surrounding being
> able to madvise() existing blocks of memory to be shadow stacks due to
> the ease with which race conditions or other bugs could be introduced.
> This is especially true given the need to place a token in the shadow
> stack, something needs to place the token and half the goal is that
> userspace can't write to the shadow stack.

Would this possible race-condition due the token setup, since madvise is not
expected to change the memory contents of the input region?

> 
> If you're willing to take an extra syscall anyway then map_shadow_stack()
> can take an address as an argument so you can control placement, you
> just have to call it on an unmapped region (though I suspect you're
> thinking of working from an already mapped block of memory so that
> doesn't work).

The problem is not the syscall itself, it is that whole idea of glibc thread
cache is to avoid mmap/munmap the thread stack on each pthread_create.  But
GCS already requires the kernel to do it on anyway for the guarded region, so
moving the logic to userland should be ok.

> 
>> Alternatively, if `clone3` itself allowed definition of the guard region start, 
>> this could be exposed. My understanding is that the guarded region is intended 
>> to prevent all writes except specific GCS operations, effectively acting as a
>> guard page.
> 
> The GCS/shadow stack is normally only writable by userspace through
> procedure call and return instructions or explicit stack pivot
> instructions.
> 
> An older version of the clone3() ABI did specify the shadow stack as a
> base and size like we do for the regular stack.  We could I guess do
> that and also do the allocation, though that'd be inconsistent with the
> way we handle the regular stack and we'd also have flags like we do in
> map_shadow_stack() to allow for configuration of the optional top of
> stack marker and anything else that comes up in future.  You'd have to
> handle the case where something, potentially something mapped with
> map_shadow_stack(), is already there.  It *does* save a syscall but I'm
> having a hard time loving it, there's a bunch of stuff to get right
> there and it's not terribly well aligned with the regular stack.
> 
> You could also just specify the size then have the kernel manage the
> allocation and free like it does for regular clone(), the main goal here
> is to allow userspace to control the size.  However I suspect that there
> will be users that do want to control the placement for whatever reason
> (eg, spawning a real thread for an existing userspace thread).

In agree that tying this to clone3 is not the best approach; my idea is
if we could place the GCS adjacent to the thread stack itself it could be
used as the pthread guard region for free.  But now that we we support
MADV_GUARD_INSTALL to setup the guard page, I guess the potential gains
are not that much.


More information about the Libc-alpha mailing list