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

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Thu Sep 4 14:32:31 GMT 2025



On 04/09/25 10:58, Mark Brown wrote:
> On Thu, Sep 04, 2025 at 09:51:59AM -0300, Adhemerval Zanella Netto wrote:
>> 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?
> 
> No, the whole point of the token is to ensure that you can't use
> clone3() to set an arbitrary GCSPTR_EL0 - clone3() checks that a token
> is present and will report an error if one is not already present.  We
> need something to ensure that clone3() isn't simply being pointed at an
> already active shadow stack since that would open up attacks.  The API
> does this by treating the clone3() as being similar to a shadow stack
> switch.
> 
>> 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.
> 
> Like I say you can pick the address used for map_shadow_stack() so if
> it's just about picking addresses that's available, though things having
> to be unmapped is annoying there.

The problem is pthread_create does not take any locks, so trying to specify
the address to map_shadow_stack might fail (because kernel can use the VMA
by other thread).

> 
>>>> 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?
> 
> I'd need to go and dig into all the concerns people had, but yes token
> setup is a big part of it, as is the more general potential for
> something to write to the shadow stack memory if the shadow stack memory
> is ever writable by userspace before it gets marked as a shadow stack.
> Another part of the concern was keeping the interface simple to reason
> about, there's a lot of complexity with the memory management
> interfaces.
> 
>> 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.
> 
> Yes, if you pass a suitable address to map_shadow_stack() you can
> do that, though note that the shadow stack is read only so the guard
> would only prevent writes.

The guard pages was mainly to avoid stack overflows, and we map it as none
as optimization.



More information about the Libc-alpha mailing list