diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/generic/mremap-failure.h | 25 | ||||
-rw-r--r-- | sysdeps/nptl/dl-tls_init_tp.c | 12 | ||||
-rw-r--r-- | sysdeps/pthread/tst-setuid3.c | 37 | ||||
-rw-r--r-- | sysdeps/s390/wcsncmp-vx.S | 10 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/Makefile | 2 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/bits/uio-ext.h | 1 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/dl-rseq-symbols.S | 64 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/mremap-failure.h | 30 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/mremap.c | 14 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/rseq-internal.h | 23 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-linux-mremap1.c | 63 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-rseq.c | 10 |
12 files changed, 247 insertions, 44 deletions
diff --git a/sysdeps/generic/mremap-failure.h b/sysdeps/generic/mremap-failure.h new file mode 100644 index 0000000000..bc0d476368 --- /dev/null +++ b/sysdeps/generic/mremap-failure.h @@ -0,0 +1,25 @@ +/* mremap failure handling. Generic version. + Copyright (C) 2024 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/>. */ + +/* Return exit value on mremap failure with errno ERR. */ + +static int +mremap_failure_exit (int err) +{ + return EXIT_FAILURE; +} diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c index 53fba774a5..662bc0158d 100644 --- a/sysdeps/nptl/dl-tls_init_tp.c +++ b/sysdeps/nptl/dl-tls_init_tp.c @@ -45,8 +45,6 @@ rtld_mutex_dummy (pthread_mutex_t *lock) #endif const unsigned int __rseq_flags; -const unsigned int __rseq_size attribute_relro; -const ptrdiff_t __rseq_offset attribute_relro; void __tls_pre_init_tp (void) @@ -106,12 +104,7 @@ __tls_init_tp (void) do_rseq = TUNABLE_GET (rseq, int, NULL); #endif if (rseq_register_current_thread (pd, do_rseq)) - { - /* We need a writable view of the variables. They are in - .data.relro and are not yet write-protected. */ - extern unsigned int size __asm__ ("__rseq_size"); - size = sizeof (pd->rseq_area); - } + _rseq_size = RSEQ_AREA_SIZE_INITIAL_USED; #ifdef RSEQ_SIG /* This should be a compile-time constant, but the current @@ -119,8 +112,7 @@ __tls_init_tp (void) all targets support __thread_pointer, so set __rseq_offset only if thre rseq registration may have happened because RSEQ_SIG is defined. */ - extern ptrdiff_t offset __asm__ ("__rseq_offset"); - offset = (char *) &pd->rseq_area - (char *) __thread_pointer (); + _rseq_offset = (char *) &pd->rseq_area - (char *) __thread_pointer (); #endif } diff --git a/sysdeps/pthread/tst-setuid3.c b/sysdeps/pthread/tst-setuid3.c index 7dd360e325..4a77791253 100644 --- a/sysdeps/pthread/tst-setuid3.c +++ b/sysdeps/pthread/tst-setuid3.c @@ -15,24 +15,19 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ -#include <stdio.h> #include <errno.h> #include <pthread.h> #include <stdbool.h> #include <unistd.h> +#include <support/check.h> + /* The test must run under a non-privileged user ID. */ static const uid_t test_uid = 1; static pthread_barrier_t barrier1; static pthread_barrier_t barrier2; -#define FAIL(fmt, ...) \ - do { printf ("FAIL: " fmt "\n", __VA_ARGS__); _exit (1); } while (0) - -#define FAIL_ERR(fmt, ...) \ - do { printf ("FAIL: " fmt ": %m\n", __VA_ARGS__); _exit (1); } while (0) - /* True if x is not a successful return code from pthread_barrier_wait. */ static inline bool is_invalid_barrier_ret (int x) @@ -45,10 +40,10 @@ thread_func (void *ctx __attribute__ ((unused))) { int ret = pthread_barrier_wait (&barrier1); if (is_invalid_barrier_ret (ret)) - FAIL ("pthread_barrier_wait (barrier1) (on thread): %d", ret); + FAIL_EXIT1 ("pthread_barrier_wait (barrier1) (on thread): %d", ret); ret = pthread_barrier_wait (&barrier2); if (is_invalid_barrier_ret (ret)) - FAIL ("pthread_barrier_wait (barrier2) (on thread): %d", ret); + FAIL_EXIT1 ("pthread_barrier_wait (barrier2) (on thread): %d", ret); return NULL; } @@ -59,13 +54,13 @@ setuid_failure (int phase) switch (ret) { case 0: - FAIL ("setuid succeeded unexpectedly in phase %d", phase); + FAIL_EXIT1 ("setuid succeeded unexpectedly in phase %d", phase); case -1: if (errno != EPERM) - FAIL_ERR ("setuid phase %d", phase); + FAIL_EXIT1 ("setuid phase %d: %m", phase); break; default: - FAIL ("invalid setuid return value in phase %d: %d", phase, ret); + FAIL_EXIT1 ("invalid setuid return value in phase %d: %d", phase, ret); } } @@ -74,42 +69,42 @@ do_test (void) { if (getuid () == 0) if (setuid (test_uid) != 0) - FAIL_ERR ("setuid (%u)", (unsigned) test_uid); + FAIL_EXIT1 ("setuid (%u): %m", (unsigned) test_uid); if (setuid (getuid ())) - FAIL_ERR ("setuid (%s)", "getuid ()"); + FAIL_EXIT1 ("setuid (%s): %m", "getuid ()"); setuid_failure (1); int ret = pthread_barrier_init (&barrier1, NULL, 2); if (ret != 0) - FAIL ("pthread_barrier_init (barrier1): %d", ret); + FAIL_EXIT1 ("pthread_barrier_init (barrier1): %d", ret); ret = pthread_barrier_init (&barrier2, NULL, 2); if (ret != 0) - FAIL ("pthread_barrier_init (barrier2): %d", ret); + FAIL_EXIT1 ("pthread_barrier_init (barrier2): %d", ret); pthread_t thread; ret = pthread_create (&thread, NULL, thread_func, NULL); if (ret != 0) - FAIL ("pthread_create: %d", ret); + FAIL_EXIT1 ("pthread_create: %d", ret); /* Ensure that the thread is running properly. */ ret = pthread_barrier_wait (&barrier1); if (is_invalid_barrier_ret (ret)) - FAIL ("pthread_barrier_wait (barrier1): %d", ret); + FAIL_EXIT1 ("pthread_barrier_wait (barrier1): %d", ret); setuid_failure (2); /* Check success case. */ if (setuid (getuid ()) != 0) - FAIL_ERR ("setuid (%s)", "getuid ()"); + FAIL_EXIT1 ("setuid (%s): %m", "getuid ()"); /* Shutdown. */ ret = pthread_barrier_wait (&barrier2); if (is_invalid_barrier_ret (ret)) - FAIL ("pthread_barrier_wait (barrier2): %d", ret); + FAIL_EXIT1 ("pthread_barrier_wait (barrier2): %d", ret); ret = pthread_join (thread, NULL); if (ret != 0) - FAIL ("pthread_join: %d", ret); + FAIL_EXIT1 ("pthread_join: %d", ret); return 0; } diff --git a/sysdeps/s390/wcsncmp-vx.S b/sysdeps/s390/wcsncmp-vx.S index c518539bfa..5db0c707a1 100644 --- a/sysdeps/s390/wcsncmp-vx.S +++ b/sysdeps/s390/wcsncmp-vx.S @@ -59,14 +59,7 @@ ENTRY(WCSNCMP_Z13) sllg %r4,%r4,2 /* Convert character-count to byte-count. */ locgrne %r4,%r1 /* Use max byte-count, if bit 0/1 was one. */ - /* Check first character without vector load. */ - lghi %r5,4 /* current_len = 4 bytes. */ - /* Check s1/2[0]. */ - lt %r0,0(%r2) - l %r1,0(%r3) - je .Lend_cmp_one_char - crjne %r0,%r1,.Lend_cmp_one_char - + lghi %r5,0 /* current_len = 0 bytes. */ .Lloop: vlbb %v17,0(%r5,%r3),6 /* Load s2 to block boundary. */ vlbb %v16,0(%r5,%r2),6 /* Load s1 to block boundary. */ @@ -167,7 +160,6 @@ ENTRY(WCSNCMP_Z13) srl %r4,2 /* And convert it to character-index. */ vlgvf %r0,%v16,0(%r4) /* Load character-values. */ vlgvf %r1,%v17,0(%r4) -.Lend_cmp_one_char: cr %r0,%r1 je .Lend_equal lghi %r2,1 diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index d5d9af4de2..d92ea7a1f3 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -131,6 +131,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ tst-pidfd \ tst-process_mrelease \ tst-mount \ + tst-linux-mremap1 \ # tests # process_madvise requires CAP_SYS_ADMIN. @@ -402,6 +403,7 @@ endif ifeq ($(subdir),elf) sysdep-rtld-routines += dl-brk dl-sbrk dl-getcwd dl-openat64 dl-opendir +dl-routines += dl-rseq-symbols libof-lddlibc4 = lddlibc4 diff --git a/sysdeps/unix/sysv/linux/bits/uio-ext.h b/sysdeps/unix/sysv/linux/bits/uio-ext.h index 5b0dba08c5..e49b66facd 100644 --- a/sysdeps/unix/sysv/linux/bits/uio-ext.h +++ b/sysdeps/unix/sysv/linux/bits/uio-ext.h @@ -47,6 +47,7 @@ extern ssize_t process_vm_writev (pid_t __pid, const struct iovec *__lvec, #define RWF_SYNC 0x00000004 /* per-IO O_SYNC. */ #define RWF_NOWAIT 0x00000008 /* per-IO nonblocking mode. */ #define RWF_APPEND 0x00000010 /* per-IO O_APPEND. */ +#define RWF_NOAPPEND 0x00000020 /* per-IO negation of O_APPEND */ __END_DECLS diff --git a/sysdeps/unix/sysv/linux/dl-rseq-symbols.S b/sysdeps/unix/sysv/linux/dl-rseq-symbols.S new file mode 100644 index 0000000000..b4bba06a99 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-rseq-symbols.S @@ -0,0 +1,64 @@ +/* Define symbols used by rseq. + Copyright (C) 2024 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 <sysdep.h> + +#if __WORDSIZE == 64 +#define RSEQ_OFFSET_SIZE 8 +#else +#define RSEQ_OFFSET_SIZE 4 +#endif + +/* Some targets define a macro to denote the zero register. */ +#undef zero + +/* Define 2 symbols: '__rseq_size' is public const and '_rseq_size' (an + alias of '__rseq_size') is hidden and writable for internal use by the + dynamic linker which will initialize the value both symbols point to + before copy relocations take place. */ + + .globl __rseq_size + .type __rseq_size, %object + .size __rseq_size, 4 + .hidden _rseq_size + .globl _rseq_size + .type _rseq_size, %object + .size _rseq_size, 4 + .section .data.rel.ro + .balign 4 +__rseq_size: +_rseq_size: + .zero 4 + +/* Define 2 symbols: '__rseq_offset' is public const and '_rseq_offset' (an + alias of '__rseq_offset') is hidden and writable for internal use by the + dynamic linker which will initialize the value both symbols point to + before copy relocations take place. */ + + .globl __rseq_offset + .type __rseq_offset, %object + .size __rseq_offset, RSEQ_OFFSET_SIZE + .hidden _rseq_offset + .globl _rseq_offset + .type _rseq_offset, %object + .size _rseq_offset, RSEQ_OFFSET_SIZE + .section .data.rel.ro + .balign RSEQ_OFFSET_SIZE +__rseq_offset: +_rseq_offset: + .zero RSEQ_OFFSET_SIZE diff --git a/sysdeps/unix/sysv/linux/mremap-failure.h b/sysdeps/unix/sysv/linux/mremap-failure.h new file mode 100644 index 0000000000..c99ab30ca9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/mremap-failure.h @@ -0,0 +1,30 @@ +/* mremap failure handling. Linux version. + Copyright (C) 2024 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 <support/check.h> + +/* Return exit value on mremap failure with errno ERR. */ + +static int +mremap_failure_exit (int err) +{ + if (err != EINVAL) + return EXIT_FAILURE; + + return EXIT_UNSUPPORTED; +} diff --git a/sysdeps/unix/sysv/linux/mremap.c b/sysdeps/unix/sysv/linux/mremap.c index e829a29dbd..c48932e569 100644 --- a/sysdeps/unix/sysv/linux/mremap.c +++ b/sysdeps/unix/sysv/linux/mremap.c @@ -20,6 +20,12 @@ #include <sysdep.h> #include <stdarg.h> #include <stddef.h> +#include <errno.h> + +#define MREMAP_KNOWN_BITS \ + (MREMAP_MAYMOVE \ + | MREMAP_FIXED \ + | MREMAP_DONTUNMAP) void * __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...) @@ -27,7 +33,13 @@ __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...) va_list va; void *new_addr = NULL; - if (flags & MREMAP_FIXED) + if (flags & ~(MREMAP_KNOWN_BITS)) + { + __set_errno (EINVAL); + return MAP_FAILED; + } + + if (flags & (MREMAP_FIXED | MREMAP_DONTUNMAP)) { va_start (va, flags); new_addr = va_arg (va, void *); diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h index 210f3ec566..f08a70dfc4 100644 --- a/sysdeps/unix/sysv/linux/rseq-internal.h +++ b/sysdeps/unix/sysv/linux/rseq-internal.h @@ -25,15 +25,34 @@ #include <stdio.h> #include <sys/rseq.h> +/* 32 is the initially required value for the area size. The + actually used rseq size may be less (20 bytes initially). */ +#define RSEQ_AREA_SIZE_INITIAL 32 +#define RSEQ_AREA_SIZE_INITIAL_USED 20 + +/* The variables are in .data.relro but are not yet write-protected. */ +extern unsigned int _rseq_size attribute_hidden; +extern ptrdiff_t _rseq_offset attribute_hidden; + #ifdef RSEQ_SIG static inline bool rseq_register_current_thread (struct pthread *self, bool do_rseq) { if (do_rseq) { + unsigned int size; +#if IS_IN (rtld) + /* Use the hidden symbol in ld.so. */ + size = _rseq_size; +#else + size = __rseq_size; +#endif + if (size < RSEQ_AREA_SIZE_INITIAL) + /* The initial implementation used only 20 bytes out of 32, + but still expected size 32. */ + size = RSEQ_AREA_SIZE_INITIAL; int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area, - sizeof (self->rseq_area), - 0, RSEQ_SIG); + size, 0, RSEQ_SIG); if (!INTERNAL_SYSCALL_ERROR_P (ret)) return true; } diff --git a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c new file mode 100644 index 0000000000..408e8af2ab --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c @@ -0,0 +1,63 @@ +/* Test mremap with MREMAP_DONTUNMAP. + Copyright (C) 2024 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 <errno.h> +#include <sys/mman.h> +#include <support/xstdlib.h> +#include <support/xunistd.h> +#include <support/check.h> +#include <support/test-driver.h> +#include <mremap-failure.h> + +static int +do_test (void) +{ + size_t old_size = getpagesize (); + size_t new_size = old_size; + char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + old_addr[0] = 1; + old_addr[old_size - 1] = 2; + + /* Create an available 64-page mmap region. */ + size_t fixed_size = old_size * 64; + char *fixed_addr = xmmap (NULL, fixed_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + xmunmap (fixed_addr, fixed_size); + + /* Add 3 * pagesize. */ + fixed_size += 3 * old_size; + + /* Test MREMAP_DONTUNMAP. It should return FIXED_ADDR created above. */ + char *new_addr = mremap (old_addr, old_size, new_size, + MREMAP_DONTUNMAP | MREMAP_MAYMOVE, + fixed_addr); + if (new_addr == MAP_FAILED) + return mremap_failure_exit (errno); + TEST_VERIFY_EXIT (fixed_addr == new_addr); + old_addr[0] = 3; + old_addr[old_size - 1] = 4; + new_addr[0] = 1; + new_addr[new_size - 1] = 2; + xmunmap (new_addr, new_size); + xmunmap (old_addr, old_size); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c index fa6a89541f..613593f7f9 100644 --- a/sysdeps/unix/sysv/linux/tst-rseq.c +++ b/sysdeps/unix/sysv/linux/tst-rseq.c @@ -29,6 +29,7 @@ # include <stdlib.h> # include <string.h> # include <syscall.h> +# include <sys/auxv.h> # include <thread_pointer.h> # include <tls.h> # include "tst-rseq.h" @@ -42,7 +43,8 @@ do_rseq_main_test (void) TEST_COMPARE (__rseq_flags, 0); TEST_VERIFY ((char *) __thread_pointer () + __rseq_offset == (char *) &pd->rseq_area); - TEST_COMPARE (__rseq_size, sizeof (pd->rseq_area)); + /* The current implementation only supports the initial size. */ + TEST_COMPARE (__rseq_size, 20); } static void @@ -52,6 +54,12 @@ do_rseq_test (void) { FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); } + printf ("info: __rseq_size: %u\n", __rseq_size); + printf ("info: __rseq_offset: %td\n", __rseq_offset); + printf ("info: __rseq_flags: %u\n", __rseq_flags); + printf ("info: getauxval (AT_RSEQ_FEATURE_SIZE): %ld\n", + getauxval (AT_RSEQ_FEATURE_SIZE)); + printf ("info: getauxval (AT_RSEQ_ALIGN): %ld\n", getauxval (AT_RSEQ_ALIGN)); do_rseq_main_test (); } #else /* RSEQ_SIG */ |