[PATCH] elf: Open the normalized $ORIGIN rpath in AT_SECURE programs (BZ 34360)
Adhemerval Zanella
adhemerval.zanella@linaro.org
Wed Jul 15 19:52:45 GMT 2026
For AT_SECURE programs the loader honors $ORIGIN in DT_RPATH only when the
expansion is rooted in a trusted directory, but it validated the normalized
path while opening the expansion. For DT_PATH with "../" and if the an
attacker who controls a component of $ORIGIN (e.g. by hard-linking the
setuid binary into an attacker-owned directory), they can make the opened
path escape the trusted directory even though the check passed.
Normalize the expansion in place and open that, so the path that is opened
is exactly the path that was validated. dst_normalize_path rewrites the
string in place without ever advancing its write cursor past its read
cursor or appending, so it stays within the original storage.
It also collapses runs of '/' before interpreting "/." and "/..", which
is_trusted_path_normalize did not. The old code normalized "/lib64//../x"
to "/lib64/x" and checked that. This was second way for the validated and
the opened path to disagree.
The trusted directory check is split out into path_is_trusted, which
matches the normalized path against the system_dirs entries on component
boundaries, so it no longer needs a scratch buffer to append a trailing
slash.
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
---
elf/Makefile | 16 ++
elf/dl-load.c | 119 +++++++-----
elf/libtst-origin-secure-mod.c | 25 +++
elf/tst-origin-secure-evilmod.c | 28 +++
elf/tst-origin-secure-victim.c | 48 +++++
elf/tst-origin-secure.c | 228 +++++++++++++++++++++++
elf/tst-origin-secure.h | 41 ++++
elf/tst-origin-secure.root/postclean.req | 0
8 files changed, 457 insertions(+), 48 deletions(-)
create mode 100644 elf/libtst-origin-secure-mod.c
create mode 100644 elf/tst-origin-secure-evilmod.c
create mode 100644 elf/tst-origin-secure-victim.c
create mode 100644 elf/tst-origin-secure.c
create mode 100644 elf/tst-origin-secure.h
create mode 100644 elf/tst-origin-secure.root/postclean.req
diff --git a/elf/Makefile b/elf/Makefile
index 94c5b7e6ed8..93f354d3c18 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -591,6 +591,7 @@ tests-container += \
tst-dlopen-self-container \
tst-dlopen-tlsmodid-container \
tst-ldconfig-cache \
+ tst-origin-secure \
tst-pldd \
tst-preload-pthread-libc \
tst-ptrguard-static-dlopen \
@@ -599,6 +600,7 @@ tests-container += \
# tests-container
test-srcs = \
+ tst-origin-secure-victim \
tst-pathopt \
tst-sprof-basic \
# tests-srcs
@@ -839,6 +841,7 @@ modules-names += \
libtracemod3-1 \
libtracemod4-1 \
libtracemod5-1 \
+ libtst-origin-secure-mod \
ltglobmod1 \
ltglobmod2 \
neededobj1 \
@@ -1034,6 +1037,7 @@ modules-names += \
tst-nodeps2-mod \
tst-non-directory-mod \
tst-null-argv-lib \
+ tst-origin-secure-evilmod \
tst-p_alignmod-base \
tst-p_alignmod3 \
tst-ptrguard-static-dlopen-mod \
@@ -3734,3 +3738,15 @@ $(objpfx)tst-dl-debug-exclude.out: tst-dl-debug-exclude.sh \
$(objpfx)tst-recursive-tls > $@; \
$(evaluate-test)
endif
+
+LDFLAGS-libtst-origin-secure-mod.so += -Wl,-soname,libtst-origin-secure-mod.so
+LDFLAGS-tst-origin-secure-evilmod.so += -Wl,-soname,libtst-origin-secure-mod.so
+$(objpfx)tst-origin-secure-victim: $(objpfx)libtst-origin-secure-mod.so
+# The number of "../" here must match UP_LEVELS in tst-origin-secure.c
+LDFLAGS-tst-origin-secure-victim += \
+ -Wl,--no-as-needed \
+ -Wl,-rpath,\$$ORIGIN/sub/../../../../..$(slibdir)/tst-origin-secure \
+ -Wl,--disable-new-dtags
+$(objpfx)tst-origin-secure.out: $(objpfx)tst-origin-secure-victim \
+ $(objpfx)libtst-origin-secure-mod.so \
+ $(objpfx)tst-origin-secure-evilmod.so
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 95404adae94..f653804c5fb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -91,67 +91,82 @@ static const size_t system_dirs_len[] =
};
#define nsystem_dirs_len array_length (system_dirs_len)
-static bool
-is_trusted_path_normalize (const char *path, size_t len)
+/* Lexically normalize the NUL-terminated PATH in place, collapsing "//",
+ "/./" and "/../" segments (a leading "/../" collapses to "/"). The
+ normalized string is a rearrangement of a prefix of PATH: the write
+ cursor never runs ahead of the read cursor and no trailing character is
+ appended, so this only ever touches bytes within the original
+ strlen (PATH) + 1 storage and can never access memory out of bounds.
+ Returns the length of the normalized path (excluding the terminating
+ NUL). */
+static size_t
+dst_normalize_path (char *path)
{
- if (len == 0)
- return false;
-
- struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
- dl_scratch_buffer_allocate (&scratch, len + 2, 0);
- char *npath = scratch.data;
- char *wnp = npath;
- while (*path != '\0')
+ char *wnp = path;
+ const char *rnp = path;
+ while (*rnp != '\0')
{
- if (path[0] == '/')
+ if (rnp[0] == '/')
{
- if (path[1] == '.')
+ /* Collapse a run of '/' to a single one before interpreting "/."
+ or "/..", so that the "." or ".." is applied to the real
+ preceding component rather than to an empty "//" segment: skip
+ this '/' whenever it is immediately followed by another one. */
+ if (rnp[1] == '/')
{
- if (path[2] == '.' && (path[3] == '/' || path[3] == '\0'))
- {
- while (wnp > npath && *--wnp != '/')
- ;
- path += 3;
- continue;
- }
- else if (path[2] == '/' || path[2] == '\0')
- {
- path += 2;
- continue;
- }
+ ++rnp;
+ continue;
}
- if (wnp > npath && wnp[-1] == '/')
+ if (rnp[1] == '.')
{
- ++path;
- continue;
+ if (rnp[2] == '.' && (rnp[3] == '/' || rnp[3] == '\0'))
+ {
+ while (wnp > path && *--wnp != '/')
+ ;
+ rnp += 3;
+ continue;
+ }
+ else if (rnp[2] == '/' || rnp[2] == '\0')
+ {
+ rnp += 2;
+ continue;
+ }
}
}
- *wnp++ = *path++;
+ *wnp++ = *rnp++;
}
- if (wnp == npath || wnp[-1] != '/')
- *wnp++ = '/';
+ *wnp = '\0';
+ return wnp - path;
+}
- bool result = false;
+/* Return true if the normalized path NPATH of length NLEN is rooted in one
+ of the trusted system directories. The system_dirs entries carry a
+ trailing '/'; NPATH matches an entry when it shares the entry's leading
+ component sequence and then either ends or continues with '/' (e.g.
+ "/lib64" and "/lib64/x" match "/lib64/" but "/lib64x" does not). */
+static bool
+path_is_trusted (const char *npath, size_t nlen)
+{
const char *trun = system_dirs;
for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
{
- if (wnp - npath >= system_dirs_len[idx]
- && memcmp (trun, npath, system_dirs_len[idx]) == 0)
- {
- /* Found it. */
- result = true;
- break;
- }
+ /* Compare against the entry without its trailing '/'. */
+ size_t dirlen = system_dirs_len[idx] - 1;
+
+ if (nlen >= dirlen
+ && memcmp (trun, npath, dirlen) == 0
+ && (npath[dirlen] == '/' || npath[dirlen] == '\0'))
+ /* Found it. */
+ return true;
trun += system_dirs_len[idx] + 1;
}
- dl_scratch_buffer_free (&scratch);
- return result;
+ return false;
}
/* Given a substring starting at INPUT, just after the DST '$' start
@@ -327,6 +342,8 @@ _dl_dst_substitute (struct link_map *l, const char *input, char *result)
}
while (*input != '\0');
+ *wp = '\0';
+
/* In SUID/SGID programs, after $ORIGIN expansion the normalized
path must be rooted in one of the trusted directories. The $LIB
and $PLATFORM DST cannot in any way be manipulated by the caller
@@ -335,15 +352,21 @@ _dl_dst_substitute (struct link_map *l, const char *input, char *result)
checked for trust, the authors of the binaries themselves are
trusted to have designed this correctly. Only $ORIGIN is tested in
this way because it may be manipulated in some ways with hard
- links. */
- if (__glibc_unlikely (check_for_trusted)
- && !is_trusted_path_normalize (result, wp - result))
- {
- *result = '\0';
- return result;
- }
+ links.
- *wp = '\0';
+ Checking the normalized path but opening the raw one is not enough:
+ "a/b/../c" only names "a/c" when "b" is not a symbolic link, so an
+ attacker who controls a component of $ORIGIN (for example by
+ hard-linking the program into an attacker-owned directory) could
+ otherwise redirect the lookup outside the trusted directory. Replace
+ the expansion with its normalized, "../"-free form, so that the path
+ that is opened is exactly the path that was validated. */
+ if (__glibc_unlikely (check_for_trusted))
+ {
+ size_t nlen = dst_normalize_path (result);
+ if (!path_is_trusted (result, nlen))
+ *result = '\0';
+ }
return result;
}
diff --git a/elf/libtst-origin-secure-mod.c b/elf/libtst-origin-secure-mod.c
new file mode 100644
index 00000000000..8d7f3720944
--- /dev/null
+++ b/elf/libtst-origin-secure-mod.c
@@ -0,0 +1,25 @@
+/* Module for tst-origin-secure (the "good" copy).
+ 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 "tst-origin-secure.h"
+
+int
+origin_secure_id (void)
+{
+ return ORIGIN_SECURE_ID_TRUSTED;
+}
diff --git a/elf/tst-origin-secure-evilmod.c b/elf/tst-origin-secure-evilmod.c
new file mode 100644
index 00000000000..0913e32e21b
--- /dev/null
+++ b/elf/tst-origin-secure-evilmod.c
@@ -0,0 +1,28 @@
+/* Module for tst-origin-secure (the attacker-controlled copy).
+ 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 "tst-origin-secure.h"
+
+/* If the victim reports this copy, the loader opened the un-normalized rpath
+ and resolved it through the attacker's symlink -- i.e. the trusted-path
+ check was bypassed (bug 34360). */
+int
+origin_secure_id (void)
+{
+ return ORIGIN_SECURE_ID_ATTACKER;
+}
diff --git a/elf/tst-origin-secure-victim.c b/elf/tst-origin-secure-victim.c
new file mode 100644
index 00000000000..50dafe3ec2e
--- /dev/null
+++ b/elf/tst-origin-secure-victim.c
@@ -0,0 +1,48 @@
+/* Victim program for tst-origin-secure.
+ 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 <stdlib.h>
+
+#include "tst-origin-secure.h"
+
+/* Report both which module was loaded and whether the loader ran the program
+ in secure mode, so the driver can tell a trusted-path bypass apart from a
+ run that was not secure.
+
+ The glibc.rtld.enable_secure=1 tunable does not change getauxval
+ (AT_SECURE), so check whether the loader strips GLIBC_TUNABLES instead.
+
+ The exit status is the combination of the ORIGIN_SECURE_STATUS_* bits:
+
+ _NONE trusted copy, not secure
+ _ATTACKER attacker copy, not secure (the control run)
+ _SECURE trusted copy, secure (a fixed loader)
+ _SECURE | _ATTACKER attacker copy, secure (the bug: the raw
+ rpath was opened) */
+int
+main (void)
+{
+ int status = ORIGIN_SECURE_STATUS_NONE;
+
+ if (origin_secure_id () == ORIGIN_SECURE_ID_ATTACKER)
+ status |= ORIGIN_SECURE_STATUS_ATTACKER;
+ if (getenv ("GLIBC_TUNABLES") == NULL)
+ status |= ORIGIN_SECURE_STATUS_SECURE;
+
+ return status;
+}
diff --git a/elf/tst-origin-secure.c b/elf/tst-origin-secure.c
new file mode 100644
index 00000000000..f4b64e147e4
--- /dev/null
+++ b/elf/tst-origin-secure.c
@@ -0,0 +1,228 @@
+/* Test that AT_SECURE $ORIGIN rpath entries are looked up using the
+ normalized (trusted) path, not the raw expansion (bug 34360).
+
+ 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/>. */
+
+
+/* For a SUID/SGID program the loader only honors $ORIGIN in DT_RPATH when
+ the *normalized* expansion is rooted in a trusted directory. If the loader
+ opens the un-normalized string (that contains "../"), it might disagree
+ as soon as a path component is a symbolic link.
+
+ This test builds the executable with the rpath:
+
+ $ORIGIN/sub/../../../../..SLIBDIR/tst-origin-secure
+
+ and runs it from a private BASE directory two levels deep. Lexically the
+ five "../" pop BASE/sub back to "/", so the entry normalizes to the trusted
+ SLIBDIR/tst-origin-secure. But "sub" is a symlink pointing six levels deep
+ under BASE, so opening the raw string makes the kernel resolve the "../"
+ through the symlink and land in BASE/x1 SLIBDIR/tst-origin-secure instead.
+
+ A trusted copy of the module (ORIGIN_SECURE_ID_TRUSTED) is installed in
+ SLIBDIR/tst-origin-secure; an attacker copy (ORIGIN_SECURE_ID_ATTACKER) is
+ placed at the symlink-diverted location. The trusted subdirectory is
+ rooted under SLIBDIR (so it passes the trusted-path check) but is not
+ itself a default loader search directory.
+
+ The victim reports, in its exit status, both which module it loaded and
+ whether it ran in secure mode.
+
+ Secure mode is forced with glibc.rtld.enable_secure=1 so that no real
+ SUID/SGID binary is required. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+
+#include "tst-origin-secure.h"
+
+#define SONAME "libtst-origin-secure-mod.so"
+/* Subdirectory of the trusted SLIBDIR that the rpath normalizes to. It is
+ trusted (rooted under SLIBDIR) but not a default search directory. */
+#define SUBDIR "tst-origin-secure"
+
+/* Number of "../" in the victim's rpath; it must match the -rpath option in
+ the Makefile. Two invariants:
+
+ - BASE must be at most UP_LEVELS - 1 levels deep, so that the "../" run
+ pops $ORIGIN/sub all the way to "/" and the entry normalizes to
+ SLIBDIR/SUBDIR. BASE is created directly under the container's /tmp,
+ so it is two levels deep.
+
+ - The "sub" symlink must point UP_LEVELS + 1 levels below BASE, so that
+ the kernel lands in BASE/x1 rather than at "/". */
+#define UP_LEVELS 5
+
+/* $ORIGIN of the victim, and the victim itself; set up by do_prepare. */
+static char BASE[] = "/tmp/tst-origin-secureXXXXXX";
+static char *victim_dst;
+
+/* With BASE the temporary directory created here and SLIBDIR "/lib64", the
+ directory contains:
+
+ /lib64/tst-origin-secure/libtst-origin-secure-mod.so trusted copy (id 1)
+ BASE/victim the executable
+ BASE/sub -> BASE/x1/x2/x3/x4/x5/x6 UP_LEVELS + 1 deep
+ BASE/x1/lib64/tst-origin-secure/libtst-origin-secure-mod.so
+ attacker copy (id 2)
+ BASE/x1/x2/x3/x4/x5/x6/ the symlink target
+
+ The victim's rpath is BASE/sub + five "../" + /lib64/tst-origin-secure. */
+static void
+do_prepare (int argc, char **argv)
+{
+ const char *slibdir = support_slibdir_prefix; /* e.g. "/lib64". */
+ const char *objelf = support_objdir_root; /* build root. */
+
+ char *good_src = xasprintf ("%s/elf/libtst-origin-secure-mod.so", objelf);
+ char *evil_src = xasprintf ("%s/elf/tst-origin-secure-evilmod.so", objelf);
+ char *victim_src = xasprintf ("%s/elf/tst-origin-secure-victim", objelf);
+
+ /* We will add a lot of directories and files, using
+ support_create_temp_directory will required to register each on them
+ in the correct order or adding a cleanup routine. Rely on test-container
+ cleanup instead. */
+ TEST_VERIFY_EXIT (mkdtemp (BASE) != NULL);
+
+ char *good_dir = xasprintf ("%s/%s", slibdir, SUBDIR);
+ char *good_dst = xasprintf ("%s/%s", good_dir, SONAME);
+ xmkdirp (good_dir, 0755);
+
+ char *evil_dir = xasprintf ("%s/x1%s/%s", BASE, slibdir, SUBDIR);
+ char *evil_dst = xasprintf ("%s/%s", evil_dir, SONAME);
+ xmkdirp (evil_dir, 0755);
+
+ victim_dst = xasprintf ("%s/victim", BASE);
+ support_copy_file (good_src, good_dst);
+ support_copy_file (evil_src, evil_dst);
+ support_copy_file (victim_src, victim_dst);
+ xchmod (victim_dst, 0755);
+
+ /* The target of the "sub" symlink, UP_LEVELS + 1 levels below BASE. */
+ char *sub_target = xstrdup (BASE);
+ for (int i = 1; i <= UP_LEVELS + 1; ++i)
+ {
+ char *next = xasprintf ("%s/x%d", sub_target, i);
+ free (sub_target);
+ sub_target = next;
+ }
+ xmkdirp (sub_target, 0755);
+
+ char *sub = xasprintf ("%s/sub", BASE);
+ xsymlink (sub_target, sub);
+
+ free (good_src);
+ free (evil_src);
+ free (victim_src);
+ free (good_dir);
+ free (good_dst);
+ free (evil_dir);
+ free (evil_dst);
+ free (sub_target);
+ free (sub);
+}
+#define PREPARE do_prepare
+
+
+static int
+run_victim (const char *victim, char *const *envp)
+{
+ char *const argv[] = { (char *) victim, NULL };
+
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (victim, argv, envp);
+
+ if (proc.err.length != 0)
+ printf ("info: victim stderr: %s\n", proc.err.buffer);
+
+ int status = proc.status;
+ support_capture_subprocess_free (&proc);
+
+ return WIFEXITED (status) ? WEXITSTATUS (status) : -1;
+}
+
+static int
+do_test (void)
+{
+ /* Control run: in normal mode $ORIGIN is honored without the trusted
+ check, so the raw rpath resolves through "sub" and the attacker copy is
+ loaded. GLIBC_TUNABLES is passed here too. */
+ {
+ char *const env[] = { (char *) "GLIBC_TUNABLES=glibc.rtld.enable_secure=0",
+ NULL };
+ int rc = run_victim (victim_dst, env);
+ if (rc != ORIGIN_SECURE_STATUS_ATTACKER)
+ FAIL_EXIT1 ("control run returned status %d, expected %d (attacker "
+ "copy, not secure): the $ORIGIN layout does not reproduce "
+ "the divergence between the raw and the normalized rpath",
+ rc, ORIGIN_SECURE_STATUS_ATTACKER);
+ }
+
+ /* Secure run: force AT_SECURE. A fixed loader normalizes the rpath to the
+ trusted SLIBDIR/SUBDIR and loads the trusted copy; a loader with the bug
+ opens the raw path, resolves "sub", and loads the attacker copy. */
+ {
+ char *const env[] = { (char *) "GLIBC_TUNABLES=glibc.rtld.enable_secure=1",
+ NULL };
+ int rc = run_victim (victim_dst, env);
+ switch (rc)
+ {
+ /* Secure, trusted copy loaded via the normalized rpath: fixed. */
+ case ORIGIN_SECURE_STATUS_SECURE:
+ break;
+
+ /* Secure, attacker copy loaded: the raw rpath was opened. */
+ case ORIGIN_SECURE_STATUS_SECURE | ORIGIN_SECURE_STATUS_ATTACKER:
+ FAIL_EXIT1 ("secure loader resolved the un-normalized rpath "
+ "through the attacker symlink (bug 34360)");
+
+ /* Not secure, attacker copy: exactly what the control run produced, so
+ the tunable did not engage and this run says nothing about the
+ trusted-path handling. */
+ case ORIGIN_SECURE_STATUS_ATTACKER:
+ FAIL_UNSUPPORTED ("glibc.rtld.enable_secure=1 did not enable secure "
+ "mode (victim status %d)", rc);
+
+ /* Not secure, yet the trusted copy was loaded, which is reachable only
+ through the normalized rpath, and only a secure loader normalizes it.
+ Fail rather than report UNSUPPORTED. */
+ case ORIGIN_SECURE_STATUS_NONE:
+ FAIL_EXIT1 ("secure run loaded the trusted copy but reports not being "
+ "secure: the GLIBC_TUNABLES proxy for secure mode in "
+ "tst-origin-secure-victim.c is no longer valid");
+
+ /* Neither copy loaded: since the trusted copy is reachable only through
+ the normalized rpath, this means the rpath entry was not honored at
+ all. */
+ default:
+ FAIL_EXIT1 ("secure run did not load the module via the normalized "
+ "rpath (victim status %d)", rc);
+ }
+ }
+
+ free (victim_dst);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-origin-secure.h b/elf/tst-origin-secure.h
new file mode 100644
index 00000000000..99d7f10df9d
--- /dev/null
+++ b/elf/tst-origin-secure.h
@@ -0,0 +1,41 @@
+/* Definitions shared by the tst-origin-secure test, its victim and modules.
+ 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/>. */
+
+#ifndef _TST_ORIGIN_SECURE_H
+#define _TST_ORIGIN_SECURE_H 1
+
+enum
+ {
+ ORIGIN_SECURE_ID_TRUSTED = 1, /* The copy installed in the trusted
+ SLIBDIR. */
+ ORIGIN_SECURE_ID_ATTACKER = 2, /* The copy reachable only by resolving the
+ "sub" symlink. */
+ };
+
+extern int origin_secure_id (void);
+
+enum
+ {
+ ORIGIN_SECURE_STATUS_NONE = 0,
+ ORIGIN_SECURE_STATUS_ATTACKER = 1 << 0, /* The victim loaded attacker
+ rather than the trusted. */
+ ORIGIN_SECURE_STATUS_SECURE = 1 << 1, /* The loader ran the victim in
+ secure mode. */
+ };
+
+#endif /* tst-origin-secure.h */
diff --git a/elf/tst-origin-secure.root/postclean.req b/elf/tst-origin-secure.root/postclean.req
new file mode 100644
index 00000000000..e69de29bb2d
--
2.43.0
More information about the Libc-alpha
mailing list