Fix and add tests for printf fortification against %n in writeable memory (RHEL-119431)
Resolves: RHEL-119431
This commit is contained in:
parent
b6c93d5187
commit
1239437396
130
glibc-RHEL-119431-1.patch
Normal file
130
glibc-RHEL-119431-1.patch
Normal file
@ -0,0 +1,130 @@
|
||||
commit 127cefd84dc551a43d0ac58b4619503d04869042
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Sep 10 09:22:17 2024 -0300
|
||||
|
||||
Do not use -Wp to disable fortify (BZ 31928)
|
||||
|
||||
The -Wp does not work properly if the compiler is configured to enable
|
||||
fortify by default, since it bypasses the compiler driver (which defines
|
||||
the fortify flags in this case).
|
||||
|
||||
This patch is similar to the one used on Ubuntu [1].
|
||||
|
||||
I checked with a build for x86_64-linux-gnu, i686-linux-gnu,
|
||||
aarch64-linux-gnu, s390x-linux-gnu, and riscv64-linux-gnu with
|
||||
gcc-13 that enables the fortify by default.
|
||||
|
||||
Co-authored-by: Matthias Klose <matthias.klose@canonical.com>
|
||||
|
||||
[1] https://git.launchpad.net/~ubuntu-core-dev/ubuntu/+source/glibc/tree/debian/patches/ubuntu/fix-fortify-source.patch
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
diff --git a/configure b/configure
|
||||
index ec0b62db36..58bfb2917f 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -7525,7 +7525,7 @@ printf "%s\n" "#define HAVE_LIBCAP 1" >>confdefs.h
|
||||
fi
|
||||
|
||||
|
||||
-no_fortify_source="-Wp,-U_FORTIFY_SOURCE"
|
||||
+no_fortify_source="-U_FORTIFY_SOURCE"
|
||||
fortify_source="${no_fortify_source}"
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for maximum supported _FORTIFY_SOURCE level" >&5
|
||||
@@ -7577,7 +7577,7 @@ esac
|
||||
|
||||
if test "$libc_cv_fortify_source" = yes
|
||||
then :
|
||||
- fortify_source="${fortify_source},-D_FORTIFY_SOURCE=${enable_fortify_source}"
|
||||
+ fortify_source="${fortify_source} -D_FORTIFY_SOURCE=${enable_fortify_source}"
|
||||
|
||||
fi
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 7c9b57789e..5af742ce41 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -1537,7 +1537,7 @@ dnl If not, then don't use it.
|
||||
dnl Note that _FORTIFY_SOURCE may have been set through FLAGS too.
|
||||
dnl _FORTIFY_SOURCE value will be selectively disabled for function that can't
|
||||
dnl support it
|
||||
-no_fortify_source="-Wp,-U_FORTIFY_SOURCE"
|
||||
+no_fortify_source="-U_FORTIFY_SOURCE"
|
||||
fortify_source="${no_fortify_source}"
|
||||
|
||||
AC_CACHE_CHECK([for maximum supported _FORTIFY_SOURCE level],
|
||||
@@ -1556,7 +1556,7 @@ AS_CASE([$enable_fortify_source],
|
||||
[libc_cv_fortify_source=no])
|
||||
|
||||
AS_IF([test "$libc_cv_fortify_source" = yes],
|
||||
- [fortify_source="${fortify_source},-D_FORTIFY_SOURCE=${enable_fortify_source}"]
|
||||
+ [fortify_source="${fortify_source} -D_FORTIFY_SOURCE=${enable_fortify_source}"]
|
||||
)
|
||||
|
||||
AC_SUBST(enable_fortify_source)
|
||||
diff --git a/debug/Makefile b/debug/Makefile
|
||||
index 3903cc97a3..89ee80bf4d 100644
|
||||
--- a/debug/Makefile
|
||||
+++ b/debug/Makefile
|
||||
@@ -175,16 +175,16 @@ CFLAGS-recvfrom_chk.c += -fexceptions -fasynchronous-unwind-tables
|
||||
# set up for us, so keep the CFLAGS/CPPFLAGS split logical as the order is:
|
||||
# <user CFLAGS> <test CFLAGS> <user CPPFLAGS> <test CPPFLAGS>
|
||||
CFLAGS-tst-longjmp_chk.c += -fexceptions -fasynchronous-unwind-tables
|
||||
-CPPFLAGS-tst-longjmp_chk.c += $(no-fortify-source),-D_FORTIFY_SOURCE=1
|
||||
+CPPFLAGS-tst-longjmp_chk.c += $(no-fortify-source) -D_FORTIFY_SOURCE=1
|
||||
CFLAGS-tst-longjmp_chk2.c += -fexceptions -fasynchronous-unwind-tables
|
||||
-CPPFLAGS-tst-longjmp_chk2.c += $(no-fortify-source),-D_FORTIFY_SOURCE=1
|
||||
+CPPFLAGS-tst-longjmp_chk2.c += $(no-fortify-source) -D_FORTIFY_SOURCE=1
|
||||
CFLAGS-tst-longjmp_chk3.c += -fexceptions -fasynchronous-unwind-tables
|
||||
-CPPFLAGS-tst-longjmp_chk3.c += $(no-fortify-source),-D_FORTIFY_SOURCE=1
|
||||
-CPPFLAGS-tst-realpath-chk.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
-CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
-CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
-CFLAGS-tst-fortify-syslog.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
-CFLAGS-tst-fortify-wide.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
+CPPFLAGS-tst-longjmp_chk3.c += $(no-fortify-source) -D_FORTIFY_SOURCE=1
|
||||
+CPPFLAGS-tst-realpath-chk.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
# _FORTIFY_SOURCE tests.
|
||||
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
||||
@@ -222,7 +222,8 @@ src-chk-nongnu = \#undef _GNU_SOURCE
|
||||
# cannot be disabled via pragmas, so require -Wno-error to be used.
|
||||
define gen-chk-test
|
||||
tests-$(1)-$(4)-chk += tst-fortify-$(1)-$(2)-$(3)-$(4)
|
||||
-CFLAGS-tst-fortify-$(1)-$(2)-$(3)-$(4).$(1) += $(no-fortify-source),-D_FORTIFY_SOURCE=$(3) -Wno-format \
|
||||
+CFLAGS-tst-fortify-$(1)-$(2)-$(3)-$(4).$(1) += $(no-fortify-source) -D_FORTIFY_SOURCE=$(3) \
|
||||
+ -Wno-format \
|
||||
-Wno-deprecated-declarations \
|
||||
-Wno-error
|
||||
$(eval $(call cflags-$(2),$(1),$(3),$(4)))
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index a1db859684..44165a9c59 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -662,7 +662,7 @@ CFLAGS-tst-gets.c += -Wno-deprecated-declarations
|
||||
|
||||
# BZ #11319 was first fixed for regular vdprintf, then reopened because
|
||||
# the fortified version had the same bug.
|
||||
-CFLAGS-tst-bz11319-fortify2.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-bz11319-fortify2.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
CFLAGS-tst-memstream-string.c += -fno-builtin-fprintf
|
||||
|
||||
diff --git a/wcsmbs/Makefile b/wcsmbs/Makefile
|
||||
index 63adf0e8ef..37a44e62c3 100644
|
||||
--- a/wcsmbs/Makefile
|
||||
+++ b/wcsmbs/Makefile
|
||||
@@ -263,7 +263,7 @@ CFLAGS-wcstod_l.c += $(strtox-CFLAGS) $(config-cflags-wno-ignored-attributes)
|
||||
CFLAGS-wcstold_l.c += $(strtox-CFLAGS) $(config-cflags-wno-ignored-attributes)
|
||||
CFLAGS-wcstof128_l.c += $(strtox-CFLAGS)
|
||||
CFLAGS-wcstof_l.c += $(strtox-CFLAGS) $(config-cflags-wno-ignored-attributes)
|
||||
-CPPFLAGS-tst-wchar-h.c += $(no-fortify-source),-D_FORTIFY_SOURCE=2
|
||||
+CPPFLAGS-tst-wchar-h.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
CFLAGS-wcschr.c += $(config-cflags-wno-ignored-attributes)
|
||||
CFLAGS-wmemchr.c += $(config-cflags-wno-ignored-attributes)
|
||||
696
glibc-RHEL-119431-2.patch
Normal file
696
glibc-RHEL-119431-2.patch
Normal file
@ -0,0 +1,696 @@
|
||||
commit ed6a68bac7cd056abda9008019c71b167f0362dc
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Fri Mar 14 16:09:57 2025 -0300
|
||||
|
||||
debug: Improve '%n' fortify detection (BZ 30932)
|
||||
|
||||
The 7bb8045ec0 path made the '%n' fortify check ignore EMFILE errors
|
||||
while trying to open /proc/self/maps, and this added a security
|
||||
issue where EMFILE can be attacker-controlled thus making it
|
||||
ineffective for some cases.
|
||||
|
||||
The EMFILE failure is reinstated but with a different error
|
||||
message. Also, to improve the false positive of the hardening for
|
||||
the cases where no new files can be opened, the
|
||||
_dl_readonly_area now uses _dl_find_object to check if the
|
||||
memory area is within a writable ELF segment. The procfs method is
|
||||
still used as fallback.
|
||||
|
||||
Checked on x86_64-linux-gnu and i686-linux-gnu.
|
||||
Reviewed-by: Arjun Shankar <arjun@redhat.com>
|
||||
|
||||
diff --git a/Makeconfig b/Makeconfig
|
||||
index aa547a443f..bf50406656 100644
|
||||
--- a/Makeconfig
|
||||
+++ b/Makeconfig
|
||||
@@ -633,7 +633,7 @@ link-libc-printers-tests = $(link-libc-rpath) \
|
||||
$(link-libc-tests-after-rpath-link)
|
||||
|
||||
# This is how to find at build-time things that will be installed there.
|
||||
-rpath-dirs = math elf dlfcn nss nis rt resolv mathvec support
|
||||
+rpath-dirs = math elf dlfcn nss nis rt resolv mathvec support debug
|
||||
rpath-link = \
|
||||
$(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
|
||||
else # build-static
|
||||
diff --git a/debug/Makefile b/debug/Makefile
|
||||
index 905f2bf7e0..2484580cd2 100644
|
||||
--- a/debug/Makefile
|
||||
+++ b/debug/Makefile
|
||||
@@ -74,6 +74,7 @@ routines = \
|
||||
readlink_chk \
|
||||
readlinkat_chk \
|
||||
readonly-area \
|
||||
+ readonly-area-fallback \
|
||||
realpath_chk \
|
||||
recv_chk \
|
||||
recvfrom_chk \
|
||||
@@ -183,9 +184,15 @@ CPPFLAGS-tst-longjmp_chk3.c += $(no-fortify-source) -D_FORTIFY_SOURCE=1
|
||||
CPPFLAGS-tst-realpath-chk.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-sprintf-fortify-rdonly-mod.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-sprintf-fortify-rdonly-dlopen.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
+$(objpfx)tst-sprintf-fortify-rdonly: \
|
||||
+ $(objpfx)tst-sprintf-fortify-rdonly-mod.so \
|
||||
+ $(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
+
|
||||
# _FORTIFY_SOURCE tests.
|
||||
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
||||
# preprocessor conditions based on tst-fortify.c.
|
||||
@@ -305,6 +312,11 @@ tests-container += \
|
||||
tst-fortify-syslog \
|
||||
# tests-container
|
||||
|
||||
+modules-names += \
|
||||
+ tst-sprintf-fortify-rdonly-dlopen \
|
||||
+ tst-sprintf-fortify-rdonly-mod \
|
||||
+ # modules-names
|
||||
+
|
||||
ifeq ($(have-ssp),yes)
|
||||
tests += tst-ssp-1
|
||||
endif
|
||||
diff --git a/debug/readonly-area.c b/debug/readonly-area.c
|
||||
index 04b437ed8d..4311b8214a 100644
|
||||
--- a/debug/readonly-area.c
|
||||
+++ b/debug/readonly-area.c
|
||||
@@ -16,18 +16,19 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdlib.h>
|
||||
+#include <ldsodefs.h>
|
||||
|
||||
-/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
||||
- Return -1 if it is writable. */
|
||||
-
|
||||
-int
|
||||
+enum readonly_error_type
|
||||
__readonly_area (const void *ptr, size_t size)
|
||||
{
|
||||
- /* We cannot determine in general whether memory is writable or not.
|
||||
- This must be handled in a system-dependent manner. to not
|
||||
- unconditionally break code we need to return here a positive
|
||||
- answer. This disables this security measure but that is the
|
||||
- price people have to pay for using systems without a real
|
||||
- implementation of this interface. */
|
||||
- return 1;
|
||||
+ switch (GLRO(dl_readonly_area (ptr, size)))
|
||||
+ {
|
||||
+ case dl_readonly_area_rdonly:
|
||||
+ return readonly_noerror;
|
||||
+ case dl_readonly_area_writable:
|
||||
+ return readonly_area_writable;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ return __readonly_area_fallback (ptr, size);
|
||||
}
|
||||
diff --git a/debug/tst-sprintf-fortify-rdonly-dlopen.c b/debug/tst-sprintf-fortify-rdonly-dlopen.c
|
||||
new file mode 100644
|
||||
index 0000000000..7da3f51f16
|
||||
--- /dev/null
|
||||
+++ b/debug/tst-sprintf-fortify-rdonly-dlopen.c
|
||||
@@ -0,0 +1 @@
|
||||
+#include "tst-sprintf-fortify-rdonly-mod.c"
|
||||
diff --git a/debug/tst-sprintf-fortify-rdonly-mod.c b/debug/tst-sprintf-fortify-rdonly-mod.c
|
||||
new file mode 100644
|
||||
index 0000000000..3655f27b32
|
||||
--- /dev/null
|
||||
+++ b/debug/tst-sprintf-fortify-rdonly-mod.c
|
||||
@@ -0,0 +1,56 @@
|
||||
+/* Testcase for BZ 30932.
|
||||
+ Copyright (C) 2025 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 <stdio.h>
|
||||
+#include <string.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+static const char *str2 = "F";
|
||||
+static char writeable_format[10] = "%s";
|
||||
+static char relro_format[10] __attribute__ ((section (".data.rel.ro"))) =
|
||||
+ "%s%n%s%n";
|
||||
+
|
||||
+void
|
||||
+init_writable (void)
|
||||
+{
|
||||
+ strcpy (writeable_format + 2, "%n%s%n");
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+sprintf_writable (int *n1, int *n2)
|
||||
+{
|
||||
+ char buf[128];
|
||||
+ return sprintf (buf, writeable_format, str2, n1, str2, n2);
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+sprintf_relro (int *n1, int *n2)
|
||||
+{
|
||||
+ char buf[128];
|
||||
+ return sprintf (buf, relro_format, str2, n1, str2, n2);
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+sprintf_writable_malloc (int *n1, int *n2)
|
||||
+{
|
||||
+ char buf[128];
|
||||
+ char *buf2_malloc = strdup (writeable_format);
|
||||
+ if (buf2_malloc == NULL)
|
||||
+ abort ();
|
||||
+ return sprintf (buf, buf2_malloc, str2, n1, str2, n2);
|
||||
+}
|
||||
diff --git a/debug/tst-sprintf-fortify-rdonly.c b/debug/tst-sprintf-fortify-rdonly.c
|
||||
index ba5a791020..fafc8340ea 100644
|
||||
--- a/debug/tst-sprintf-fortify-rdonly.c
|
||||
+++ b/debug/tst-sprintf-fortify-rdonly.c
|
||||
@@ -27,16 +27,64 @@
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/temp_file.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
|
||||
-jmp_buf chk_fail_buf;
|
||||
-bool chk_fail_ok;
|
||||
+static sigjmp_buf chk_fail_buf;
|
||||
+static volatile int ret;
|
||||
+static bool chk_fail_ok;
|
||||
|
||||
-const char *str2 = "F";
|
||||
-char buf2[10] = "%s";
|
||||
+static void
|
||||
+handler (int sig)
|
||||
+{
|
||||
+ if (chk_fail_ok)
|
||||
+ {
|
||||
+ chk_fail_ok = false;
|
||||
+ longjmp (chk_fail_buf, 1);
|
||||
+ }
|
||||
+ else
|
||||
+ _exit (127);
|
||||
+}
|
||||
+
|
||||
+#define FORTIFY_FAIL \
|
||||
+ do { printf ("Failure on line %d\n", __LINE__); ret = 1; } while (0)
|
||||
+#define CHK_FAIL_START \
|
||||
+ chk_fail_ok = true; \
|
||||
+ if (! sigsetjmp (chk_fail_buf, 1)) \
|
||||
+ {
|
||||
+#define CHK_FAIL_END \
|
||||
+ chk_fail_ok = false; \
|
||||
+ FORTIFY_FAIL; \
|
||||
+ }
|
||||
+
|
||||
+static const char *str2 = "F";
|
||||
+static char writeable_format[10] = "%s";
|
||||
+static char relro_format[10] __attribute__ ((section (".data.rel.ro"))) =
|
||||
+ "%s%n%s%n";
|
||||
+
|
||||
+extern void init_writable (void);
|
||||
+extern int sprintf_writable (int *, int *);
|
||||
+extern int sprintf_relro (int *, int *);
|
||||
+extern int sprintf_writable_malloc (int *, int *);
|
||||
+
|
||||
+#define str(__x) # __x
|
||||
+void (*init_writable_dlopen)(void);
|
||||
+int (*sprintf_writable_dlopen)(int *, int *);
|
||||
+int (*sprintf_rdonly_dlopen)(int *, int *);
|
||||
+int (*sprintf_writable_malloc_dlopen)(int *, int *);
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
+ set_fortify_handler (handler);
|
||||
+
|
||||
+ {
|
||||
+ void *h = xdlopen ("tst-sprintf-fortify-rdonly-dlopen.so", RTLD_NOW);
|
||||
+ init_writable_dlopen = xdlsym (h, str(init_writable));
|
||||
+ sprintf_writable_dlopen = xdlsym (h, str(sprintf_writable));
|
||||
+ sprintf_rdonly_dlopen = xdlsym (h, str(sprintf_relro));
|
||||
+ sprintf_writable_malloc_dlopen = xdlsym (h, str(sprintf_writable_malloc));
|
||||
+ }
|
||||
+
|
||||
struct rlimit rl;
|
||||
int max_fd = 24;
|
||||
|
||||
@@ -63,20 +111,94 @@ do_test (void)
|
||||
}
|
||||
TEST_VERIFY_EXIT (nfiles != 0);
|
||||
|
||||
- /* When the format string is writable and contains %n,
|
||||
- with -D_FORTIFY_SOURCE=2 it causes __chk_fail. However, if libc can not
|
||||
- open procfs to check if the input format string in within a writable
|
||||
- memory segment, the fortify version can not perform the check. */
|
||||
- char buf[128];
|
||||
- int n1;
|
||||
- int n2;
|
||||
-
|
||||
- strcpy (buf2 + 2, "%n%s%n");
|
||||
- if (sprintf (buf, buf2, str2, &n1, str2, &n2) != 2
|
||||
- || n1 != 1 || n2 != 2)
|
||||
- FAIL_EXIT1 ("sprintf failed: %s %d %d", buf, n1, n2);
|
||||
-
|
||||
- return 0;
|
||||
+ strcpy (writeable_format + 2, "%n%s%n");
|
||||
+ init_writable ();
|
||||
+ init_writable_dlopen ();
|
||||
+
|
||||
+ /* writeable_format is at a writable part of .bss segment, so libc should be
|
||||
+ able to check it without resorting to procfs. */
|
||||
+ {
|
||||
+ char buf[128];
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf (buf, writeable_format, str2, &n1, str2, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ /* Same as before, but from an library. */
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf_writable (&n1, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf_writable_dlopen (&n1, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ /* relro_format is at a readonly part of .bss segment, so '%n' in format input
|
||||
+ should not trigger a fortify failure. */
|
||||
+ {
|
||||
+ char buf[128];
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ if (sprintf (buf, relro_format, str2, &n1, str2, &n2) != 2
|
||||
+ || n1 != 1 || n2 != 2)
|
||||
+ FAIL_EXIT1 ("sprintf failed: %s %d %d", buf, n1, n2);
|
||||
+ }
|
||||
+
|
||||
+ /* Same as before, but from an library. */
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ if (sprintf_relro (&n1, &n2) != 2 || n1 != 1 || n2 != 2)
|
||||
+ FAIL_EXIT1 ("sprintf failed: %d %d", n1, n2);
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ if (sprintf_rdonly_dlopen (&n1, &n2) != 2 || n1 != 1 || n2 != 2)
|
||||
+ FAIL_EXIT1 ("sprintf failed: %d %d", n1, n2);
|
||||
+ }
|
||||
+
|
||||
+ /* However if the format string is placed on a writable memory not covered
|
||||
+ by ELF segments, libc needs to resort to procfs. */
|
||||
+ {
|
||||
+ char buf[128];
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ char *buf2_malloc = xstrdup (writeable_format);
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf (buf, buf2_malloc, str2, &n1, str2, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ /* Same as before, but from an library. */
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf_writable_malloc (&n1, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
+ int n1;
|
||||
+ int n2;
|
||||
+ CHK_FAIL_START
|
||||
+ sprintf_writable_malloc_dlopen (&n1, &n2);
|
||||
+ CHK_FAIL_END
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 3d60000ec9..5566d39dac 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -73,6 +73,7 @@ dl-routines = \
|
||||
dl-open \
|
||||
dl-origin \
|
||||
dl-printf \
|
||||
+ dl-readonly-area \
|
||||
dl-reloc \
|
||||
dl-runtime \
|
||||
dl-scope \
|
||||
diff --git a/elf/dl-readonly-area.c b/elf/dl-readonly-area.c
|
||||
new file mode 100644
|
||||
index 0000000000..22769ec9d9
|
||||
--- /dev/null
|
||||
+++ b/elf/dl-readonly-area.c
|
||||
@@ -0,0 +1,86 @@
|
||||
+/* Check if range is within a read-only from a loaded ELF object.
|
||||
+ Copyright (C) 2025 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 <ldsodefs.h>
|
||||
+
|
||||
+static bool
|
||||
+check_relro (const struct link_map *l, uintptr_t start, uintptr_t end)
|
||||
+{
|
||||
+ if (l->l_relro_addr != 0)
|
||||
+ {
|
||||
+ uintptr_t relro_start = ALIGN_DOWN (l->l_addr + l->l_relro_addr,
|
||||
+ GLRO(dl_pagesize));
|
||||
+ uintptr_t relro_end = ALIGN_DOWN (l->l_addr + l->l_relro_addr
|
||||
+ + l->l_relro_size,
|
||||
+ GLRO(dl_pagesize));
|
||||
+ /* RELRO is caved out from a RW segment, so the next range is either
|
||||
+ RW or nonexistent. */
|
||||
+ return relro_start <= start && end <= relro_end
|
||||
+ ? dl_readonly_area_rdonly : dl_readonly_area_writable;
|
||||
+
|
||||
+ }
|
||||
+ return dl_readonly_area_writable;
|
||||
+}
|
||||
+
|
||||
+enum dl_readonly_area_error_type
|
||||
+_dl_readonly_area (const void *ptr, size_t size)
|
||||
+{
|
||||
+ struct dl_find_object dlfo;
|
||||
+ if (_dl_find_object ((void *)ptr, &dlfo) != 0)
|
||||
+ return dl_readonly_area_not_found;
|
||||
+
|
||||
+ const struct link_map *l = dlfo.dlfo_link_map;
|
||||
+ uintptr_t ptr_start = (uintptr_t) ptr;
|
||||
+ uintptr_t ptr_end = ptr_start + size;
|
||||
+
|
||||
+ for (const ElfW(Phdr) *ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
|
||||
+ if (ph->p_type == PT_LOAD)
|
||||
+ {
|
||||
+ /* For segments with alignment larger than the page size,
|
||||
+ _dl_map_segment allocates additional space that is mark as
|
||||
+ PROT_NONE (so we can ignore). */
|
||||
+ uintptr_t from = l->l_addr
|
||||
+ + ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
|
||||
+ uintptr_t to = l->l_addr
|
||||
+ + ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize));
|
||||
+
|
||||
+ /* Found an entry that at least partially covers the area. */
|
||||
+ if (from < ptr_end && to > ptr_start)
|
||||
+ {
|
||||
+ if (ph->p_flags & PF_W)
|
||||
+ return check_relro (l, ptr_start, ptr_end);
|
||||
+
|
||||
+ if ((ph->p_flags & PF_R) == 0)
|
||||
+ return dl_readonly_area_writable;
|
||||
+
|
||||
+ if (from <= ptr_start && to >= ptr_end)
|
||||
+ return dl_readonly_area_rdonly;
|
||||
+ else if (from <= ptr_start)
|
||||
+ size -= to - ptr_start;
|
||||
+ else if (to >= ptr_end)
|
||||
+ size -= ptr_end - from;
|
||||
+ else
|
||||
+ size -= to - from;
|
||||
+
|
||||
+ if (size == 0)
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return size == 0 ? dl_readonly_area_rdonly : dl_readonly_area_not_found;
|
||||
+}
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 00b25c1a73..099c447e80 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -372,6 +372,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
|
||||
._dl_error_free = _dl_error_free,
|
||||
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
|
||||
._dl_libc_freeres = __rtld_libc_freeres,
|
||||
+ ._dl_readonly_area = _dl_readonly_area,
|
||||
};
|
||||
/* If we would use strong_alias here the compiler would see a
|
||||
non-hidden definition. This would undo the effect of the previous
|
||||
diff --git a/include/stdlib.h b/include/stdlib.h
|
||||
index 57f4ab8545..b7147ba590 100644
|
||||
--- a/include/stdlib.h
|
||||
+++ b/include/stdlib.h
|
||||
@@ -361,6 +361,21 @@ struct abort_msg_s
|
||||
extern struct abort_msg_s *__abort_msg;
|
||||
libc_hidden_proto (__abort_msg)
|
||||
|
||||
+enum readonly_error_type
|
||||
+{
|
||||
+ readonly_noerror,
|
||||
+ readonly_area_writable,
|
||||
+ readonly_procfs_inaccessible,
|
||||
+ readonly_procfs_open_fail,
|
||||
+};
|
||||
+
|
||||
+extern enum readonly_error_type __readonly_area (const void *ptr,
|
||||
+ size_t size)
|
||||
+ attribute_hidden;
|
||||
+extern enum readonly_error_type __readonly_area_fallback (const void *ptr,
|
||||
+ size_t size)
|
||||
+ attribute_hidden;
|
||||
+
|
||||
# if IS_IN (rtld)
|
||||
extern __typeof (unsetenv) unsetenv attribute_hidden;
|
||||
extern __typeof (__strtoul_internal) __strtoul_internal attribute_hidden;
|
||||
diff --git a/stdio-common/vfprintf-internal.c b/stdio-common/vfprintf-internal.c
|
||||
index aa9708bff5..fa41e1b242 100644
|
||||
--- a/stdio-common/vfprintf-internal.c
|
||||
+++ b/stdio-common/vfprintf-internal.c
|
||||
@@ -576,7 +576,8 @@ static const uint8_t jump_table[] =
|
||||
|
||||
/* Handle positional format specifiers. */
|
||||
static void printf_positional (struct Xprintf_buffer *buf,
|
||||
- const CHAR_T *format, int readonly_format,
|
||||
+ const CHAR_T *format,
|
||||
+ enum readonly_error_type readonly_format,
|
||||
va_list ap, va_list *ap_savep,
|
||||
int nspecs_done, const UCHAR_T *lead_str_end,
|
||||
CHAR_T *work_buffer, int save_errno,
|
||||
@@ -626,9 +627,7 @@ Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
|
||||
/* For the %m format we may need the current `errno' value. */
|
||||
int save_errno = errno;
|
||||
|
||||
- /* 1 if format is in read-only memory, -1 if it is in writable memory,
|
||||
- 0 if unknown. */
|
||||
- int readonly_format = 0;
|
||||
+ enum readonly_error_type readonly_format = readonly_noerror;
|
||||
|
||||
/* Initialize local variables. */
|
||||
grouping = (const char *) -1;
|
||||
@@ -1045,7 +1044,7 @@ do_positional:
|
||||
|
||||
static void
|
||||
printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
|
||||
- int readonly_format,
|
||||
+ enum readonly_error_type readonly_format,
|
||||
va_list ap, va_list *ap_savep, int nspecs_done,
|
||||
const UCHAR_T *lead_str_end,
|
||||
CHAR_T *work_buffer, int save_errno,
|
||||
diff --git a/stdio-common/vfprintf-process-arg.c b/stdio-common/vfprintf-process-arg.c
|
||||
index 8d20493766..90b5e61ceb 100644
|
||||
--- a/stdio-common/vfprintf-process-arg.c
|
||||
+++ b/stdio-common/vfprintf-process-arg.c
|
||||
@@ -324,16 +324,24 @@ LABEL (form_pointer):
|
||||
LABEL (form_number):
|
||||
if ((mode_flags & PRINTF_FORTIFY) != 0)
|
||||
{
|
||||
- if (! readonly_format)
|
||||
- {
|
||||
- extern int __readonly_area (const void *, size_t)
|
||||
- attribute_hidden;
|
||||
- readonly_format
|
||||
- = __readonly_area (format, ((STR_LEN (format) + 1)
|
||||
- * sizeof (CHAR_T)));
|
||||
- }
|
||||
- if (readonly_format < 0)
|
||||
- __libc_fatal ("*** %n in writable segment detected ***\n");
|
||||
+ if (readonly_format == readonly_noerror)
|
||||
+ readonly_format = __readonly_area (format, ((STR_LEN (format) + 1)
|
||||
+ * sizeof (CHAR_T)));
|
||||
+ switch (readonly_format)
|
||||
+ {
|
||||
+ case readonly_area_writable:
|
||||
+ __libc_fatal ("*** %n in writable segments detected ***\n");
|
||||
+ /* The format is not within ELF segments and opening /proc/self/maps
|
||||
+ failed because there are too many files. */
|
||||
+ case readonly_procfs_open_fail:
|
||||
+ __libc_fatal ("*** procfs could not open ***\n");
|
||||
+ /* The /proc/self/maps can not be opened either because it is not
|
||||
+ available or the process does not have the right permission. Since
|
||||
+ it should not be attacker-controlled we can avoid failure. */
|
||||
+ case readonly_procfs_inaccessible:
|
||||
+ case readonly_noerror:
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
/* Answer the count of characters written. */
|
||||
void *ptrptr = process_arg_pointer ();
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 19494b82ee..5b12a41a18 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -264,6 +264,12 @@ struct audit_ifaces
|
||||
struct audit_ifaces *next;
|
||||
};
|
||||
|
||||
+enum dl_readonly_area_error_type
|
||||
+{
|
||||
+ dl_readonly_area_rdonly,
|
||||
+ dl_readonly_area_writable,
|
||||
+ dl_readonly_area_not_found,
|
||||
+};
|
||||
|
||||
/* Test whether given NAME matches any of the names of the given object. */
|
||||
extern int _dl_name_match_p (const char *__name, const struct link_map *__map)
|
||||
@@ -685,6 +691,10 @@ struct rtld_global_ro
|
||||
dlopen. */
|
||||
int (*_dl_find_object) (void *, struct dl_find_object *);
|
||||
|
||||
+ /* Implementation of _dl_readonly_area, used in fortify routines to check
|
||||
+ if memory area is within a read-only ELF segment. */
|
||||
+ enum dl_readonly_area_error_type (*_dl_readonly_area) (const void *, size_t);
|
||||
+
|
||||
/* Dynamic linker operations used after static dlopen. */
|
||||
const struct dlfcn_hook *_dl_dlfcn_hook;
|
||||
|
||||
@@ -1297,6 +1307,10 @@ extern void _dl_show_scope (struct link_map *new, int from)
|
||||
extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
|
||||
rtld_hidden_proto (_dl_find_dso_for_object)
|
||||
|
||||
+extern enum dl_readonly_area_error_type _dl_readonly_area (const void *ptr,
|
||||
+ size_t size)
|
||||
+ attribute_hidden;
|
||||
+
|
||||
/* Initialization which is normally done by the dynamic linker. */
|
||||
extern void _dl_non_dynamic_init (void)
|
||||
attribute_hidden;
|
||||
diff --git a/sysdeps/mach/readonly-area.c b/sysdeps/mach/readonly-area-fallback.c
|
||||
similarity index 90%
|
||||
rename from sysdeps/mach/readonly-area.c
|
||||
rename to sysdeps/mach/readonly-area-fallback.c
|
||||
index fb89413fe6..3fb873298b 100644
|
||||
--- a/sysdeps/mach/readonly-area.c
|
||||
+++ b/sysdeps/mach/readonly-area-fallback.c
|
||||
@@ -20,11 +20,8 @@
|
||||
#include <stdint.h>
|
||||
#include <mach.h>
|
||||
|
||||
-/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
||||
- Return -1 if it is writable. */
|
||||
-
|
||||
-int
|
||||
-__readonly_area (const char *ptr, size_t size)
|
||||
+enum readonly_error_type
|
||||
+__readonly_area_fallback (const void *ptr, size_t size)
|
||||
{
|
||||
vm_address_t region_address = (uintptr_t) ptr;
|
||||
vm_size_t region_length = size;
|
||||
@@ -46,11 +43,11 @@ __readonly_area (const char *ptr, size_t size)
|
||||
continue;
|
||||
|
||||
if (protection & VM_PROT_WRITE)
|
||||
- return -1;
|
||||
+ return readonly_area_writable;
|
||||
|
||||
if (region_address - (uintptr_t) ptr >= size)
|
||||
break;
|
||||
}
|
||||
|
||||
- return 1;
|
||||
+ return readonly_noerror;
|
||||
}
|
||||
diff --git a/sysdeps/unix/sysv/linux/readonly-area.c b/sysdeps/unix/sysv/linux/readonly-area-fallback.c
|
||||
similarity index 84%
|
||||
rename from sysdeps/unix/sysv/linux/readonly-area.c
|
||||
rename to sysdeps/unix/sysv/linux/readonly-area-fallback.c
|
||||
index 62d2070c72..c93ad2a598 100644
|
||||
--- a/sysdeps/unix/sysv/linux/readonly-area.c
|
||||
+++ b/sysdeps/unix/sysv/linux/readonly-area-fallback.c
|
||||
@@ -23,11 +23,8 @@
|
||||
#include <string.h>
|
||||
#include "libio/libioP.h"
|
||||
|
||||
-/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
|
||||
- Return -1 if it is writable. */
|
||||
-
|
||||
-int
|
||||
-__readonly_area (const char *ptr, size_t size)
|
||||
+enum readonly_error_type
|
||||
+__readonly_area_fallback (const void *ptr, size_t size)
|
||||
{
|
||||
const void *ptr_end = ptr + size;
|
||||
|
||||
@@ -42,11 +39,11 @@ __readonly_area (const char *ptr, size_t size)
|
||||
to the /proc filesystem if it is set[ug]id. There has
|
||||
been no willingness to change this in the kernel so
|
||||
far. */
|
||||
- || errno == EACCES
|
||||
- /* Process has reached the maximum number of open files. */
|
||||
- || errno == EMFILE)
|
||||
- return 1;
|
||||
- return -1;
|
||||
+ || errno == EACCES)
|
||||
+ return readonly_procfs_inaccessible;
|
||||
+ /* Process has reached the maximum number of open files or another
|
||||
+ unusual error. */
|
||||
+ return readonly_procfs_open_fail;
|
||||
}
|
||||
|
||||
/* We need no locking. */
|
||||
@@ -98,7 +95,5 @@ __readonly_area (const char *ptr, size_t size)
|
||||
fclose (fp);
|
||||
free (line);
|
||||
|
||||
- /* If the whole area between ptr and ptr_end is covered by read-only
|
||||
- VMAs, return 1. Otherwise return -1. */
|
||||
- return size == 0 ? 1 : -1;
|
||||
+ return size == 0 ? readonly_noerror : readonly_area_writable;
|
||||
}
|
||||
28
glibc-RHEL-119431-3.patch
Normal file
28
glibc-RHEL-119431-3.patch
Normal file
@ -0,0 +1,28 @@
|
||||
commit 3a9c1497e320d77b2453ee88d7d6b40e162e85d4
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Jul 18 19:58:56 2025 +0200
|
||||
|
||||
debug: Do not link tst-sprintf-fortify-rdonly against the dlopen module
|
||||
|
||||
The test intends to load tst-sprintf-fortify-rdonly-dlopen.so via
|
||||
dlopen, and directly linking against it prevents that.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/debug/Makefile b/debug/Makefile
|
||||
index f29bd20b97..13f638e344 100644
|
||||
--- a/debug/Makefile
|
||||
+++ b/debug/Makefile
|
||||
@@ -189,9 +189,9 @@ CFLAGS-tst-sprintf-fortify-rdonly-dlopen.c += $(no-fortify-source) -D_FORTIFY_SO
|
||||
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
-$(objpfx)tst-sprintf-fortify-rdonly: \
|
||||
- $(objpfx)tst-sprintf-fortify-rdonly-mod.so \
|
||||
- $(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
+$(objpfx)tst-sprintf-fortify-rdonly: $(objpfx)tst-sprintf-fortify-rdonly-mod.so
|
||||
+$(objpfx)tst-sprintf-fortify-rdonly.out: \
|
||||
+ $(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
|
||||
# _FORTIFY_SOURCE tests.
|
||||
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
||||
93
glibc-RHEL-119431-4.patch
Normal file
93
glibc-RHEL-119431-4.patch
Normal file
@ -0,0 +1,93 @@
|
||||
commit 01196393c257c59f63e0e14fa1bfe8d2a699bf2d
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Jul 18 19:58:59 2025 +0200
|
||||
|
||||
elf: Initialize GLRO (dl_read_only_area) after static dlopen (bug 33139)
|
||||
|
||||
The _dl_read_only_area function in the uninitialized ld.so after
|
||||
static dlopen is not able to find anything. Instead, we need to
|
||||
redirect to the code from the statically linked main program.
|
||||
|
||||
Fixes commit d60fffe28a46b2a41fc308c1804ff02375d27408 ("debug:
|
||||
Improve '%n' fortify detection (BZ 30932)").
|
||||
|
||||
Reviewed-by: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/debug/Makefile b/debug/Makefile
|
||||
index 13f638e344..919bf91c42 100644
|
||||
--- a/debug/Makefile
|
||||
+++ b/debug/Makefile
|
||||
@@ -186,12 +186,19 @@ CPPFLAGS-tst-chk-cancel.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-sprintf-fortify-rdonly.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-sprintf-fortify-rdonly-mod.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-sprintf-fortify-rdonly-dlopen.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
+CFLAGS-tst-sprintf-fortify-rdonly-static.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
$(objpfx)tst-sprintf-fortify-rdonly: $(objpfx)tst-sprintf-fortify-rdonly-mod.so
|
||||
$(objpfx)tst-sprintf-fortify-rdonly.out: \
|
||||
$(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
+$(objpfx)tst-sprintf-fortify-rdonly-static: \
|
||||
+ $(objpfx)tst-sprintf-fortify-rdonly-dlopen.o
|
||||
+$(objpfx)tst-sprintf-fortify-rdonly-static.out: \
|
||||
+ $(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
+tst-sprintf-fortify-rdonly-static-ENV = \
|
||||
+ LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)/elf
|
||||
|
||||
# _FORTIFY_SOURCE tests.
|
||||
# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
|
||||
@@ -302,9 +309,14 @@ tests = \
|
||||
tst-longjmp_chk3 \
|
||||
tst-realpath-chk \
|
||||
tst-sprintf-fortify-rdonly \
|
||||
+ tst-sprintf-fortify-rdonly-static \
|
||||
tst-sprintf-fortify-unchecked \
|
||||
# tests
|
||||
|
||||
+tests-static = \
|
||||
+ tst-sprintf-fortify-rdonly-static \
|
||||
+ # tests-static
|
||||
+
|
||||
tests-time64 += \
|
||||
$(tests-all-time64-chk) \
|
||||
# tests-time64
|
||||
diff --git a/debug/tst-sprintf-fortify-rdonly-static.c b/debug/tst-sprintf-fortify-rdonly-static.c
|
||||
new file mode 100644
|
||||
index 0000000000..ca5bbd5aff
|
||||
--- /dev/null
|
||||
+++ b/debug/tst-sprintf-fortify-rdonly-static.c
|
||||
@@ -0,0 +1 @@
|
||||
+#include "tst-sprintf-fortify-rdonly.c"
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index f889f01b4a..493f9696ea 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -372,7 +372,6 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
|
||||
._dl_error_free = _dl_error_free,
|
||||
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
|
||||
._dl_libc_freeres = __rtld_libc_freeres,
|
||||
- ._dl_readonly_area = _dl_readonly_area,
|
||||
};
|
||||
/* If we would use strong_alias here the compiler would see a
|
||||
non-hidden definition. This would undo the effect of the previous
|
||||
@@ -457,6 +456,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info)
|
||||
/* Do not use an initializer for these members because it would
|
||||
interfere with __rtld_static_init. */
|
||||
GLRO (dl_find_object) = &_dl_find_object;
|
||||
+ GLRO (dl_readonly_area) = &_dl_readonly_area;
|
||||
|
||||
/* If it hasn't happen yet record the startup time. */
|
||||
rtld_timer_start (&start_time);
|
||||
diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c
|
||||
index 642379076b..9c56180f88 100644
|
||||
--- a/elf/rtld_static_init.c
|
||||
+++ b/elf/rtld_static_init.c
|
||||
@@ -79,6 +79,7 @@ __rtld_static_init (struct link_map *map)
|
||||
attribute_hidden;
|
||||
dl->_dl_tls_static_size = _dl_tls_static_size;
|
||||
dl->_dl_find_object = _dl_find_object;
|
||||
+ dl->_dl_readonly_area = _dl_readonly_area;
|
||||
|
||||
__rtld_static_init_arch (map, dl);
|
||||
}
|
||||
93
glibc-RHEL-119431-5.patch
Normal file
93
glibc-RHEL-119431-5.patch
Normal file
@ -0,0 +1,93 @@
|
||||
Downstream-only patch to restore internal GLIBC_PRIVATE ABI change due
|
||||
to glibc-RHEL-119431-2.patch. Adding an entry to rtld_global_ro struct
|
||||
changes offsets. This patch corrects that.
|
||||
|
||||
RHEL does not support fortify inside shared objects loaded from
|
||||
statically linked programs, so that test is xfail'd.
|
||||
|
||||
diff --git a/debug/readonly-area.c b/debug/readonly-area.c
|
||||
index 0648121846cdd5d1..86e3e9b9c3b03958 100644
|
||||
--- a/debug/readonly-area.c
|
||||
+++ b/debug/readonly-area.c
|
||||
@@ -21,7 +21,7 @@
|
||||
enum readonly_error_type
|
||||
__readonly_area (const void *ptr, size_t size)
|
||||
{
|
||||
- switch (GLRO(dl_readonly_area (ptr, size)))
|
||||
+ switch (_dl_readonly_area (ptr, size))
|
||||
{
|
||||
case dl_readonly_area_rdonly:
|
||||
return readonly_noerror;
|
||||
diff --git a/elf/Versions b/elf/Versions
|
||||
index 4b8b155d8e2bef70..17834c7d1c371e1d 100644
|
||||
--- a/elf/Versions
|
||||
+++ b/elf/Versions
|
||||
@@ -79,5 +70,8 @@ ld {
|
||||
# Set value of a tunable.
|
||||
__tunable_is_initialized;
|
||||
__tunable_get_val;
|
||||
+
|
||||
+ # Check if an address range within a loaded ELF object is read-only.
|
||||
+ _dl_readonly_area;
|
||||
}
|
||||
}
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index d421750734e113e6..d02ecc834c9a4d43 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -456,7 +456,6 @@ _dl_start_final (void *arg, struct dl_st
|
||||
/* Do not use an initializer for these members because it would
|
||||
interfere with __rtld_static_init. */
|
||||
GLRO (dl_find_object) = &_dl_find_object;
|
||||
- GLRO (dl_readonly_area) = &_dl_readonly_area;
|
||||
|
||||
/* If it hasn't happen yet record the startup time. */
|
||||
rtld_timer_start (&start_time);
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 15c21491a291d1ae..537d1293c7b5543b 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -691,10 +691,6 @@ struct rtld_global_ro
|
||||
dlopen. */
|
||||
int (*_dl_find_object) (void *, struct dl_find_object *);
|
||||
|
||||
- /* Implementation of _dl_readonly_area, used in fortify routines to check
|
||||
- if memory area is within a read-only ELF segment. */
|
||||
- enum dl_readonly_area_error_type (*_dl_readonly_area) (const void *, size_t);
|
||||
-
|
||||
/* Dynamic linker operations used after static dlopen. */
|
||||
const struct dlfcn_hook *_dl_dlfcn_hook;
|
||||
|
||||
@@ -1308,8 +1304,7 @@ extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
|
||||
rtld_hidden_proto (_dl_find_dso_for_object)
|
||||
|
||||
extern enum dl_readonly_area_error_type _dl_readonly_area (const void *ptr,
|
||||
- size_t size)
|
||||
- attribute_hidden;
|
||||
+ size_t size);
|
||||
|
||||
/* Initialization which is normally done by the dynamic linker. */
|
||||
extern void _dl_non_dynamic_init (void)
|
||||
diff -rup a/debug/Makefile b/debug/Makefile
|
||||
--- a/debug/Makefile 2025-11-13 15:43:52.493437313 -0500
|
||||
+++ b/debug/Makefile 2025-11-18 14:18:05.311656186 -0500
|
||||
@@ -190,6 +190,8 @@ CFLAGS-tst-sprintf-fortify-rdonly-static
|
||||
CFLAGS-tst-fortify-syslog.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
CFLAGS-tst-fortify-wide.c += $(no-fortify-source) -D_FORTIFY_SOURCE=2
|
||||
|
||||
+test-xfail-tst-sprintf-fortify-rdonly-static = yes
|
||||
+
|
||||
$(objpfx)tst-sprintf-fortify-rdonly: $(objpfx)tst-sprintf-fortify-rdonly-mod.so
|
||||
$(objpfx)tst-sprintf-fortify-rdonly.out: \
|
||||
$(objpfx)tst-sprintf-fortify-rdonly-dlopen.so
|
||||
diff -rup a/elf/rtld_static_init.c b/elf/rtld_static_init.c
|
||||
--- a/elf/rtld_static_init.c 2025-11-18 15:48:15.355621059 -0500
|
||||
+++ b/elf/rtld_static_init.c 2025-11-18 17:35:21.427465597 -0500
|
||||
@@ -79,7 +79,6 @@ __rtld_static_init (struct link_map *map
|
||||
attribute_hidden;
|
||||
dl->_dl_tls_static_size = _dl_tls_static_size;
|
||||
dl->_dl_find_object = _dl_find_object;
|
||||
- dl->_dl_readonly_area = _dl_readonly_area;
|
||||
|
||||
__rtld_static_init_arch (map, dl);
|
||||
}
|
||||
@ -4,3 +4,8 @@
|
||||
type_kind = struct
|
||||
name = _IO_FILE
|
||||
accessed_through = pointer
|
||||
[suppress_function]
|
||||
# From glibc-2.39-98.
|
||||
name = _dl_readonly_area
|
||||
parameter = '0 void *
|
||||
parameter = '1 size_t
|
||||
|
||||
Loading…
Reference in New Issue
Block a user