Question about stack protector in RELRO on Linux

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed Mar 18 20:38:20 GMT 2026



On 18/03/26 07:07, Jakob Koschel wrote:
> Thank you for the response!
> 
>> On 16. Mar 2026, at 18:43, Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 16/03/26 07:24, Jakob Koschel wrote:
>>> I came across Florian Weimer's talk on "New TLS allocators for glibc" and reached out with some questions with the suggestion to move it here (this is my first time posting here, so I hope this fits).
>>>
>>> I've been looking at this mostly in the context of https://sourceware.org/bugzilla/show_bug.cgi?id=22850 and what could be done to avoid a large contiguous stack overflow to overwrite the stack protector value in the TCB.
>>>
>>> Florian mentioned in his response that GCC 16 would likely allow mirroring the OpenBSD model of putting the stack protector into RELRO memory. I was curious what changes in gcc/glibc does that actually require? Is the compiler side of things primarily using `-mstack-protector-guard=global` and most changes for it need to happen in glibc (to support 'global' on x86_64 Linux and allocate it correctly in RELRO)?
>>
>> This request is being tracked by BZ 26817 [1]. On glibc this is defined internally
>> by the THREAD_SET_STACK_GUARD macro, defined at tls.h for each ABI.  The global
>> RELRO pointer require to extend the glibc ABI, for instance on aarch64:
>>
>> $ grep -w __stack_chk_guard sysdeps/unix/sysv/linux/aarch64/ld.abilist
>> ./sysdeps/unix/sysv/linux/aarch64/ld.abilist:GLIBC_2.17 __stack_chk_guard D 0x8
>>
>> The current code assumes the ABI will either place the stack pointer in per-thread
>> memory (usually on TCB, as for x86_64) or through the __stack_chk_guard global
>> variable.
>>
>> Having both ways would require to refactor the code to install the cookie on
>> both places and also start to export __stack_chk_guard as a new ABI symbol.
>> The later that the implication that binaries built with -mstack-protector-guard=global
>> will fail to load on old glibc.
> 
> Right, I've seen that bug and also found the proposed patch [1]. I didn't see any update
> there recently, so I was curious what might change with GCC 16 making this easier.

I think it got stalled because Fangrui did not followed after Florian
latest comment.  Afaik -mstack-protector-guard=global was added on 
gcc and clang to solve a specific problem for bare-metal systems, 
bootloaders, and OS kernels (like the Linux kernel) that cannot rely 
on Thread-Local Storage (TLS) to retrieve the stack protector "canary" 
value. 

The -mstack-protector-guard=tls is the intend ABI for userland and
using the global mode has some performance implications:

- An extra memory read (pointer indirection) for every protected function
  call and return due the GOT usage on PIC objects.

- A global __stack_chk_guard sits in a single memory location shared by 
  all threads across all CPU cores. While the variable is read-only 
  (meaning it won't cause severe cache-invalidation), all CPU cores still
  have to fetch it from the exact same physical memory address. This add
  extra memory contention on some scenarios.

But at same time I do not think performance would be a problem here, usually
compilers defaults to -fstack-protector=strong and gcc added quite
aggressive inline/LTO optimizations (so stack protector prologue/epilogue
should be amortized by total function cost).

I think a per-DSO canary would an interesting project, but it would 
required extensive changes on the toolchain (like new compiler flags,
and relocations as Florian added).

> 
>>
>> This change is orthogonal to the "New TLS allocators for glibc" work proposed by
>> Florian.
>>
>>>
>>> As for splitting static TLS and stack on non-main threads, I understood it's not worth it doing it in two transitions (splitting it and then moving to the new TLS allocator) and the new TLS allocator is mostly "struggling" with supporting sanitizers correctly.
>>>
>>> Probably a stupid question: Would it be possible to add a guard page between stack and static TLS?
>>> I'm guessing it's not worth it given the "problem" will go eventually away with the new TLS allocator and allowing to store the stack protector in RELRO.
>>
>> It would be possible, but it would add extra overhead as Fangrui has pointed 
>> in comment #1 (extra memory consumption due extra alignment, more memory 
>> mapping and extra page tables).
> 
> This sounds like it most likely wouldn't get accepted as a patch.
> I wonder if with lightweight guard pages in Linux the problem is not as bad
> or if there is an option to put it behind a tunable that would make it
> realistic enough to implement and send for review?

I am not sure if Florian will follow up with his new TLS allocator to remove
the TCB being place at the top of the stack.  This would be the ideal solution
indeed, but I don't think we have a timeframe for that yet.

And I would prefer to not gate this hardening behind a tunable, or at least
make it the default.  This will require align the static TLS block to
page size and adding another page to act as the guard, but at least with
MADV_GUARD_INSTALL it should be cheap in term of memory utilization
(but we will still need provide a PROT_NONE fallback).

The pthread interfaces provides an option to disable the guard page, so we
might disable this is the guard size is disabled.

What worries most is the required extra complexity to enable this in a
generic manner, which would require handle both TLS_TCB_AT_TP and
TLS_DTV_AT_TP plus PROT_NONE/MADV_GUARD_INSTALL along with pthread
guard size. But I think it should be doable.

> 
> Basically, I see three options:
> 
> 1) allow '-mstack-protector-guard=global'
> 2) the new TLS allocator that no longer allocate stack and static TLS together
> 3) add a guard page (potentially behind a tunable?)
> 
> If 1) or 2) are realistically happening (relatively) soon, there is no reason
> to work/propose 3).
> Otherwise, it might be a good solution in the meantime.
> 

I would say 1. seem the most straightforward *today*, since there is
already compile support.  The 3. is also doable, it does not depended
of a ABI symbol and can be backportable.


More information about the Libc-alpha mailing list