[1/8] elf: Propagate the pointer guard to ld.so loaded via static dlopen (BZ 34196)
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
Commit Message
The static-dlopen does not initialize the pointer guard for ABIs that
define THREAD_SET_POINTER_GUARD. Besides not properly guard the
pointer if a libc.so symbol is called, this can leads to setjmp
failures (a jmp_buf set up by the loaded ibc.so.6 cannot be restored
by the static program's __longjmp, and vice versa).
Seed the just-mapped loader's __pointer_chk_guard from the program's
__pointer_chk_guard_local in __rtld_static_init, next to the other
runtime values copied there.
Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.
---
elf/Makefile | 6 +++
elf/rtld_static_init.c | 12 +++++
elf/tst-ptrguard-static-dlopen-mod.c | 29 +++++++++++
elf/tst-ptrguard-static-dlopen.c | 51 +++++++++++++++++++
.../tst-ptrguard-static-dlopen.script | 1 +
5 files changed, 99 insertions(+)
create mode 100644 elf/tst-ptrguard-static-dlopen-mod.c
create mode 100644 elf/tst-ptrguard-static-dlopen.c
create mode 100644 elf/tst-ptrguard-static-dlopen.root/tst-ptrguard-static-dlopen.script
Comments
Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
> The static-dlopen does not initialize the pointer guard for ABIs that
> define THREAD_SET_POINTER_GUARD. Besides not properly guard the
> pointer if a libc.so symbol is called, this can leads to setjmp
"can lead"
> failures (a jmp_buf set up by the loaded ibc.so.6 cannot be restored
> by the static program's __longjmp, and vice versa).
s/ibc/libc/
> diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c
> index 04eb1c6fcc4..a428542e57e 100644
> --- a/elf/rtld_static_init.c
> +++ b/elf/rtld_static_init.c
> @@ -81,5 +81,17 @@ __rtld_static_init (struct link_map *map)
> dl->_dl_find_object = _dl_find_object;
> dl->_dl_readonly_area = _dl_readonly_area;
>
> +#ifndef THREAD_SET_POINTER_GUARD
> + extern uintptr_t __pointer_chk_guard_local attribute_hidden;
> + const ElfW(Sym) *guard_sym
> + = _dl_lookup_direct (map, "__pointer_chk_guard",
> + 0x69f99cab, /* dl_new_hash output. */
Missing _
> + "GLIBC_PRIVATE",
> + 0x0963cf85); /* _dl_elf_hash output. */
> + if (guard_sym != NULL)
What do we do if it is NULL ? Just ignore it?
> + *(uintptr_t *) DL_SYMBOL_ADDRESS (map, guard_sym)
> + = __pointer_chk_guard_local;
Do we need to check for DL_SYMBOL_ADDRESS() returning NULL? The
definitions include such returns.
> diff --git a/elf/tst-ptrguard-static-dlopen-mod.c b/elf/tst-ptrguard-static-dlopen-mod.c
> @@ -0,0 +1,29 @@
> +#include <setjmp.h>
> +
> +static jmp_buf jb;
> +void (*do_longjmp) (jmp_buf);
> +
> +void
> +foo (void)
> +{
> + if (setjmp (jb) == 0)
> + do_longjmp (jb);
> +}
Ok.
> diff --git a/elf/tst-ptrguard-static-dlopen.c b/elf/tst-ptrguard-static-dlopen.c
> +/* A statically linked program dlopens a shared object; the object's setjmp
> + uses the just-mapped libc.so's pointer guard while the longjmp below uses
> + the program's guard. Unless __rtld_static_init propagates the guard to
> + the loaded loader the two differ, and the setjmp/longjmp round-trip jumps
> + to a corrupt address and crashes. */
> +
> +#include <setjmp.h>
> +#include <support/check.h>
> +#include <support/xdlfcn.h>
> +
> +static void
> +call_longjmp (jmp_buf jb)
> +{
> + longjmp (jb, 1);
> +}
> +
> +static int
> +do_test (void)
> +{
> + void *h = xdlopen ("tst-ptrguard-static-dlopen-mod.so", RTLD_NOW);
> + void (*foo) (void) = xdlsym (h, "foo");
> + void (**do_longjmp) (jmp_buf) = xdlsym (h, "do_longjmp");
> + *do_longjmp = call_longjmp;
> +
> + /* foo () sets the jump buffer and calls back into call_longjmp; a
> + mismatched guard makes the return jump fault. */
> + foo ();
> +
> + xdlclose (h);
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
Ok.
On 09/06/26 22:35, DJ Delorie wrote:
> Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
>
>> The static-dlopen does not initialize the pointer guard for ABIs that
>> define THREAD_SET_POINTER_GUARD. Besides not properly guard the
>> pointer if a libc.so symbol is called, this can leads to setjmp
>
> "can lead"
Ack.
>
>> failures (a jmp_buf set up by the loaded ibc.so.6 cannot be restored
>> by the static program's __longjmp, and vice versa).
>
> s/ibc/libc/
Ack.
>
>> diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c
>> index 04eb1c6fcc4..a428542e57e 100644
>> --- a/elf/rtld_static_init.c
>> +++ b/elf/rtld_static_init.c
>> @@ -81,5 +81,17 @@ __rtld_static_init (struct link_map *map)
>> dl->_dl_find_object = _dl_find_object;
>> dl->_dl_readonly_area = _dl_readonly_area;
>>
>> +#ifndef THREAD_SET_POINTER_GUARD
>> + extern uintptr_t __pointer_chk_guard_local attribute_hidden;
>> + const ElfW(Sym) *guard_sym
>> + = _dl_lookup_direct (map, "__pointer_chk_guard",
>> + 0x69f99cab, /* dl_new_hash output. */
>
> Missing _
>
Ack.
>> + "GLIBC_PRIVATE",
>> + 0x0963cf85); /* _dl_elf_hash output. */
>> + if (guard_sym != NULL)
>
> What do we do if it is NULL ? Just ignore it?
We should assert, as for _rtld_global_ro. I will fix it.
>
>> + *(uintptr_t *) DL_SYMBOL_ADDRESS (map, guard_sym)
>> + = __pointer_chk_guard_local;
>
> Do we need to check for DL_SYMBOL_ADDRESS() returning NULL? The
> definitions include such returns.
I do not think we need for this specific case, __rtld_static_init already
assumes that DL_SYMBOL_ADDRESS (map, "_rtld_global_ro") always works.
For a valid map and symbol reference, DL_SYMBOL_ADDRESS would return NULL
for two cases:
1. l_addr == 0 and st_value == 0
2. SHN_ABS symbol with st_value == 0
The 1. should not happen for the libc.so, and for 2. we need either explicit
set the symbol value to 0 (as elf/tst-absolute-zero-lib.lds does).
>
>> diff --git a/elf/tst-ptrguard-static-dlopen-mod.c b/elf/tst-ptrguard-static-dlopen-mod.c
>> @@ -0,0 +1,29 @@
>> +#include <setjmp.h>
>> +
>> +static jmp_buf jb;
>> +void (*do_longjmp) (jmp_buf);
>> +
>> +void
>> +foo (void)
>> +{
>> + if (setjmp (jb) == 0)
>> + do_longjmp (jb);
>> +}
>
> Ok.
>
>> diff --git a/elf/tst-ptrguard-static-dlopen.c b/elf/tst-ptrguard-static-dlopen.c
>> +/* A statically linked program dlopens a shared object; the object's setjmp
>> + uses the just-mapped libc.so's pointer guard while the longjmp below uses
>> + the program's guard. Unless __rtld_static_init propagates the guard to
>> + the loaded loader the two differ, and the setjmp/longjmp round-trip jumps
>> + to a corrupt address and crashes. */
>> +
>> +#include <setjmp.h>
>> +#include <support/check.h>
>> +#include <support/xdlfcn.h>
>> +
>> +static void
>> +call_longjmp (jmp_buf jb)
>> +{
>> + longjmp (jb, 1);
>> +}
>> +
>> +static int
>> +do_test (void)
>> +{
>> + void *h = xdlopen ("tst-ptrguard-static-dlopen-mod.so", RTLD_NOW);
>> + void (*foo) (void) = xdlsym (h, "foo");
>> + void (**do_longjmp) (jmp_buf) = xdlsym (h, "do_longjmp");
>> + *do_longjmp = call_longjmp;
>> +
>> + /* foo () sets the jump buffer and calls back into call_longjmp; a
>> + mismatched guard makes the return jump fault. */
>> + foo ();
>> +
>> + xdlclose (h);
>> + return 0;
>> +}
>> +
>> +#include <support/test-driver.c>
>
> Ok.
>
@@ -270,6 +270,7 @@ tests-static-normal := \
tst-env-setuid-static \
tst-getauxval-static \
tst-linkall-static \
+ tst-ptrguard-static-dlopen \
tst-single_threaded-pthread-static \
tst-single_threaded-static \
tst-tls-allocation-failure-static \
@@ -576,6 +577,7 @@ tests-container += \
tst-dlopen-tlsmodid-container \
tst-pldd \
tst-preload-pthread-libc \
+ tst-ptrguard-static-dlopen \
tst-rootdir \
# tests-container
@@ -1013,6 +1015,7 @@ modules-names += \
tst-null-argv-lib \
tst-p_alignmod-base \
tst-p_alignmod3 \
+ tst-ptrguard-static-dlopen-mod \
tst-recursive-tlsmallocmod \
tst-recursive-tlsmod0 \
tst-recursive-tlsmod1 \
@@ -3177,6 +3180,9 @@ $(objpfx)tst-tls21mod.so: $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so)
$(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
+$(objpfx)tst-ptrguard-static-dlopen.out: \
+ $(objpfx)tst-ptrguard-static-dlopen-mod.so
+
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
$(objpfx)tst-ro-dynamic: $(objpfx)tst-ro-dynamic-mod.so
@@ -81,5 +81,17 @@ __rtld_static_init (struct link_map *map)
dl->_dl_find_object = _dl_find_object;
dl->_dl_readonly_area = _dl_readonly_area;
+#ifndef THREAD_SET_POINTER_GUARD
+ extern uintptr_t __pointer_chk_guard_local attribute_hidden;
+ const ElfW(Sym) *guard_sym
+ = _dl_lookup_direct (map, "__pointer_chk_guard",
+ 0x69f99cab, /* dl_new_hash output. */
+ "GLIBC_PRIVATE",
+ 0x0963cf85); /* _dl_elf_hash output. */
+ if (guard_sym != NULL)
+ *(uintptr_t *) DL_SYMBOL_ADDRESS (map, guard_sym)
+ = __pointer_chk_guard_local;
+#endif
+
__rtld_static_init_arch (map, dl);
}
new file mode 100644
@@ -0,0 +1,29 @@
+/* Shared object for the static-dlopen pointer guard test.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <setjmp.h>
+
+static jmp_buf jb;
+void (*do_longjmp) (jmp_buf);
+
+void
+foo (void)
+{
+ if (setjmp (jb) == 0)
+ do_longjmp (jb);
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* Test that the pointer guard is propagated to ld.so via static dlopen.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* A statically linked program dlopens a shared object; the object's setjmp
+ uses the just-mapped libc.so's pointer guard while the longjmp below uses
+ the program's guard. Unless __rtld_static_init propagates the guard to
+ the loaded loader the two differ, and the setjmp/longjmp round-trip jumps
+ to a corrupt address and crashes. */
+
+#include <setjmp.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static void
+call_longjmp (jmp_buf jb)
+{
+ longjmp (jb, 1);
+}
+
+static int
+do_test (void)
+{
+ void *h = xdlopen ("tst-ptrguard-static-dlopen-mod.so", RTLD_NOW);
+ void (*foo) (void) = xdlsym (h, "foo");
+ void (**do_longjmp) (jmp_buf) = xdlsym (h, "do_longjmp");
+ *do_longjmp = call_longjmp;
+
+ /* foo () sets the jump buffer and calls back into call_longjmp; a
+ mismatched guard makes the return jump fault. */
+ foo ();
+
+ xdlclose (h);
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1 @@
+cp $B/elf/tst-ptrguard-static-dlopen-mod.so $L/tst-ptrguard-static-dlopen-mod.so