From 542816b60852be9a7ed84e3211fb975468a10932 Mon Sep 17 00:00:00 2001 From: GNU Libc Maintainers Date: Fri, 20 Sep 2019 20:39:03 +0100 Subject: [PATCH] git-updates GIT update of https://sourceware.org/git/glibc.git/release/2.29/master from glibc-2.29 Gbp-Pq: Name git-updates.diff --- ChangeLog | 324 ++++++++++ NEWS | 46 ++ Rules | 5 +- dlfcn/dlerror.c | 29 +- elf/Makefile | 1 + elf/dl-sysdep.c | 11 +- elf/elf.h | 7 + elf/pldd-xx.c | 114 ++-- elf/pldd.c | 64 +- elf/tst-pldd.c | 118 ++++ include/stap-probe.h | 1 + io/Makefile | 5 - io/copy_file_range-compat.c | 160 ----- io/copy_file_range.c | 16 +- io/tst-copy_file_range.c | 560 +----------------- libio/Makefile | 17 +- libio/genops.c | 16 +- libio/tst-bz24228.c | 29 + libio/tst-bz24228.map | 5 + libio/tst-wfile-sync.c | 39 ++ libio/tst-wfile-sync.input | 1 + libio/wfileops.c | 5 +- localedata/locales/ja_JP | 6 +- malloc/malloc.c | 13 +- malloc/mtrace.c | 72 ++- malloc/tst-malloc-too-large.c | 39 ++ malloc/tst-memalign.c | 10 + nptl/Makefile | 3 +- nptl/pthread_join_common.c | 5 +- nptl/pthread_mutex_trylock.c | 57 +- nptl/pthread_rwlock_tryrdlock.c | 25 +- nptl/pthread_rwlock_trywrlock.c | 9 +- nptl/register-atfork.c | 8 +- nptl/tst-rwlock-tryrdlock-stall.c | 355 +++++++++++ nptl/tst-rwlock-trywrlock-stall.c | 108 ++++ posix/bits/types.h | 10 +- posix/regexec.c | 6 +- posix/sys/types.h | 33 +- posix/tst-mmap-offset.c | 9 +- scripts/check-installed-headers.sh | 37 +- scripts/check-obsolete-constructs.py | 466 +++++++++++++++ string/memmem.c | 127 ++-- string/str-two-way.h | 9 +- string/strstr.c | 165 ++++-- support/Makefile | 7 + support/capture_subprocess.h | 6 + support/subprocess.h | 49 ++ support/support_capture_subprocess.c | 80 ++- support/support_subprocess.c | 152 +++++ support/tst-support_capture_subprocess.c | 183 +++++- support/xposix_spawn.c | 32 + support/xposix_spawn_file_actions_addclose.c | 29 + .../xposix_spawn_file_actions_adddup2.c | 26 +- support/xpthread_rwlock_destroy.c | 26 + support/xspawn.h | 34 ++ support/xthread.h | 1 + sysdeps/aarch64/dl-machine.h | 35 +- sysdeps/alpha/fpu/libm-test-ulps | 16 +- sysdeps/arm/stap-probe-machine.h | 22 + sysdeps/generic/mmap_info.h | 16 + sysdeps/generic/stap-probe-machine.h | 19 + sysdeps/hppa/fpu/libm-test-ulps | 88 +-- sysdeps/nptl/fork.c | 6 +- sysdeps/nptl/fork.h | 8 +- sysdeps/powerpc/dl-procinfo.h | 2 +- sysdeps/s390/dl-procinfo.h | 3 +- sysdeps/sparc/dl-procinfo.h | 4 +- sysdeps/unix/{ => sysv/linux}/alpha/getegid.S | 0 sysdeps/unix/{ => sysv/linux}/alpha/geteuid.S | 0 sysdeps/unix/{ => sysv/linux}/alpha/getppid.S | 0 .../unix/sysv/linux/alpha/kernel-features.h | 7 +- sysdeps/unix/sysv/linux/alpha/sysdep.h | 4 +- sysdeps/unix/sysv/linux/arm/dl-procinfo.h | 2 +- sysdeps/unix/sysv/linux/arm/kernel-features.h | 1 - sysdeps/unix/sysv/linux/copy_file_range.c | 21 +- sysdeps/unix/sysv/linux/i386/dl-procinfo.h | 4 +- sysdeps/unix/sysv/linux/kernel-features.h | 4 - .../sysv/linux/microblaze/kernel-features.h | 5 - sysdeps/unix/sysv/linux/mips/mmap_info.h | 13 + sysdeps/unix/sysv/linux/mmap64.c | 9 +- sysdeps/unix/sysv/linux/riscv/vfork.S | 5 +- sysdeps/unix/sysv/linux/s390/dl-procinfo.h | 4 +- sysdeps/unix/sysv/linux/sh/kernel-features.h | 1 - sysdeps/x86_64/memcmp.S | 20 +- sysdeps/x86_64/x32/Makefile | 3 +- sysdeps/x86_64/x32/tst-size_t-memcmp-2.c | 79 +++ 86 files changed, 2931 insertions(+), 1240 deletions(-) create mode 100644 elf/tst-pldd.c delete mode 100644 io/copy_file_range-compat.c create mode 100644 libio/tst-bz24228.c create mode 100644 libio/tst-bz24228.map create mode 100644 libio/tst-wfile-sync.c create mode 100644 libio/tst-wfile-sync.input create mode 100644 nptl/tst-rwlock-tryrdlock-stall.c create mode 100644 nptl/tst-rwlock-trywrlock-stall.c create mode 100644 scripts/check-obsolete-constructs.py create mode 100644 support/subprocess.h create mode 100644 support/support_subprocess.c create mode 100644 support/xposix_spawn.c create mode 100644 support/xposix_spawn_file_actions_addclose.c rename io/tst-copy_file_range-compat.c => support/xposix_spawn_file_actions_adddup2.c (57%) create mode 100644 support/xpthread_rwlock_destroy.c create mode 100644 support/xspawn.h create mode 100644 sysdeps/arm/stap-probe-machine.h create mode 100644 sysdeps/generic/mmap_info.h create mode 100644 sysdeps/generic/stap-probe-machine.h rename sysdeps/unix/{ => sysv/linux}/alpha/getegid.S (100%) rename sysdeps/unix/{ => sysv/linux}/alpha/geteuid.S (100%) rename sysdeps/unix/{ => sysv/linux}/alpha/getppid.S (100%) create mode 100644 sysdeps/unix/sysv/linux/mips/mmap_info.h create mode 100644 sysdeps/x86_64/x32/tst-size_t-memcmp-2.c diff --git a/ChangeLog b/ChangeLog index 59dab1846..74f792369 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,327 @@ +2019-09-20 Joseph Myers + + * sysdeps/unix/sysv/linux/riscv/vfork.S: Do not include + . + (CLONE_VM): New macro. + (CLONE_VFORK): Likewise. + +2019-09-14 Aurelien Jarno + + [BZ #24986] + * sysdeps/unix/alpha/getegid.S: Move to ... + * sysdeps/unix/sysv/linux/alpha/getegid.S: ... here. + * sysdeps/unix/alpha/geteuid.S: Move to ... + * sysdeps/unix/sysv/linux/alpha/geteuid.S: ... here. + * sysdeps/unix/alpha/getppid.S: Move to ... + * sysdeps/unix/sysv/linux/alpha/getppid.S: ... here + +2019-09-13 Wilco Dijkstra + + * string/memmem.c (__memmem): Rewrite to improve performance. + +2019-06-12 Wilco Dijkstra + + * string/str-two-way.h (two_way_short_needle): Add inline to avoid + warning. + (two_way_long_needle): Block inlining. + * string/strstr.c (strstr2): Add new function. + (strstr3): Likewise. + (STRSTR): Completely rewrite strstr to improve performance. + +2019-09-03 Aurelien Jarno + + * sysdeps/alpha/fpu/libm-test-ulps: Regenerated using GCC 9.2. + +2019-08-18 Adhemerval Zanella + + * sysdeps/hppa/fpu/libm-test-ulps: Update. + +2019-08-15 Richard Henderson + + * sysdeps/unix/sysv/linux/alpha/kernel-features.h (__NR_shmat): + Do not redefine. + * sysdeps/unix/sysv/linux/alpha/sysdep.h (__NR_osf_shmat): + Do not redefine. + +2019-07-15 Adhemerval Zanella + + [BZ #24699] + * posix/tst-mmap-offset.c: Mention BZ #24699. + (do_test_bz21270): Rename to do_test_large_offset and use + mmap64_maximum_offset to check for maximum expected offset value. + * sysdeps/generic/mmap_info.h: New file. + * sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise. + * sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff + __NR_mmap2 is used. + +2019-07-12 Szabolcs Nagy + + * sysdeps/aarch64/dl-machine.h (elf_machine_lazy_rel): Check + STO_AARCH64_VARIANT_PCS and bind such symbols at load time. + +2019-06-13 Szabolcs Nagy + + * elf/elf.h (STO_AARCH64_VARIANT_PCS): Define. + (DT_AARCH64_VARIANT_PCS): Define. + +2019-06-28 Florian Weimer + + [BZ #24744] + io: Remove the copy_file_range emulation. + * sysdeps/unix/sysv/linux/copy_file_range.c (copy_file_range): Do + not define and call copy_file_range_compat. + * io/Makefile (tests-static, tests-internal): Do not add + tst-copy_file_range-compat. + * io/copy_file_range-compat.c: Remove file. + * io/copy_file_range.c (copy_file_range): Define as stub. + * io/tst-copy_file_range-compat.c: Remove file. + * io/tst-copy_file_range.c (xdevfile): Remove variable. + (typical_sizes): Update comment. Remove 16K sizes. + (maximum_offset, maximum_offset_errno, maximum_offset_hard_limit): + Remove variables. + (find_maximum_offset, pipe_as_source, pipe_as_destination) + (delayed_write_failure_beginning, delayed_write_failure_end) + (cross_device_failure, enospc_failure_1, enospc_failure) + (oappend_failure): Remove functions. + (tests): Adjust test case list. + (do_test): Remove file system search code. Check for ENOSYS from + copy_file_range. Do not free xdevfile. + * manual/llio.texi (Copying File Data): Document ENOSYS error from + copy_file_range. Do not document the EXDEV error, which future + kernels may not report. Update the wording to reflect that + further errors are possible. + * sysdeps/unix/sysv/linux/alpha/kernel-features.h + [__LINUX_KERNEL_VERSION < 0x040D00] (__ASSUME_COPY_FILE_RANGE): Do + not undefine. + * sysdeps/unix/sysv/linux/arm/kernel-features.h + [__LINUX_KERNEL_VERSION < 0x040700] (__ASSUME_COPY_FILE_RANGE): + Likewise. + * sysdeps/unix/sysv/linux/kernel-features.h + [__LINUX_KERNEL_VERSION >= 0x040500] (__ASSUME_COPY_FILE_RANGE): + Remove definition. + * sysdeps/unix/sysv/linux/microblaze/kernel-features.h + [__LINUX_KERNEL_VERSION < 0x040A00] (__ASSUME_COPY_FILE_RANGE): Do + not undefine. + * sysdeps/unix/sysv/linux/sh/kernel-features.h + [__LINUX_KERNEL_VERSION < 0x040800] (__ASSUME_COPY_FILE_RANGE): + Likewise. + +2019-06-20 Dmitry V. Levin + Florian Weimer + + [BZ #24228] + * libio/genops.c (_IO_unbuffer_all) + [SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)]: Do not attempt to free wide + buffers and access _IO_FILE_complete members of legacy libio streams. + * libio/tst-bz24228.c: New file. + * libio/tst-bz24228.map: Likewise. + * libio/Makefile [build-shared] (tests): Add tst-bz24228. + [build-shared] (generated): Add tst-bz24228.mtrace and + tst-bz24228.check. + [run-built-tests && build-shared] (tests-special): Add + $(objpfx)tst-bz24228-mem.out. + (LDFLAGS-tst-bz24228, tst-bz24228-ENV): New variables. + ($(objpfx)tst-bz24228-mem.out): New rule. + +2019-03-13 Zack Weinberg + + * scripts/check-obsolete-constructs.py: New test script. + * scripts/check-installed-headers.sh: Remove tests for + obsolete typedefs, superseded by check-obsolete-constructs.py. + * Rules: Run scripts/check-obsolete-constructs.py over $(headers) + as a special test. Update commentary. + * posix/bits/types.h (__SQUAD_TYPE, __S64_TYPE): Define as __int64_t. + (__UQUAD_TYPE, __U64_TYPE): Define as __uint64_t. + Update commentary. + * posix/sys/types.h (__u_intN_t): Remove. + (u_int8_t): Typedef using __uint8_t. + (u_int16_t): Typedef using __uint16_t. + (u_int32_t): Typedef using __uint32_t. + (u_int64_t): Typedef using __uint64_t. + +2019-04-18 Adhemerval Zanella + + * malloc/tst-memalign.c (do_test): Disable + -Walloc-size-larger-than= around tests of malloc with negative + sizes. + * malloc/tst-malloc-too-large.c (do_test): Likewise. + +2019-05-22 Wilco Dijkstra + + [BZ #24531] + * malloc/malloc.c (MAX_TCACHE_COUNT): New define. + (do_set_tcache_count): Only update if count is small enough. + * manual/tunables.texi (glibc.malloc.tcache_count): Document max value. + +2019-05-15 Mark Wielaard + + [BZ#24476] + * dlfcn/dlerror.c (__dlerror_main_freeres): Guard using + __libc_once_get (once) and static_buf == NULL. + (__dlerror): Check we have a valid key, set result to static_buf + otherwise. + +2019-05-15 Andreas Schwab + + [BZ #20568] + * libio/wfileops.c (_IO_wfile_sync): Correct last argument to + __codecvt_do_length. + * libio/Makefile (tests): Add tst-wfile-sync. + ($(objpfx)tst-wfile-sync.out): Depend on $(gen-locales). + * libio/tst-wfile-sync.c: New file. + * libio/tst-wfile-sync.input: New file. + +2019-04-23 Adhemerval Zanella + + [BZ #18035] + * elf/Makefile (tests-container): Add tst-pldd. + * elf/pldd-xx.c: Use _Static_assert in of pldd_assert. + (E(find_maps)): Avoid use alloca, use default read file operations + instead of explicit LFS names, and fix infinite loop. + * elf/pldd.c: Explicit set _FILE_OFFSET_BITS, cleanup headers. + (get_process_info): Use _Static_assert instead of assert, use default + directory operations instead of explicit LFS names, and free some + leadek pointers. + * elf/tst-pldd.c: New file. + +2019-04-17 Adhemerval Zanella + + * support/Makefile (libsupport-routines): Add support_subprocess, + xposix_spawn, xposix_spawn_file_actions_addclose, and + xposix_spawn_file_actions_adddup2. + (tst-support_capture_subprocess-ARGS): New rule. + * support/capture_subprocess.h (support_capture_subprogram): New + prototype. + * support/support_capture_subprocess.c (support_capture_subprocess): + Refactor to use support_subprocess and support_capture_poll. + (support_capture_subprogram): New function. + * support/tst-support_capture_subprocess.c (write_mode_to_str, + str_to_write_mode, test_common, parse_int, handle_restart, + do_subprocess, do_subprogram, do_multiple_tests): New functions. + (do_test): Add support_capture_subprogram tests. + * support/subprocess.h: New file. + * support/support_subprocess.c: Likewise. + * support/xposix_spawn.c: Likewise. + * support/xposix_spawn_file_actions_addclose.c: Likewise. + * support/xposix_spawn_file_actions_adddup2.c: Likewise. + * support/xspawn.h: Likewise. + +2019-04-09 Carlos O'Donell + Kwok Cheung Yeung + + [BZ #16573] + * malloc/mtrace.c: Define prototypes for all hooks. + (set_default_hooks): New function. + (set_trace_hooks): Likewise. + (save_default_hooks): Likewise. + (tr_freehook): Use new s*_hooks functions. + (tr_mallochook): Likewise. + (tr_reallochook): Likewise. + (tr_memalignhook): Likewise. + (mtrace): Likewise. + (muntrace): Likewise. + +2019-04-02 TAMUKI Shoichi + + [BZ #22964] + * localedata/locales/ja_JP (LC_TIME): Add entry for the new Japanese + era. + +2019-03-02 TAMUKI Shoichi + + [BZ #24162] + * localedata/locales/ja_JP (LC_TIME): Change the offset for Taisho + gan-nen from 2 to 1. Problem reported by Morimitsu, Junji. + +2019-03-21 Stefan Liebler + + * sysdeps/s390/dl-procinfo.h (HWCAP_IMPORTANT): + Add HWCAP_S390_VX and HWCAP_S390_VXE. + +2019-01-31 Paul Eggert + + CVE-2019-9169 + regex: fix read overrun [BZ #24114] + Problem found by AddressSanitizer, reported by Hongxu Chen in: + https://debbugs.gnu.org/34140 + * posix/regexec.c (proceed_next_node): + Do not read past end of input buffer. + +2019-03-13 Stefan Liebler + + * elf/dl-sysdep.c (_dl_show_auxv): Remove condition and always + call _dl_procinfo. + * sysdeps/unix/sysv/linux/s390/dl-procinfo.h (_dl_procinfo): + Ignore types other than AT_HWCAP. + * sysdeps/sparc/dl-procinfo.h (_dl_procinfo): Likewise. + * sysdeps/unix/sysv/linux/i386/dl-procinfo.h (_dl_procinfo): + Likewise. + * sysdeps/powerpc/dl-procinfo.h (_dl_procinfo): Adjust comment + in the case of falling back to generic output mechanism. + * sysdeps/unix/sysv/linux/arm/dl-procinfo.h (_dl_procinfo): + Likewise. + +2019-02-15 Florian Weimer + + [BZ #24211] + * nptl/pthread_join_common.c (__pthread_timedjoin_ex): Do not read + pd->result after the thread descriptor has been freed. + +2019-02-08 Florian Weimer + + [BZ #24161] + * sysdeps/nptl/fork.h (__run_fork_handlers): Add multiple_threads + argument. + * nptl/register-atfork.c (__run_fork_handlers): Only perform + locking if the new do_locking argument is true. + * sysdeps/nptl/fork.c (__libc_fork): Pass multiple_threads to + __run_fork_handlers. + +2019-02-07 Stefan Liebler + + [BZ #24180] + * nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock): + Add compiler barriers and comments. + +2019-02-05 Florian Weimer + + [BZ #24164] + arm: Use "nr" constraint for Systemtap probes, to avoid the + compiler using memory operands for constants, due to the "o" + alternative in the default "nor" constraint. + * include/stap-probe.h [USE_STAP_PROBE]: Include + + * sysdeps/generic/stap-probe-machine.h: New file. + * sysdeps/arm/stap-probe-machine.h: Likewise. + +2019-02-04 H.J. Lu + + [BZ #24155] + CVE-2019-7309 + * NEWS: Updated for CVE-2019-7309. + * sysdeps/x86_64/memcmp.S: Use RDX_LP for size. Clear the + upper 32 bits of RDX register for x32. Use unsigned Jcc + instructions, instead of signed. + * sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcmp-2. + * sysdeps/x86_64/x32/tst-size_t-memcmp-2.c: New test. + +2019-01-31 Carlos O'Donell + Torvald Riegel + Rik Prohaska + + [BZ# 23844] + * nptl/Makefile (tests): Add tst-rwlock-tryrdlock-stall, and + tst-rwlock-trywrlock-stall. + * nptl/pthread_rwlock_tryrdlock.c (__pthread_rwlock_tryrdlock): + Wake waiters if PTHREAD_RWLOCK_FUTEX_USED is set. + * nptl/pthread_rwlock_trywrlock.c (__pthread_rwlock_trywrlock): + Set __wrphase_fute to 1 only if we started the write phase. + * nptl/tst-rwlock-tryrdlock-stall.c: New file. + * nptl/tst-rwlock-trywrlock-stall.c: New file. + * support/Makefile (libsupport-routines): Add xpthread_rwlock_destroy. + * support/xpthread_rwlock_destroy.c: New file. + * support/xthread.h: Declare xpthread_rwlock_destroy. + 2019-01-31 Siddhesh Poyarekar * version.h (RELEASE): Set to "stable". diff --git a/NEWS b/NEWS index 912a9bdc0..5dc41ff3e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,52 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. + +Version 2.29.1 + +Major new features: + +* The entry for the new Japanese era has been added for ja_JP locale. + +Deprecated and removed features, and other changes affecting compatibility: + +* The copy_file_range function fails with ENOSYS if the kernel does not + support the system call of the same name. Previously, user space + emulation was performed, but its behavior did not match the kernel + behavior, which was deemed too confusing. Applications which use the + copy_file_range function will have to be run on kernels which implement + the copy_file_range system call. Support for most architectures was added + in version 4.5 of the mainline Linux kernel. + +The following bugs are resolved with this release: + + [16573] malloc: Set and reset all hooks for tracing + [18035] Fix pldd hang + [20568] Fix crash in _IO_wfile_sync + [24155] x32 memcmp can treat positive length as 0 (if sign bit in RDX is set) (CVE-2019-7309) + [24164] Systemtap probes need to use "nr" constraint on 32-bit Arm + [24161] __run_fork_handlers self-deadlocks in malloc/tst-mallocfork2 + [24211] Use-after-free in Systemtap probe in pthread_join + [24228] old x86 applications that use legacy libio crash on exit + [24476] dlfcn: Guard __dlerror_main_freeres with __libc_once_get (once) + [24744] io: Remove the copy_file_range emulation + [24986] alpha: new getegid, geteuid and getppid syscalls used + unconditionally + +Security related changes: + + CVE-2019-7309: x86-64 memcmp used signed Jcc instructions to check + size. For x86-64, memcmp on an object size larger than SSIZE_MAX + has undefined behavior. On x32, the size_t argument may be passed + in the lower 32 bits of the 64-bit RDX register with non-zero upper + 32 bits. When it happened with the sign bit of RDX register set, + memcmp gave the wrong result since it treated the size argument as + zero. Reported by H.J. Lu. + + CVE-2019-9169: Attempted case-insensitive regular-expression match + via proceed_next_node in posix/regexec.c leads to heap-based buffer + over-read. Reported by Hongxu Chen. + Version 2.29 diff --git a/Rules b/Rules index 1562f2ce6..16afa6aca 100644 --- a/Rules +++ b/Rules @@ -82,7 +82,8 @@ $(common-objpfx)dummy.c: common-generated += dummy.o dummy.c ifneq "$(headers)" "" -# Special test of all the installed headers in this directory. +# Test that all of the headers installed by this directory can be compiled +# in isolation. tests-special += $(objpfx)check-installed-headers-c.out libof-check-installed-headers-c := testsuite $(objpfx)check-installed-headers-c.out: \ @@ -93,6 +94,8 @@ $(objpfx)check-installed-headers-c.out: \ $(evaluate-test) ifneq "$(CXX)" "" +# If a C++ compiler is available, also test that they can be compiled +# in isolation as C++. tests-special += $(objpfx)check-installed-headers-cxx.out libof-check-installed-headers-cxx := testsuite $(objpfx)check-installed-headers-cxx.out: \ diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c index 27376582d..ca42c126c 100644 --- a/dlfcn/dlerror.c +++ b/dlfcn/dlerror.c @@ -72,9 +72,16 @@ __dlerror (void) __libc_once (once, init); /* Get error string. */ - result = (struct dl_action_result *) __libc_getspecific (key); - if (result == NULL) - result = &last_result; + if (static_buf != NULL) + result = static_buf; + else + { + /* init () has been run and we don't use the static buffer. + So we have a valid key. */ + result = (struct dl_action_result *) __libc_getspecific (key); + if (result == NULL) + result = &last_result; + } /* Test whether we already returned the string. */ if (result->returned != 0) @@ -230,13 +237,19 @@ free_key_mem (void *mem) void __dlerror_main_freeres (void) { - void *mem; /* Free the global memory if used. */ check_free (&last_result); - /* Free the TSD memory if used. */ - mem = __libc_getspecific (key); - if (mem != NULL) - free_key_mem (mem); + + if (__libc_once_get (once) && static_buf == NULL) + { + /* init () has been run and we don't use the static buffer. + So we have a valid key. */ + void *mem; + /* Free the TSD memory if used. */ + mem = __libc_getspecific (key); + if (mem != NULL) + free_key_mem (mem); + } } struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon)); diff --git a/elf/Makefile b/elf/Makefile index 9cf5cd8df..e7457e809 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -194,6 +194,7 @@ tests-internal += loadtest unload unload2 circleload1 \ tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \ tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \ tst-create_format1 +tests-container += tst-pldd ifeq ($(build-hardcoded-path-in-tests),yes) tests += tst-dlopen-aout tst-dlopen-aout-no-pie = yes diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c index 5f6c679a3..5d19b100b 100644 --- a/elf/dl-sysdep.c +++ b/elf/dl-sysdep.c @@ -328,14 +328,9 @@ _dl_show_auxv (void) assert (AT_NULL == 0); assert (AT_IGNORE == 1); - if (av->a_type == AT_HWCAP || av->a_type == AT_HWCAP2 - || AT_L1I_CACHEGEOMETRY || AT_L1D_CACHEGEOMETRY - || AT_L2_CACHEGEOMETRY || AT_L3_CACHEGEOMETRY) - { - /* These are handled in a special way per platform. */ - if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0) - continue; - } + /* Some entries are handled in a special way per platform. */ + if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0) + continue; if (idx < sizeof (auxvars) / sizeof (auxvars[0]) && auxvars[idx].form != unknown) diff --git a/elf/elf.h b/elf/elf.h index 47a514a38..38567f489 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -2854,6 +2854,13 @@ enum #define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ #define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ +/* AArch64 specific values for the Dyn d_tag field. */ +#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5) +#define DT_AARCH64_NUM 6 + +/* AArch64 specific values for the st_other field. */ +#define STO_AARCH64_VARIANT_PCS 0x80 + /* ARM relocs. */ #define R_ARM_NONE 0 /* No reloc */ diff --git a/elf/pldd-xx.c b/elf/pldd-xx.c index 547f840ee..756f6d7a1 100644 --- a/elf/pldd-xx.c +++ b/elf/pldd-xx.c @@ -23,10 +23,6 @@ #define EW_(e, w, t) EW__(e, w, _##t) #define EW__(e, w, t) e##w##t -#define pldd_assert(name, exp) \ - typedef int __assert_##name[((exp) != 0) - 1] - - struct E(link_map) { EW(Addr) l_addr; @@ -39,12 +35,12 @@ struct E(link_map) EW(Addr) l_libname; }; #if CLASS == __ELF_NATIVE_CLASS -pldd_assert (l_addr, (offsetof (struct link_map, l_addr) - == offsetof (struct E(link_map), l_addr))); -pldd_assert (l_name, (offsetof (struct link_map, l_name) - == offsetof (struct E(link_map), l_name))); -pldd_assert (l_next, (offsetof (struct link_map, l_next) - == offsetof (struct E(link_map), l_next))); +_Static_assert (offsetof (struct link_map, l_addr) + == offsetof (struct E(link_map), l_addr), "l_addr"); +_Static_assert (offsetof (struct link_map, l_name) + == offsetof (struct E(link_map), l_name), "l_name"); +_Static_assert (offsetof (struct link_map, l_next) + == offsetof (struct E(link_map), l_next), "l_next"); #endif @@ -54,10 +50,10 @@ struct E(libname_list) EW(Addr) next; }; #if CLASS == __ELF_NATIVE_CLASS -pldd_assert (name, (offsetof (struct libname_list, name) - == offsetof (struct E(libname_list), name))); -pldd_assert (next, (offsetof (struct libname_list, next) - == offsetof (struct E(libname_list), next))); +_Static_assert (offsetof (struct libname_list, name) + == offsetof (struct E(libname_list), name), "name"); +_Static_assert (offsetof (struct libname_list, next) + == offsetof (struct E(libname_list), next), "next"); #endif struct E(r_debug) @@ -69,16 +65,17 @@ struct E(r_debug) EW(Addr) r_map; }; #if CLASS == __ELF_NATIVE_CLASS -pldd_assert (r_version, (offsetof (struct r_debug, r_version) - == offsetof (struct E(r_debug), r_version))); -pldd_assert (r_map, (offsetof (struct r_debug, r_map) - == offsetof (struct E(r_debug), r_map))); +_Static_assert (offsetof (struct r_debug, r_version) + == offsetof (struct E(r_debug), r_version), "r_version"); +_Static_assert (offsetof (struct r_debug, r_map) + == offsetof (struct E(r_debug), r_map), "r_map"); #endif static int -E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) +E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv, + size_t auxv_size) { EW(Addr) phdr = 0; unsigned int phnum = 0; @@ -104,12 +101,9 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) if (phdr == 0 || phnum == 0 || phent == 0) error (EXIT_FAILURE, 0, gettext ("cannot find program header of process")); - EW(Phdr) *p = alloca (phnum * phent); - if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent) - { - error (0, 0, gettext ("cannot read program header")); - return EXIT_FAILURE; - } + EW(Phdr) *p = xmalloc (phnum * phent); + if (pread (memfd, p, phnum * phent, phdr) != phnum * phent) + error (EXIT_FAILURE, 0, gettext ("cannot read program header")); /* Determine the load offset. We need this for interpreting the other program header entries so we do this in a separate loop. @@ -129,24 +123,18 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) if (p[i].p_type == PT_DYNAMIC) { EW(Dyn) *dyn = xmalloc (p[i].p_filesz); - if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr) + if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr) != p[i].p_filesz) - { - error (0, 0, gettext ("cannot read dynamic section")); - return EXIT_FAILURE; - } + error (EXIT_FAILURE, 0, gettext ("cannot read dynamic section")); /* Search for the DT_DEBUG entry. */ for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j) if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0) { struct E(r_debug) r; - if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr) + if (pread (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr) != sizeof (r)) - { - error (0, 0, gettext ("cannot read r_debug")); - return EXIT_FAILURE; - } + error (EXIT_FAILURE, 0, gettext ("cannot read r_debug")); if (r.r_map != 0) { @@ -160,13 +148,10 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) } else if (p[i].p_type == PT_INTERP) { - interp = alloca (p[i].p_filesz); - if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr) + interp = xmalloc (p[i].p_filesz); + if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr) != p[i].p_filesz) - { - error (0, 0, gettext ("cannot read program interpreter")); - return EXIT_FAILURE; - } + error (EXIT_FAILURE, 0, gettext ("cannot read program interpreter")); } if (list == 0) @@ -174,14 +159,16 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) if (interp == NULL) { // XXX check whether the executable itself is the loader - return EXIT_FAILURE; + exit (EXIT_FAILURE); } // XXX perhaps try finding ld.so and _r_debug in it - - return EXIT_FAILURE; + exit (EXIT_FAILURE); } + free (p); + free (interp); + /* Print the PID and program name first. */ printf ("%lu:\t%s\n", (unsigned long int) pid, exe); @@ -192,47 +179,27 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) do { struct E(link_map) m; - if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m)) - { - error (0, 0, gettext ("cannot read link map")); - status = EXIT_FAILURE; - goto out; - } + if (pread (memfd, &m, sizeof (m), list) != sizeof (m)) + error (EXIT_FAILURE, 0, gettext ("cannot read link map")); EW(Addr) name_offset = m.l_name; - again: while (1) { - ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset); + ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length, name_offset); if (n == -1) - { - error (0, 0, gettext ("cannot read object name")); - status = EXIT_FAILURE; - goto out; - } + error (EXIT_FAILURE, 0, gettext ("cannot read object name")); if (memchr (tmpbuf.data, '\0', n) != NULL) break; if (!scratch_buffer_grow (&tmpbuf)) - { - error (0, 0, gettext ("cannot allocate buffer for object name")); - status = EXIT_FAILURE; - goto out; - } + error (EXIT_FAILURE, 0, + gettext ("cannot allocate buffer for object name")); } - if (((char *)tmpbuf.data)[0] == '\0' && name_offset == m.l_name - && m.l_libname != 0) - { - /* Try the l_libname element. */ - struct E(libname_list) ln; - if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln)) - { - name_offset = ln.name; - goto again; - } - } + /* The m.l_name and m.l_libname.name for loader linkmap points to same + values (since BZ#387 fix). Trying to use l_libname name as the + shared object name might lead to an infinite loop (BZ#18035). */ /* Skip over the executable. */ if (((char *)tmpbuf.data)[0] != '\0') @@ -242,7 +209,6 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size) } while (list != 0); - out: scratch_buffer_free (&tmpbuf); return status; } diff --git a/elf/pldd.c b/elf/pldd.c index f3fac4e48..69629bd5d 100644 --- a/elf/pldd.c +++ b/elf/pldd.c @@ -17,23 +17,17 @@ License along with the GNU C Library; if not, see . */ -#include +#define _FILE_OFFSET_BITS 64 + #include -#include #include -#include -#include #include #include #include -#include -#include #include #include -#include #include #include -#include #include #include @@ -76,14 +70,9 @@ static struct argp argp = options, parse_opt, args_doc, doc, NULL, more_help, NULL }; -// File descriptor of /proc/*/mem file. -static int memfd; - -/* Name of the executable */ -static char *exe; /* Local functions. */ -static int get_process_info (int dfd, long int pid); +static int get_process_info (const char *exe, int dfd, long int pid); static void wait_for_ptrace_stop (long int pid); @@ -102,8 +91,10 @@ main (int argc, char *argv[]) return 1; } - assert (sizeof (pid_t) == sizeof (int) - || sizeof (pid_t) == sizeof (long int)); + _Static_assert (sizeof (pid_t) == sizeof (int) + || sizeof (pid_t) == sizeof (long int), + "sizeof (pid_t) != sizeof (int) or sizeof (long int)"); + char *endp; errno = 0; long int pid = strtol (argv[remaining], &endp, 10); @@ -119,25 +110,24 @@ main (int argc, char *argv[]) if (dfd == -1) error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf); - struct scratch_buffer exebuf; - scratch_buffer_init (&exebuf); + /* Name of the executable */ + struct scratch_buffer exe; + scratch_buffer_init (&exe); ssize_t nexe; while ((nexe = readlinkat (dfd, "exe", - exebuf.data, exebuf.length)) == exebuf.length) + exe.data, exe.length)) == exe.length) { - if (!scratch_buffer_grow (&exebuf)) + if (!scratch_buffer_grow (&exe)) { nexe = -1; break; } } if (nexe == -1) - exe = (char *) ""; + /* Default stack allocation is at least 1024. */ + snprintf (exe.data, exe.length, ""); else - { - exe = exebuf.data; - exe[nexe] = '\0'; - } + ((char*)exe.data)[nexe] = '\0'; /* Stop all threads since otherwise the list of loaded modules might change while we are reading it. */ @@ -155,8 +145,8 @@ main (int argc, char *argv[]) error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"), buf); - struct dirent64 *d; - while ((d = readdir64 (dir)) != NULL) + struct dirent *d; + while ((d = readdir (dir)) != NULL) { if (! isdigit (d->d_name[0])) continue; @@ -182,7 +172,7 @@ main (int argc, char *argv[]) wait_for_ptrace_stop (tid); - struct thread_list *newp = alloca (sizeof (*newp)); + struct thread_list *newp = xmalloc (sizeof (*newp)); newp->tid = tid; newp->next = thread_list; thread_list = newp; @@ -190,17 +180,22 @@ main (int argc, char *argv[]) closedir (dir); - int status = get_process_info (dfd, pid); + if (thread_list == NULL) + error (EXIT_FAILURE, 0, gettext ("no valid %s/task entries"), buf); + + int status = get_process_info (exe.data, dfd, pid); - assert (thread_list != NULL); do { ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL); + struct thread_list *prev = thread_list; thread_list = thread_list->next; + free (prev); } while (thread_list != NULL); close (dfd); + scratch_buffer_free (&exe); return status; } @@ -281,9 +276,10 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ static int -get_process_info (int dfd, long int pid) +get_process_info (const char *exe, int dfd, long int pid) { - memfd = openat (dfd, "mem", O_RDONLY); + /* File descriptor of /proc//mem file. */ + int memfd = openat (dfd, "mem", O_RDONLY); if (memfd == -1) goto no_info; @@ -333,9 +329,9 @@ get_process_info (int dfd, long int pid) int retval; if (e_ident[EI_CLASS] == ELFCLASS32) - retval = find_maps32 (pid, auxv, auxv_size); + retval = find_maps32 (exe, memfd, pid, auxv, auxv_size); else - retval = find_maps64 (pid, auxv, auxv_size); + retval = find_maps64 (exe, memfd, pid, auxv, auxv_size); free (auxv); close (memfd); diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c new file mode 100644 index 000000000..ed19cedd0 --- /dev/null +++ b/elf/tst-pldd.c @@ -0,0 +1,118 @@ +/* Basic tests for pldd program. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +static void +target_process (void *arg) +{ + pause (); +} + +/* The test runs in a container because pldd does not support tracing + a binary started by the loader iself (as with testrun.sh). */ + +static int +do_test (void) +{ + /* Create a copy of current test to check with pldd. */ + struct support_subprocess target = support_subprocess (target_process, NULL); + + /* Run 'pldd' on test subprocess. */ + struct support_capture_subprocess pldd; + { + /* Three digits per byte plus null terminator. */ + char pid[3 * sizeof (uint32_t) + 1]; + snprintf (pid, array_length (pid), "%d", target.pid); + + const char prog[] = "/usr/bin/pldd"; + + pldd = support_capture_subprogram (prog, + (char *const []) { (char *) prog, pid, NULL }); + + support_capture_subprocess_check (&pldd, "pldd", 0, sc_allow_stdout); + } + + /* Check 'pldd' output. The test is expected to be linked against only + loader and libc. */ + { + pid_t pid; + char buffer[512]; +#define STRINPUT(size) "%" # size "s" + + FILE *out = fmemopen (pldd.out.buffer, pldd.out.length, "r"); + TEST_VERIFY (out != NULL); + + /* First line is in the form of : */ + TEST_COMPARE (fscanf (out, "%u: " STRINPUT (512), &pid, buffer), 2); + + TEST_COMPARE (pid, target.pid); + TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0); + + /* It expects only one loader and libc loaded by the program. */ + bool interpreter_found = false, libc_found = false; + while (fgets (buffer, array_length (buffer), out) != NULL) + { + /* Ignore vDSO. */ + if (buffer[0] != '/') + continue; + + /* Remove newline so baseline (buffer) can compare against the + LD_SO and LIBC_SO macros unmodified. */ + if (buffer[strlen(buffer)-1] == '\n') + buffer[strlen(buffer)-1] = '\0'; + + if (strcmp (basename (buffer), LD_SO) == 0) + { + TEST_COMPARE (interpreter_found, false); + interpreter_found = true; + continue; + } + + if (strcmp (basename (buffer), LIBC_SO) == 0) + { + TEST_COMPARE (libc_found, false); + libc_found = true; + continue; + } + } + TEST_COMPARE (interpreter_found, true); + TEST_COMPARE (libc_found, true); + + fclose (out); + } + + support_capture_subprocess_free (&pldd); + support_process_terminate (&target); + + return 0; +} + +#include diff --git a/include/stap-probe.h b/include/stap-probe.h index c53dd8659..8c26292ed 100644 --- a/include/stap-probe.h +++ b/include/stap-probe.h @@ -21,6 +21,7 @@ #ifdef USE_STAP_PROBE +# include # include /* Our code uses one macro LIBC_PROBE (name, n, arg1, ..., argn). diff --git a/io/Makefile b/io/Makefile index cfae00bbf..70914d36a 100644 --- a/io/Makefile +++ b/io/Makefile @@ -73,11 +73,6 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ tst-fts tst-fts-lfs tst-open-tmpfile \ tst-copy_file_range tst-getcwd-abspath \ -# This test includes the compat implementation of copy_file_range, -# which uses internal, unexported libc functions. -tests-static += tst-copy_file_range-compat -tests-internal += tst-copy_file_range-compat - # Likewise for statx, but we do not need static linking here. tests-internal += tst-statx diff --git a/io/copy_file_range-compat.c b/io/copy_file_range-compat.c deleted file mode 100644 index 58dbeef3e..000000000 --- a/io/copy_file_range-compat.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Emulation of copy_file_range. - Copyright (C) 2017-2019 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 - . */ - -/* The following macros should be defined before including this - file: - - COPY_FILE_RANGE_DECL Declaration specifiers for the function below. - COPY_FILE_RANGE Name of the function to define. */ - -#include -#include -#include -#include -#include -#include -#include - -COPY_FILE_RANGE_DECL -ssize_t -COPY_FILE_RANGE (int infd, __off64_t *pinoff, - int outfd, __off64_t *poutoff, - size_t length, unsigned int flags) -{ - if (flags != 0) - { - __set_errno (EINVAL); - return -1; - } - - { - struct stat64 instat; - struct stat64 outstat; - if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0) - return -1; - if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode)) - { - __set_errno (EISDIR); - return -1; - } - if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode)) - { - /* We need a regular input file so that the we can seek - backwards in case of a write failure. */ - __set_errno (EINVAL); - return -1; - } - if (instat.st_dev != outstat.st_dev) - { - /* Cross-device copies are not supported. */ - __set_errno (EXDEV); - return -1; - } - } - - /* The output descriptor must not have O_APPEND set. */ - { - int flags = __fcntl (outfd, F_GETFL); - if (flags & O_APPEND) - { - __set_errno (EBADF); - return -1; - } - } - - /* Avoid an overflow in the result. */ - if (length > SSIZE_MAX) - length = SSIZE_MAX; - - /* Main copying loop. The buffer size is arbitrary and is a - trade-off between stack size consumption, cache usage, and - amortization of system call overhead. */ - size_t copied = 0; - char buf[8192]; - while (length > 0) - { - size_t to_read = length; - if (to_read > sizeof (buf)) - to_read = sizeof (buf); - - /* Fill the buffer. */ - ssize_t read_count; - if (pinoff == NULL) - read_count = read (infd, buf, to_read); - else - read_count = __libc_pread64 (infd, buf, to_read, *pinoff); - if (read_count == 0) - /* End of file reached prematurely. */ - return copied; - if (read_count < 0) - { - if (copied > 0) - /* Report the number of bytes copied so far. */ - return copied; - return -1; - } - if (pinoff != NULL) - *pinoff += read_count; - - /* Write the buffer part which was read to the destination. */ - char *end = buf + read_count; - for (char *p = buf; p < end; ) - { - ssize_t write_count; - if (poutoff == NULL) - write_count = write (outfd, p, end - p); - else - write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff); - if (write_count < 0) - { - /* Adjust the input read position to match what we have - written, so that the caller can pick up after the - error. */ - size_t written = p - buf; - /* NB: This needs to be signed so that we can form the - negative value below. */ - ssize_t overread = read_count - written; - if (pinoff == NULL) - { - if (overread > 0) - { - /* We are on an error recovery path, so we - cannot deal with failure here. */ - int save_errno = errno; - (void) __libc_lseek64 (infd, -overread, SEEK_CUR); - __set_errno (save_errno); - } - } - else /* pinoff != NULL */ - *pinoff -= overread; - - if (copied + written > 0) - /* Report the number of bytes copied so far. */ - return copied + written; - return -1; - } - p += write_count; - if (poutoff != NULL) - *poutoff += write_count; - } /* Write loop. */ - - copied += read_count; - length -= read_count; - } - return copied; -} diff --git a/io/copy_file_range.c b/io/copy_file_range.c index 7b968be19..59fb97977 100644 --- a/io/copy_file_range.c +++ b/io/copy_file_range.c @@ -1,4 +1,4 @@ -/* Generic implementation of copy_file_range. +/* Stub implementation of copy_file_range. Copyright (C) 2017-2019 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -16,7 +16,15 @@ License along with the GNU C Library; if not, see . */ -#define COPY_FILE_RANGE_DECL -#define COPY_FILE_RANGE copy_file_range +#include +#include -#include +ssize_t +copy_file_range (int infd, __off64_t *pinoff, + int outfd, __off64_t *poutoff, + size_t length, unsigned int flags) +{ + __set_errno (ENOSYS); + return -1; +} +stub_warning (copy_file_range) diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c index a5dcf3c1f..a9237cb38 100644 --- a/io/tst-copy_file_range.c +++ b/io/tst-copy_file_range.c @@ -20,22 +20,15 @@ #include #include #include -#include -#include -#include #include #include #include #include #include -#include #include #include #include #include -#ifdef CLONE_NEWNS -# include -#endif /* Boolean flags which indicate whether to use pointers with explicit output flags. */ @@ -49,10 +42,6 @@ static int infd; static char *outfile; static int outfd; -/* Like the above, but on a different file system. xdevfile can be - NULL if no suitable file system has been found. */ -static char *xdevfile; - /* Input and output offsets. Set according to do_inoff and do_outoff before the test. The offsets themselves are always set to zero. */ @@ -61,13 +50,10 @@ static off64_t *pinoff; static off64_t outoff; static off64_t *poutoff; -/* These are a collection of copy sizes used in tests. The selection - takes into account that the fallback implementation uses an - internal buffer of 8192 bytes. */ +/* These are a collection of copy sizes used in tests. */ enum { maximum_size = 99999 }; static const int typical_sizes[] = - { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385, - maximum_size }; + { 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, maximum_size }; /* The random contents of this array can be used as a pattern to check for correct write operations. */ @@ -76,101 +62,6 @@ static unsigned char random_data[maximum_size]; /* The size chosen by the test harness. */ static int current_size; -/* Maximum writable file offset. Updated by find_maximum_offset - below. */ -static off64_t maximum_offset; - -/* Error code when crossing the offset. */ -static int maximum_offset_errno; - -/* If true: Writes which cross the limit will fail. If false: Writes - which cross the limit will result in a partial write. */ -static bool maximum_offset_hard_limit; - -/* Fills maximum_offset etc. above. Truncates outfd as a side - effect. */ -static void -find_maximum_offset (void) -{ - xftruncate (outfd, 0); - if (maximum_offset != 0) - return; - - uint64_t upper = -1; - upper >>= 1; /* Maximum of off64_t. */ - TEST_VERIFY ((off64_t) upper > 0); - TEST_VERIFY ((off64_t) (upper + 1) < 0); - if (lseek64 (outfd, upper, SEEK_SET) >= 0) - { - if (write (outfd, "", 1) == 1) - FAIL_EXIT1 ("created a file larger than the off64_t range"); - } - - uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */ - /* Loop invariant: writing at lower succeeds, writing at upper fails. */ - while (lower + 1 < upper) - { - uint64_t middle = (lower + upper) / 2; - if (test_verbose > 0) - printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64 - ", probe at %" PRIu64 "\n", __func__, lower, upper, middle); - xftruncate (outfd, 0); - if (lseek64 (outfd, middle, SEEK_SET) >= 0 - && write (outfd, "", 1) == 1) - lower = middle; - else - upper = middle; - } - TEST_VERIFY (lower + 1 == upper); - maximum_offset = lower; - printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n", - lower, lower); - - /* Check that writing at the valid offset actually works. */ - xftruncate (outfd, 0); - xlseek (outfd, lower, SEEK_SET); - TEST_COMPARE (write (outfd, "", 1), 1); - - /* Cross the boundary with a two-byte write. This can either result - in a short write, or a failure. */ - xlseek (outfd, lower, SEEK_SET); - ssize_t ret = write (outfd, " ", 2); - if (ret < 0) - { - maximum_offset_errno = errno; - maximum_offset_hard_limit = true; - } - else - maximum_offset_hard_limit = false; - - /* Check that writing at the next offset actually fails. This also - obtains the expected errno value. */ - xftruncate (outfd, 0); - const char *action; - if (lseek64 (outfd, lower + 1, SEEK_SET) != 0) - { - if (write (outfd, "", 1) != -1) - FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded", - lower + 1); - action = "writing"; - int errno_copy = errno; - if (maximum_offset_hard_limit) - TEST_COMPARE (errno_copy, maximum_offset_errno); - else - maximum_offset_errno = errno_copy; - } - else - { - action = "seeking"; - maximum_offset_errno = errno; - } - printf ("info: %s out of range fails with %m (%d)\n", - action, maximum_offset_errno); - - xftruncate (outfd, 0); - xlseek (outfd, 0, SEEK_SET); -} - /* Perform a copy of a file. */ static void simple_file_copy (void) @@ -247,390 +138,6 @@ simple_file_copy (void) free (bytes); } -/* Test that reading from a pipe willfails. */ -static void -pipe_as_source (void) -{ - int pipefds[2]; - xpipe (pipefds); - - for (int length = 0; length < 2; ++length) - { - if (test_verbose > 0) - printf ("info: %s: length=%d\n", __func__, length); - - /* Make sure that there is something to copy in the pipe. */ - xwrite (pipefds[1], "@", 1); - - TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff, - length, 0), -1); - /* Linux 4.10 and later return EINVAL. Older kernels return - EXDEV. */ - TEST_VERIFY (errno == EINVAL || errno == EXDEV); - TEST_COMPARE (inoff, 0); - TEST_COMPARE (outoff, 0); - TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0); - - /* Make sure that nothing was read. */ - char buf = 'A'; - TEST_COMPARE (read (pipefds[0], &buf, 1), 1); - TEST_COMPARE (buf, '@'); - } - - xclose (pipefds[0]); - xclose (pipefds[1]); -} - -/* Test that writing to a pipe fails. */ -static void -pipe_as_destination (void) -{ - /* Make sure that there is something to read in the input file. */ - xwrite (infd, "abc", 3); - xlseek (infd, 0, SEEK_SET); - - int pipefds[2]; - xpipe (pipefds); - - for (int length = 0; length < 2; ++length) - { - if (test_verbose > 0) - printf ("info: %s: length=%d\n", __func__, length); - - TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff, - length, 0), -1); - /* Linux 4.10 and later return EINVAL. Older kernels return - EXDEV. */ - TEST_VERIFY (errno == EINVAL || errno == EXDEV); - TEST_COMPARE (inoff, 0); - TEST_COMPARE (outoff, 0); - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0); - - /* Make sure that nothing was written. */ - struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, }; - TEST_COMPARE (poll (&pollfd, 1, 0), 0); - } - - xclose (pipefds[0]); - xclose (pipefds[1]); -} - -/* Test a write failure after (potentially) writing some bytes. - Failure occurs near the start of the buffer. */ -static void -delayed_write_failure_beginning (void) -{ - /* We need to write something to provoke the error. */ - if (current_size == 0) - return; - xwrite (infd, random_data, sizeof (random_data)); - xlseek (infd, 0, SEEK_SET); - - /* Write failure near the start. The actual error code varies among - file systems. */ - find_maximum_offset (); - off64_t where = maximum_offset; - - if (current_size == 1) - ++where; - outoff = where; - if (do_outoff) - xlseek (outfd, 1, SEEK_SET); - else - xlseek (outfd, where, SEEK_SET); - if (maximum_offset_hard_limit || where > maximum_offset) - { - TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff, - sizeof (random_data), 0), -1); - TEST_COMPARE (errno, maximum_offset_errno); - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0); - TEST_COMPARE (inoff, 0); - if (do_outoff) - TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1); - else - TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where); - TEST_COMPARE (outoff, where); - struct stat64 st; - xfstat (outfd, &st); - TEST_COMPARE (st.st_size, 0); - } - else - { - /* The offset is not a hard limit. This means we write one - byte. */ - TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff, - sizeof (random_data), 0), 1); - if (do_inoff) - { - TEST_COMPARE (inoff, 1); - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0); - } - else - { - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1); - TEST_COMPARE (inoff, 0); - } - if (do_outoff) - { - TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1); - TEST_COMPARE (outoff, where + 1); - } - else - { - TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1); - TEST_COMPARE (outoff, where); - } - struct stat64 st; - xfstat (outfd, &st); - TEST_COMPARE (st.st_size, where + 1); - } -} - -/* Test a write failure after (potentially) writing some bytes. - Failure occurs near the end of the buffer. */ -static void -delayed_write_failure_end (void) -{ - if (current_size <= 1) - /* This would be same as the first test because there is not - enough data to write to make a difference. */ - return; - xwrite (infd, random_data, sizeof (random_data)); - xlseek (infd, 0, SEEK_SET); - - find_maximum_offset (); - off64_t where = maximum_offset - current_size + 1; - if (current_size == sizeof (random_data)) - /* Otherwise we do not reach the non-writable byte. */ - ++where; - outoff = where; - if (do_outoff) - xlseek (outfd, 1, SEEK_SET); - else - xlseek (outfd, where, SEEK_SET); - ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff, - sizeof (random_data), 0); - if (ret < 0) - { - TEST_COMPARE (ret, -1); - TEST_COMPARE (errno, maximum_offset_errno); - struct stat64 st; - xfstat (outfd, &st); - TEST_COMPARE (st.st_size, 0); - } - else - { - /* The first copy succeeded. This happens in the emulation - because the internal buffer of limited size does not - necessarily cross the off64_t boundary on the first write - operation. */ - if (test_verbose > 0) - printf ("info: copy_file_range (%zu) returned %zd\n", - sizeof (random_data), ret); - TEST_VERIFY (ret > 0); - TEST_VERIFY (ret < maximum_size); - struct stat64 st; - xfstat (outfd, &st); - TEST_COMPARE (st.st_size, where + ret); - if (do_inoff) - { - TEST_COMPARE (inoff, ret); - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0); - } - else - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret); - - char *buffer = xmalloc (ret); - TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret); - TEST_VERIFY (memcmp (buffer, random_data, ret) == 0); - free (buffer); - - /* The second copy fails. */ - TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff, - sizeof (random_data), 0), -1); - TEST_COMPARE (errno, maximum_offset_errno); - } -} - -/* Test a write failure across devices. */ -static void -cross_device_failure (void) -{ - if (xdevfile == NULL) - /* Subtest not supported due to missing cross-device file. */ - return; - - /* We need something to write. */ - xwrite (infd, random_data, sizeof (random_data)); - xlseek (infd, 0, SEEK_SET); - - int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0); - TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff, - current_size, 0), -1); - TEST_COMPARE (errno, EXDEV); - TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0); - struct stat64 st; - xfstat (xdevfd, &st); - TEST_COMPARE (st.st_size, 0); - - xclose (xdevfd); -} - -/* Try to exercise ENOSPC behavior with a tempfs file system (so that - we do not have to fill up a regular file system to get the error). - This function runs in a subprocess, so that we do not change the - mount namespace of the actual test process. */ -static void -enospc_failure_1 (void *closure) -{ -#ifdef CLONE_NEWNS - support_become_root (); - - /* Make sure that we do not alter the file system mounts of the - parents. */ - if (! support_enter_mount_namespace ()) - { - printf ("warning: ENOSPC test skipped\n"); - return; - } - - char *mountpoint = closure; - if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC, - "size=500k") != 0) - { - printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint); - return; - } - - /* The source file must reside on the same file system. */ - char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in"); - int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600); - xwrite (intmpfsfd, random_data, sizeof (random_data)); - xlseek (intmpfsfd, 1, SEEK_SET); - inoff = 1; - - char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out"); - int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600); - - /* Fill the file with data until ENOSPC is reached. */ - while (true) - { - ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data)); - if (ret < 0 && errno != ENOSPC) - FAIL_EXIT1 ("write to %s: %m", outtmpfsfile); - if (ret < sizeof (random_data)) - break; - } - TEST_COMPARE (write (outtmpfsfd, "", 1), -1); - TEST_COMPARE (errno, ENOSPC); - off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR); - TEST_VERIFY_EXIT (maxsize > sizeof (random_data)); - - /* Constructed the expected file contents. */ - char *expected = xmalloc (maxsize); - TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize); - /* Go back a little, so some bytes can be written. */ - enum { offset = 20000 }; - TEST_VERIFY_EXIT (offset < maxsize); - TEST_VERIFY_EXIT (offset < sizeof (random_data)); - memcpy (expected + maxsize - offset, random_data + 1, offset); - - if (do_outoff) - { - outoff = maxsize - offset; - xlseek (outtmpfsfd, 2, SEEK_SET); - } - else - xlseek (outtmpfsfd, -offset, SEEK_CUR); - - /* First call is expected to succeed because we made room for some - bytes. */ - TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff, - maximum_size, 0), offset); - if (do_inoff) - { - TEST_COMPARE (inoff, 1 + offset); - TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1); - } - else - TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset); - if (do_outoff) - { - TEST_COMPARE (outoff, maxsize); - TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2); - } - else - TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize); - struct stat64 st; - xfstat (outtmpfsfd, &st); - TEST_COMPARE (st.st_size, maxsize); - char *actual = xmalloc (st.st_size); - TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size); - TEST_VERIFY (memcmp (expected, actual, maxsize) == 0); - - /* Second call should fail with ENOSPC. */ - TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff, - maximum_size, 0), -1); - TEST_COMPARE (errno, ENOSPC); - - /* Offsets should be unchanged. */ - if (do_inoff) - { - TEST_COMPARE (inoff, 1 + offset); - TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1); - } - else - TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset); - if (do_outoff) - { - TEST_COMPARE (outoff, maxsize); - TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2); - } - else - TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize); - TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize); - TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize); - TEST_VERIFY (memcmp (expected, actual, maxsize) == 0); - - free (actual); - free (expected); - - xclose (intmpfsfd); - xclose (outtmpfsfd); - free (intmpfsfile); - free (outtmpfsfile); - -#else /* !CLONE_NEWNS */ - puts ("warning: ENOSPC test skipped (no mount namespaces)"); -#endif -} - -/* Call enospc_failure_1 in a subprocess. */ -static void -enospc_failure (void) -{ - char *mountpoint - = support_create_temp_directory ("tst-copy_file_range-enospc-"); - support_isolate_in_subprocess (enospc_failure_1, mountpoint); - free (mountpoint); -} - -/* The target file descriptor must have O_APPEND enabled. */ -static void -oappend_failure (void) -{ - /* Add data, to make sure we do not fail because there is - insufficient input data. */ - xwrite (infd, random_data, current_size); - xlseek (infd, 0, SEEK_SET); - - xclose (outfd); - outfd = xopen (outfile, O_RDWR | O_APPEND, 0); - TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff, - current_size, 0), -1); - TEST_COMPARE (errno, EBADF); -} - /* Test that a short input file results in a shortened copy. */ static void short_copy (void) @@ -721,14 +228,6 @@ struct test_case static struct test_case tests[] = { { "simple_file_copy", simple_file_copy, .sizes = true }, - { "pipe_as_source", pipe_as_source, }, - { "pipe_as_destination", pipe_as_destination, }, - { "delayed_write_failure_beginning", delayed_write_failure_beginning, - .sizes = true }, - { "delayed_write_failure_end", delayed_write_failure_end, .sizes = true }, - { "cross_device_failure", cross_device_failure, .sizes = true }, - { "enospc_failure", enospc_failure, }, - { "oappend_failure", oappend_failure, .sizes = true }, { "short_copy", short_copy, .sizes = true }, }; @@ -739,58 +238,18 @@ do_test (void) *p = rand () >> 24; infd = create_temp_file ("tst-copy_file_range-in-", &infile); + outfd = create_temp_file ("tst-copy_file_range-out-", &outfile); { - int outfd = create_temp_file ("tst-copy_file_range-out-", &outfile); - if (!support_descriptor_supports_holes (outfd)) - FAIL_UNSUPPORTED ("File %s does not support holes", outfile); - xclose (outfd); - } - - /* Try to find a different directory from the default input/output - file. */ - { - struct stat64 instat; - xfstat (infd, &instat); - static const char *const candidates[] = - { NULL, "/var/tmp", "/dev/shm" }; - for (const char *const *c = candidates; c < array_end (candidates); ++c) - { - const char *path = *c; - char *to_free = NULL; - if (path == NULL) - { - to_free = xreadlink ("/proc/self/exe"); - path = dirname (to_free); - } - - struct stat64 cstat; - xstat (path, &cstat); - if (cstat.st_dev == instat.st_dev) - { - free (to_free); - continue; - } - - printf ("info: using alternate temporary files directory: %s\n", path); - xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path); - free (to_free); - break; - } - if (xdevfile != NULL) + ssize_t ret = copy_file_range (infd, NULL, outfd, NULL, 0, 0); + if (ret != 0) { - int xdevfd = mkstemp (xdevfile); - if (xdevfd < 0) - FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile); - struct stat64 xdevst; - xfstat (xdevfd, &xdevst); - TEST_VERIFY (xdevst.st_dev != instat.st_dev); - add_temp_file (xdevfile); - xclose (xdevfd); + if (errno == ENOSYS) + FAIL_UNSUPPORTED ("copy_file_range is not support on this system"); + FAIL_EXIT1 ("copy_file_range probing call: %m"); } - else - puts ("warning: no alternate directory on different file system found"); } xclose (infd); + xclose (outfd); for (do_inoff = 0; do_inoff < 2; ++do_inoff) for (do_outoff = 0; do_outoff < 2; ++do_outoff) @@ -832,7 +291,6 @@ do_test (void) free (infile); free (outfile); - free (xdevfile); return 0; } diff --git a/libio/Makefile b/libio/Makefile index 5bee83e55..9626a16b0 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -65,7 +65,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \ tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \ tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \ - tst-sprintf-ub tst-sprintf-chk-ub + tst-sprintf-ub tst-sprintf-chk-ub tst-wfile-sync tests-internal = tst-vtables tst-vtables-interposed tst-readline @@ -73,6 +73,9 @@ ifeq (yes,$(build-shared)) # Add test-fopenloc only if shared library is enabled since it depends on # shared localedata objects. tests += tst-fopenloc +# Add tst-bz24228 only if shared library is enabled since it can never meet its +# objective with static linking because the relevant code just is not there. +tests += tst-bz24228 endif test-srcs = test-freopen @@ -157,11 +160,14 @@ CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\" CFLAGS-tst-sprintf-ub.c += -Wno-restrict CFLAGS-tst-sprintf-chk-ub.c += -Wno-restrict +LDFLAGS-tst-bz24228 = -Wl,--version-script=tst-bz24228.map + tst_wprintf2-ARGS = "Some Text" test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace +tst-bz24228-ENV = MALLOC_TRACE=$(objpfx)tst-bz24228.mtrace generated += test-fmemopen.mtrace test-fmemopen.check generated += tst-fopenloc.mtrace tst-fopenloc.check @@ -170,6 +176,7 @@ generated += tst-bz22415.mtrace tst-bz22415.check aux := fileops genops stdfiles stdio strops ifeq ($(build-shared),yes) +generated += tst-bz24228.mtrace tst-bz24228.check aux += oldfileops oldstdfiles endif @@ -184,7 +191,8 @@ tests-special += $(objpfx)test-freopen.out $(objpfx)test-fmemopen-mem.out \ ifeq (yes,$(build-shared)) # Run tst-fopenloc-cmp.out and tst-openloc-mem.out only if shared # library is enabled since they depend on tst-fopenloc.out. -tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out +tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out \ + $(objpfx)tst-bz24228-mem.out endif endif @@ -212,6 +220,7 @@ $(objpfx)tst-ungetwc1.out: $(gen-locales) $(objpfx)tst-ungetwc2.out: $(gen-locales) $(objpfx)tst-widetext.out: $(gen-locales) $(objpfx)tst_wprintf2.out: $(gen-locales) +$(objpfx)tst-wfile-sync.out: $(gen-locales) endif $(objpfx)test-freopen.out: test-freopen.sh $(objpfx)test-freopen @@ -235,3 +244,7 @@ $(objpfx)tst-fopenloc-mem.out: $(objpfx)tst-fopenloc.out $(objpfx)tst-bz22415-mem.out: $(objpfx)tst-bz22415.out $(common-objpfx)malloc/mtrace $(objpfx)tst-bz22415.mtrace > $@; \ $(evaluate-test) + +$(objpfx)tst-bz24228-mem.out: $(objpfx)tst-bz24228.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-bz24228.mtrace > $@; \ + $(evaluate-test) diff --git a/libio/genops.c b/libio/genops.c index 2a0d9b81d..11a15549e 100644 --- a/libio/genops.c +++ b/libio/genops.c @@ -789,9 +789,16 @@ _IO_unbuffer_all (void) for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain) { + int legacy = 0; + +#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) + if (__glibc_unlikely (_IO_vtable_offset (fp) != 0)) + legacy = 1; +#endif + if (! (fp->_flags & _IO_UNBUFFERED) /* Iff stream is un-orientated, it wasn't used. */ - && fp->_mode != 0) + && (legacy || fp->_mode != 0)) { #ifdef _IO_MTSAFE_IO int cnt; @@ -805,7 +812,7 @@ _IO_unbuffer_all (void) __sched_yield (); #endif - if (! dealloc_buffers && !(fp->_flags & _IO_USER_BUF)) + if (! legacy && ! dealloc_buffers && !(fp->_flags & _IO_USER_BUF)) { fp->_flags |= _IO_USER_BUF; @@ -816,7 +823,7 @@ _IO_unbuffer_all (void) _IO_SETBUF (fp, NULL, 0); - if (fp->_mode > 0) + if (! legacy && fp->_mode > 0) _IO_wsetb (fp, NULL, NULL, 0); #ifdef _IO_MTSAFE_IO @@ -827,7 +834,8 @@ _IO_unbuffer_all (void) /* Make sure that never again the wide char functions can be used. */ - fp->_mode = -1; + if (! legacy) + fp->_mode = -1; } #ifdef _IO_MTSAFE_IO diff --git a/libio/tst-bz24228.c b/libio/tst-bz24228.c new file mode 100644 index 000000000..6a74500d4 --- /dev/null +++ b/libio/tst-bz24228.c @@ -0,0 +1,29 @@ +/* BZ #24228 check for memory corruption in legacy libio + Copyright (C) 2019 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 + . */ + +#include +#include + +static int +do_test (void) +{ + mtrace (); + return 0; +} + +#include diff --git a/libio/tst-bz24228.map b/libio/tst-bz24228.map new file mode 100644 index 000000000..4383e0817 --- /dev/null +++ b/libio/tst-bz24228.map @@ -0,0 +1,5 @@ +# Hide the symbol from libc.so.6 to switch to the libio/oldfileops.c +# implementation when it is available for the architecture. +{ + local: _IO_stdin_used; +}; diff --git a/libio/tst-wfile-sync.c b/libio/tst-wfile-sync.c new file mode 100644 index 000000000..618682064 --- /dev/null +++ b/libio/tst-wfile-sync.c @@ -0,0 +1,39 @@ +/* Test that _IO_wfile_sync does not crash (bug 20568). + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + TEST_VERIFY_EXIT (setlocale (LC_ALL, "de_DE.UTF-8") != NULL); + /* Fill the stdio buffer and advance the read pointer. */ + TEST_VERIFY_EXIT (fgetwc (stdin) != WEOF); + /* This calls _IO_wfile_sync, it should not crash. */ + TEST_VERIFY_EXIT (setvbuf (stdin, NULL, _IONBF, 0) == 0); + /* Verify that the external file offset has been synchronized. */ + TEST_COMPARE (xlseek (0, 0, SEEK_CUR), 1); + + return 0; +} + +#include diff --git a/libio/tst-wfile-sync.input b/libio/tst-wfile-sync.input new file mode 100644 index 000000000..12d0958f7 --- /dev/null +++ b/libio/tst-wfile-sync.input @@ -0,0 +1 @@ +This is a test of _IO_wfile_sync. diff --git a/libio/wfileops.c b/libio/wfileops.c index 78f20486e..bab2ba489 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -508,11 +508,12 @@ _IO_wfile_sync (FILE *fp) generate the wide characters up to the current reading position. */ int nread; - + size_t wnread = (fp->_wide_data->_IO_read_ptr + - fp->_wide_data->_IO_read_base); fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state; nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state, fp->_IO_read_base, - fp->_IO_read_end, delta); + fp->_IO_read_end, wnread); fp->_IO_read_ptr = fp->_IO_read_base + nread; delta = -(fp->_IO_read_end - fp->_IO_read_base - nread); } diff --git a/localedata/locales/ja_JP b/localedata/locales/ja_JP index 1fd2fee44..c64aaaff5 100644 --- a/localedata/locales/ja_JP +++ b/localedata/locales/ja_JP @@ -14946,12 +14946,14 @@ am_pm "";"" t_fmt_ampm "%p%I%M%S" -era "+:2:1990//01//01:+*::%EC%Ey";/ +era "+:2:2020//01//01:+*::%EC%Ey";/ + "+:1:2019//05//01:2019//12//31::%EC";/ + "+:2:1990//01//01:2019//04//30::%EC%Ey";/ "+:1:1989//01//08:1989//12//31::%EC";/ "+:2:1927//01//01:1989//01//07::%EC%Ey";/ "+:1:1926//12//25:1926//12//31::%EC";/ "+:2:1913//01//01:1926//12//24::%EC%Ey";/ - "+:2:1912//07//30:1912//12//31::%EC";/ + "+:1:1912//07//30:1912//12//31::%EC";/ "+:6:1873//01//01:1912//07//29::%EC%Ey";/ "+:1:0001//01//01:1872//12//31::%EC%Ey";/ "+:1:-0001//12//31:-*::%EC%Ey" diff --git a/malloc/malloc.c b/malloc/malloc.c index feaf7ee0b..0abd653be 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -2919,6 +2919,8 @@ typedef struct tcache_perthread_struct tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; +#define MAX_TCACHE_COUNT 127 /* Maximum value of counts[] entries. */ + static __thread bool tcache_shutting_down = false; static __thread tcache_perthread_struct *tcache = NULL; @@ -3876,10 +3878,14 @@ _int_malloc (mstate av, size_t bytes) { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; + if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) + malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)"); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; + if (bck->fd != fwd) + malloc_printerr ("malloc(): largebin double linked list corrupted (bk)"); } } else @@ -5120,8 +5126,11 @@ static inline int __always_inline do_set_tcache_count (size_t value) { - LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count); - mp_.tcache_count = value; + if (value <= MAX_TCACHE_COUNT) + { + LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count); + mp_.tcache_count = value; + } return 1; } diff --git a/malloc/mtrace.c b/malloc/mtrace.c index a2facf65e..2fda26250 100644 --- a/malloc/mtrace.c +++ b/malloc/mtrace.c @@ -121,6 +121,41 @@ lock_and_info (const void *caller, Dl_info *mem) return res; } +static void tr_freehook (void *, const void *); +static void * tr_mallochook (size_t, const void *); +static void * tr_reallochook (void *, size_t, const void *); +static void * tr_memalignhook (size_t, size_t, const void *); + +/* Set all the default non-trace hooks. */ +static __always_inline void +set_default_hooks (void) +{ + __free_hook = tr_old_free_hook; + __malloc_hook = tr_old_malloc_hook; + __realloc_hook = tr_old_realloc_hook; + __memalign_hook = tr_old_memalign_hook; +} + +/* Set all of the tracing hooks used for mtrace. */ +static __always_inline void +set_trace_hooks (void) +{ + __free_hook = tr_freehook; + __malloc_hook = tr_mallochook; + __realloc_hook = tr_reallochook; + __memalign_hook = tr_memalignhook; +} + +/* Save the current set of hooks as the default hooks. */ +static __always_inline void +save_default_hooks (void) +{ + tr_old_free_hook = __free_hook; + tr_old_malloc_hook = __malloc_hook; + tr_old_realloc_hook = __realloc_hook; + tr_old_memalign_hook = __memalign_hook; +} + static void tr_freehook (void *ptr, const void *caller) { @@ -138,12 +173,12 @@ tr_freehook (void *ptr, const void *caller) tr_break (); __libc_lock_lock (lock); } - __free_hook = tr_old_free_hook; + set_default_hooks (); if (tr_old_free_hook != NULL) (*tr_old_free_hook)(ptr, caller); else free (ptr); - __free_hook = tr_freehook; + set_trace_hooks (); __libc_lock_unlock (lock); } @@ -155,12 +190,12 @@ tr_mallochook (size_t size, const void *caller) Dl_info mem; Dl_info *info = lock_and_info (caller, &mem); - __malloc_hook = tr_old_malloc_hook; + set_default_hooks (); if (tr_old_malloc_hook != NULL) hdr = (void *) (*tr_old_malloc_hook)(size, caller); else hdr = (void *) malloc (size); - __malloc_hook = tr_mallochook; + set_trace_hooks (); tr_where (caller, info); /* We could be printing a NULL here; that's OK. */ @@ -185,16 +220,12 @@ tr_reallochook (void *ptr, size_t size, const void *caller) Dl_info mem; Dl_info *info = lock_and_info (caller, &mem); - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; + set_default_hooks (); if (tr_old_realloc_hook != NULL) hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller); else hdr = (void *) realloc (ptr, size); - __free_hook = tr_freehook; - __malloc_hook = tr_mallochook; - __realloc_hook = tr_reallochook; + set_trace_hooks (); tr_where (caller, info); if (hdr == NULL) @@ -230,14 +261,12 @@ tr_memalignhook (size_t alignment, size_t size, const void *caller) Dl_info mem; Dl_info *info = lock_and_info (caller, &mem); - __memalign_hook = tr_old_memalign_hook; - __malloc_hook = tr_old_malloc_hook; + set_default_hooks (); if (tr_old_memalign_hook != NULL) hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller); else hdr = (void *) memalign (alignment, size); - __memalign_hook = tr_memalignhook; - __malloc_hook = tr_mallochook; + set_trace_hooks (); tr_where (caller, info); /* We could be printing a NULL here; that's OK. */ @@ -305,14 +334,8 @@ mtrace (void) malloc_trace_buffer = mtb; setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); fprintf (mallstream, "= Start\n"); - tr_old_free_hook = __free_hook; - __free_hook = tr_freehook; - tr_old_malloc_hook = __malloc_hook; - __malloc_hook = tr_mallochook; - tr_old_realloc_hook = __realloc_hook; - __realloc_hook = tr_reallochook; - tr_old_memalign_hook = __memalign_hook; - __memalign_hook = tr_memalignhook; + save_default_hooks (); + set_trace_hooks (); #ifdef _LIBC if (!added_atexit_handler) { @@ -338,10 +361,7 @@ muntrace (void) file. */ FILE *f = mallstream; mallstream = NULL; - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - __memalign_hook = tr_old_memalign_hook; + set_default_hooks (); fprintf (f, "= End\n"); fclose (f); diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c index 15e25f558..51d42cee8 100644 --- a/malloc/tst-malloc-too-large.c +++ b/malloc/tst-malloc-too-large.c @@ -72,13 +72,28 @@ test_large_allocations (size_t size) void * ptr_to_realloc; test_setup (); + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (malloc (size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); ptr_to_realloc = malloc (16); TEST_VERIFY_EXIT (ptr_to_realloc != NULL); test_setup (); +#if __GNUC_PREREQ (7, 0) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); free (ptr_to_realloc); @@ -135,7 +150,13 @@ test_large_aligned_allocations (size_t size) for (align = 1; align <= pagesize; align *= 2) { test_setup (); +#if __GNUC_PREREQ (7, 0) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (memalign (align, size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); /* posix_memalign expects an alignment that is a power of 2 *and* a @@ -151,7 +172,13 @@ test_large_aligned_allocations (size_t size) if ((size % align) == 0) { test_setup (); +#if __GNUC_PREREQ (7, 0) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (aligned_alloc (align, size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); } } @@ -159,11 +186,23 @@ test_large_aligned_allocations (size_t size) /* Both valloc and pvalloc return page-aligned memory. */ test_setup (); +#if __GNUC_PREREQ (7, 0) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (valloc (size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); test_setup (); +#if __GNUC_PREREQ (7, 0) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif TEST_VERIFY (pvalloc (size) == NULL); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif TEST_VERIFY (errno == ENOMEM); } diff --git a/malloc/tst-memalign.c b/malloc/tst-memalign.c index a6a9140a3..e7997518c 100644 --- a/malloc/tst-memalign.c +++ b/malloc/tst-memalign.c @@ -21,6 +21,7 @@ #include #include #include +#include static int errors = 0; @@ -41,9 +42,18 @@ do_test (void) errno = 0; + DIAG_PUSH_NEEDS_COMMENT; +#if __GNUC_PREREQ (7, 0) + /* GCC 7 warns about too-large allocations; here we want to test + that they fail. */ + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); +#endif /* An attempt to allocate a huge value should return NULL and set errno to ENOMEM. */ p = memalign (sizeof (void *), -1); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif save = errno; diff --git a/nptl/Makefile b/nptl/Makefile index 340282c6c..0e316edfa 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -319,7 +319,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \ tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \ - tst-rwlock-pwn + tst-rwlock-pwn \ + tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall tests-internal := tst-rwlock19 tst-rwlock20 \ tst-sem11 tst-sem12 tst-sem13 \ diff --git a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c index ecb78ffba..366feb376 100644 --- a/nptl/pthread_join_common.c +++ b/nptl/pthread_join_common.c @@ -86,6 +86,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return, pthread_cleanup_pop (0); } + void *pd_result = pd->result; if (__glibc_likely (result == 0)) { /* We mark the thread as terminated and as joined. */ @@ -93,7 +94,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return, /* Store the return value if the caller is interested. */ if (thread_return != NULL) - *thread_return = pd->result; + *thread_return = pd_result; /* Free the TCB. */ __free_tcb (pd); @@ -101,7 +102,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return, else pd->joinid = NULL; - LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd->result); + LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd_result); return result; } diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c index 8fe43b8f0..bf2869eca 100644 --- a/nptl/pthread_mutex_trylock.c +++ b/nptl/pthread_mutex_trylock.c @@ -94,6 +94,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP: THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, &mutex->__data.__list.__next); + /* We need to set op_pending before starting the operation. Also + see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); oldval = mutex->__data.__lock; do @@ -119,7 +122,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) /* But it is inconsistent unless marked otherwise. */ mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT; + /* We must not enqueue the mutex before we have acquired it. + Also see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); ENQUEUE_MUTEX (mutex); + /* We need to clear op_pending after we enqueue the mutex. */ + __asm ("" ::: "memory"); THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); /* Note that we deliberately exist here. If we fall @@ -135,6 +143,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) int kind = PTHREAD_MUTEX_TYPE (mutex); if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP) { + /* We do not need to ensure ordering wrt another memory + access. Also see comments at ENQUEUE_MUTEX. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return EDEADLK; @@ -142,6 +152,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP) { + /* We do not need to ensure ordering wrt another memory + access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); @@ -160,6 +172,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) id, 0); if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0) { + /* We haven't acquired the lock as it is already acquired by + another owner. We do not need to ensure ordering wrt another + memory access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return EBUSY; @@ -173,13 +188,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) if (oldval == id) lll_unlock (mutex->__data.__lock, PTHREAD_ROBUST_MUTEX_PSHARED (mutex)); + /* FIXME This violates the mutex destruction requirements. See + __pthread_mutex_unlock_full. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return ENOTRECOVERABLE; } } while ((oldval & FUTEX_OWNER_DIED) != 0); + /* We must not enqueue the mutex before we have acquired it. + Also see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); ENQUEUE_MUTEX (mutex); + /* We need to clear op_pending after we enqueue the mutex. */ + __asm ("" ::: "memory"); THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); mutex->__data.__owner = id; @@ -211,10 +233,15 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) } if (robust) - /* Note: robust PI futexes are signaled by setting bit 0. */ - THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, - (void *) (((uintptr_t) &mutex->__data.__list.__next) - | 1)); + { + /* Note: robust PI futexes are signaled by setting bit 0. */ + THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, + (void *) (((uintptr_t) &mutex->__data.__list.__next) + | 1)); + /* We need to set op_pending before starting the operation. Also + see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); + } oldval = mutex->__data.__lock; @@ -223,12 +250,16 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) { if (kind == PTHREAD_MUTEX_ERRORCHECK_NP) { + /* We do not need to ensure ordering wrt another memory + access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return EDEADLK; } if (kind == PTHREAD_MUTEX_RECURSIVE_NP) { + /* We do not need to ensure ordering wrt another memory + access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); /* Just bump the counter. */ @@ -250,6 +281,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) { if ((oldval & FUTEX_OWNER_DIED) == 0) { + /* We haven't acquired the lock as it is already acquired by + another owner. We do not need to ensure ordering wrt another + memory access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return EBUSY; @@ -270,6 +304,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) if (INTERNAL_SYSCALL_ERROR_P (e, __err) && INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK) { + /* The kernel has not yet finished the mutex owner death. + We do not need to ensure ordering wrt another memory + access. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return EBUSY; @@ -287,7 +324,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) /* But it is inconsistent unless marked otherwise. */ mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT; + /* We must not enqueue the mutex before we have acquired it. + Also see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); ENQUEUE_MUTEX (mutex); + /* We need to clear op_pending after we enqueue the mutex. */ + __asm ("" ::: "memory"); THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); /* Note that we deliberately exit here. If we fall @@ -310,13 +352,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex) PTHREAD_ROBUST_MUTEX_PSHARED (mutex)), 0, 0); + /* To the kernel, this will be visible after the kernel has + acquired the mutex in the syscall. */ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); return ENOTRECOVERABLE; } if (robust) { + /* We must not enqueue the mutex before we have acquired it. + Also see comments at ENQUEUE_MUTEX. */ + __asm ("" ::: "memory"); ENQUEUE_MUTEX_PI (mutex); + /* We need to clear op_pending after we enqueue the mutex. */ + __asm ("" ::: "memory"); THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL); } diff --git a/nptl/pthread_rwlock_tryrdlock.c b/nptl/pthread_rwlock_tryrdlock.c index 368862ff0..2f94f17f3 100644 --- a/nptl/pthread_rwlock_tryrdlock.c +++ b/nptl/pthread_rwlock_tryrdlock.c @@ -94,15 +94,22 @@ __pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) /* Same as in __pthread_rwlock_rdlock_full: We started the read phase, so we are also responsible for updating the write-phase futex. Relaxed MO is sufficient. - Note that there can be no other reader that we have to wake - because all other readers will see the read phase started by us - (or they will try to start it themselves); if a writer started - the read phase, we cannot have started it. Furthermore, we - cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will - overwrite the value set by the most recent writer (or the readers - before it in case of explicit hand-over) and we know that there - are no waiting readers. */ - atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0); + We have to do the same steps as a writer would when handing over the + read phase to use because other readers cannot distinguish between + us and the writer. + Note that __pthread_rwlock_tryrdlock callers will not have to be + woken up because they will either see the read phase started by us + or they will try to start it themselves; however, callers of + __pthread_rwlock_rdlock_full just increase the reader count and then + check what state the lock is in, so they cannot distinguish between + us and a writer that acquired and released the lock in the + meantime. */ + if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0) + & PTHREAD_RWLOCK_FUTEX_USED) != 0) + { + int private = __pthread_rwlock_get_private (rwlock); + futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private); + } } return 0; diff --git a/nptl/pthread_rwlock_trywrlock.c b/nptl/pthread_rwlock_trywrlock.c index fd37a71ce..fae475cc7 100644 --- a/nptl/pthread_rwlock_trywrlock.c +++ b/nptl/pthread_rwlock_trywrlock.c @@ -46,8 +46,15 @@ __pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) &rwlock->__data.__readers, &r, r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED)) { + /* We have become the primary writer and we cannot have shared + the PTHREAD_RWLOCK_FUTEX_USED flag with someone else, so we + can simply enable blocking (see full wrlock code). */ atomic_store_relaxed (&rwlock->__data.__writers_futex, 1); - atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); + /* If we started a write phase, we need to enable readers to + wait. If we did not, we must not change it because other threads + may have set the PTHREAD_RWLOCK_FUTEX_USED in the meantime. */ + if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) + atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); atomic_store_relaxed (&rwlock->__data.__cur_writer, THREAD_GETMEM (THREAD_SELF, tid)); return 0; diff --git a/nptl/register-atfork.c b/nptl/register-atfork.c index bc797b761..80a1becb5 100644 --- a/nptl/register-atfork.c +++ b/nptl/register-atfork.c @@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle) } void -__run_fork_handlers (enum __run_fork_handler_type who) +__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking) { struct fork_handler *runp; if (who == atfork_run_prepare) { - lll_lock (atfork_lock, LLL_PRIVATE); + if (do_locking) + lll_lock (atfork_lock, LLL_PRIVATE); size_t sl = fork_handler_list_size (&fork_handlers); for (size_t i = sl; i > 0; i--) { @@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_handler_type who) else if (who == atfork_run_parent && runp->parent_handler) runp->parent_handler (); } - lll_unlock (atfork_lock, LLL_PRIVATE); + if (do_locking) + lll_unlock (atfork_lock, LLL_PRIVATE); } } diff --git a/nptl/tst-rwlock-tryrdlock-stall.c b/nptl/tst-rwlock-tryrdlock-stall.c new file mode 100644 index 000000000..5e476da2b --- /dev/null +++ b/nptl/tst-rwlock-tryrdlock-stall.c @@ -0,0 +1,355 @@ +/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls. + Copyright (C) 2019 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 + . */ + +/* For a full analysis see comment: + https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14 + + Provided here for reference: + + --- Analysis of pthread_rwlock_tryrdlock() stall --- + A read lock begins to execute. + + In __pthread_rwlock_rdlock_full: + + We can attempt a read lock, but find that the lock is + in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit + is set), and the lock is held by a primary writer + (PTHREAD_RWLOCK_WRLOCKED is set). In this case we must + wait for explicit hand over from the writer to us or + one of the other waiters. The read lock threads are + about to execute: + + 341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers, + 342 (1 << PTHREAD_RWLOCK_READER_SHIFT)) + 343 + (1 << PTHREAD_RWLOCK_READER_SHIFT)); + + An unlock beings to execute. + + Then in __pthread_rwlock_wrunlock: + + 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers); + ... + 549 while (!atomic_compare_exchange_weak_release + 550 (&rwlock->__data.__readers, &r, + 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED) + 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0 + 553 : PTHREAD_RWLOCK_WRPHASE)))) + 554 { + ... + 556 } + + We clear PTHREAD_RWLOCK_WRLOCKED, and if there are + no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE. + + Back in the read lock. + + The read lock adjusts __readres as above. + + 383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0 + 384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0) + 385 { + ... + 390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r, + 391 r ^ PTHREAD_RWLOCK_WRPHASE)) + 392 { + + And then attemps to start the read phase. + + Assume there happens to be a tryrdlock at this point, noting + that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE + is 1. So the try lock attemps to start the read phase. + + In __pthread_rwlock_tryrdlock: + + 44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) + 45 { + ... + 49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0) + 50 && (rwlock->__data.__flags + 51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)) + 52 return EBUSY; + 53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT); + 54 } + ... + 89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, + 90 &r, rnew)); + + And succeeds. + + Back in the write unlock: + + 557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0) + 558 { + ... + 563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0) + 564 & PTHREAD_RWLOCK_FUTEX_USED) != 0) + 565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private); + 566 } + + We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero + and don't wake anyone. This is OK because we handed + over to the trylock. It will be the trylock's responsibility + to wake any waiters. + + Back in the read lock: + + The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because + the __readers value was adjusted by the trylock, and so it falls through + to waiting on the lock for explicit handover from either a new writer + or a new reader. + + 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, + 449 1 | PTHREAD_RWLOCK_FUTEX_USED, + 450 abstime, private); + + We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex + is in use. + + At this point we have readers waiting on the read lock + to unlock. The wrlock is done. The trylock is finishing + the installation of the read phase. + + 92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0) + 93 { + ... + 105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0); + 106 } + + The trylock does note that we were the one that + installed the read phase, but the comments are not + correct, the execution ordering above shows that + readers might indeed be waiting, and they are. + + The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED, + and the waiting reader is never worken becuase as noted + above it is conditional on the futex being used. + + The solution is for the trylock thread to inspect + PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers. + + --- Analysis of pthread_rwlock_trywrlock() stall --- + + A write lock begins to execute, takes the write lock, + and then releases the lock... + + In pthread_rwlock_wrunlock(): + + 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers); + ... + 549 while (!atomic_compare_exchange_weak_release + 550 (&rwlock->__data.__readers, &r, + 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED) + 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0 + 553 : PTHREAD_RWLOCK_WRPHASE)))) + 554 { + ... + 556 } + + ... leaving it in the write phase with zero readers + (the case where we leave the write phase in place + during a write unlock). + + A write trylock begins to execute. + + In __pthread_rwlock_trywrlock: + + 40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0) + 41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0) + 42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0)))) + 43 { + + The lock is not locked. + + There are no readers. + + 45 if (atomic_compare_exchange_weak_acquire ( + 46 &rwlock->__data.__readers, &r, + 47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED)) + + We atomically install the write phase and we take the + exclusive write lock. + + 48 { + 49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1); + + We get this far. + + A reader lock begins to execute. + + In pthread_rwlock_rdlock: + + 437 for (;;) + 438 { + 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) + 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) + 441 { + 442 int private = __pthread_rwlock_get_private (rwlock); + 443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) + 444 && (!atomic_compare_exchange_weak_relaxed + 445 (&rwlock->__data.__wrphase_futex, + 446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))) + 447 continue; + 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, + 449 1 | PTHREAD_RWLOCK_FUTEX_USED, + 450 abstime, private); + + We are in a write phase, so the while() on line 439 is true. + + The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set + since this is the first reader to lock. + + The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED + on the expectation that this reader will be woken during + the handoff. + + Back in pthread_rwlock_trywrlock: + + 50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); + 51 atomic_store_relaxed (&rwlock->__data.__cur_writer, + 52 THREAD_GETMEM (THREAD_SELF, tid)); + 53 return 0; + 54 } + ... + 57 } + + We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED, + and so in the unlock we will not awaken the waiting reader. + + The solution to this is to realize that if we did not start the write + phase we need not write 1 or any other value to __wrphase_futex. + This ensures that any readers (which saw __wrphase_futex != 0) can + set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to + wake them. + + If we installed the write phase then all other readers are looping + here: + + In __pthread_rwlock_rdlock_full: + + 437 for (;;) + 438 { + 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) + 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) + 441 { + ... + 508 } + + waiting for the write phase to be installed or removed before they + can begin waiting on __wrphase_futex (part of the algorithm), or + taking a concurrent read lock, and thus we can safely write 1 to + __wrphase_futex. + + If we did not install the write phase then the readers may already + be waiting on the futex, the original writer wrote 1 to __wrphase_futex + as part of starting the write phase, and we cannot also write 1 + without loosing the PTHREAD_RWLOCK_FUTEX_USED bit. + + --- + + Summary for the pthread_rwlock_tryrdlock() stall: + + The stall is caused by pthread_rwlock_tryrdlock failing to check + that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex + and then waking the futex. + + The fix for bug 23844 ensures that waiters on __wrphase_futex are + correctly woken. Before the fix the test stalls as readers can + wait forever on __wrphase_futex. */ + +#include +#include +#include +#include +#include +#include + +/* We need only one lock to reproduce the issue. We will need multiple + threads to get the exact case where we have a read, try, and unlock + all interleaving to produce the case where the readers are waiting + and the try fails to wake them. */ +pthread_rwlock_t onelock; + +/* The number of threads is arbitrary but empirically chosen to have + enough threads that we see the condition where waiting readers are + not woken by a successful tryrdlock. */ +#define NTHREADS 32 + +_Atomic int do_exit; + +void * +run_loop (void *arg) +{ + int i = 0, ret; + while (!do_exit) + { + /* Arbitrarily choose if we are the writer or reader. Choose a + high enough ratio of readers to writers to make it likely + that readers block (and eventually are susceptable to + stalling). + + If we are a writer, take the write lock, and then unlock. + If we are a reader, try the lock, then lock, then unlock. */ + if ((i % 8) != 0) + xpthread_rwlock_wrlock (&onelock); + else + { + if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0) + { + if (ret == EBUSY) + xpthread_rwlock_rdlock (&onelock); + else + exit (EXIT_FAILURE); + } + } + /* Thread does some work and then unlocks. */ + xpthread_rwlock_unlock (&onelock); + i++; + } + return NULL; +} + +int +do_test (void) +{ + int i; + pthread_t tids[NTHREADS]; + xpthread_rwlock_init (&onelock, NULL); + for (i = 0; i < NTHREADS; i++) + tids[i] = xpthread_create (NULL, run_loop, NULL); + /* Run for some amount of time. Empirically speaking exercising + the stall via pthread_rwlock_tryrdlock is much harder, and on + a 3.5GHz 4 core x86_64 VM system it takes somewhere around + 20-200s to stall, approaching 100% stall past 200s. We can't + wait that long for a regression test so we just test for 20s, + and expect the stall to happen with a 5-10% chance (enough for + developers to see). */ + sleep (20); + /* Then exit. */ + printf ("INFO: Exiting...\n"); + do_exit = 1; + /* If any readers stalled then we will timeout waiting for them. */ + for (i = 0; i < NTHREADS; i++) + xpthread_join (tids[i]); + printf ("INFO: Done.\n"); + xpthread_rwlock_destroy (&onelock); + printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n"); + return 0; +} + +#define TIMEOUT 30 +#include diff --git a/nptl/tst-rwlock-trywrlock-stall.c b/nptl/tst-rwlock-trywrlock-stall.c new file mode 100644 index 000000000..14d27cbcb --- /dev/null +++ b/nptl/tst-rwlock-trywrlock-stall.c @@ -0,0 +1,108 @@ +/* Bug 23844: Test for pthread_rwlock_trywrlock stalls. + Copyright (C) 2019 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 + . */ + +/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c. + + Summary for the pthread_rwlock_trywrlock() stall: + + The stall is caused by pthread_rwlock_trywrlock setting + __wrphase_futex futex to 1 and loosing the + PTHREAD_RWLOCK_FUTEX_USED bit. + + The fix for bug 23844 ensures that waiters on __wrphase_futex are + correctly woken. Before the fix the test stalls as readers can + wait forever on __wrphase_futex. */ + +#include +#include +#include +#include +#include +#include + +/* We need only one lock to reproduce the issue. We will need multiple + threads to get the exact case where we have a read, try, and unlock + all interleaving to produce the case where the readers are waiting + and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a + subsequent unlock fails to wake them. */ +pthread_rwlock_t onelock; + +/* The number of threads is arbitrary but empirically chosen to have + enough threads that we see the condition where waiting readers are + not woken by a successful unlock. */ +#define NTHREADS 32 + +_Atomic int do_exit; + +void * +run_loop (void *arg) +{ + int i = 0, ret; + while (!do_exit) + { + /* Arbitrarily choose if we are the writer or reader. Choose a + high enough ratio of readers to writers to make it likely + that readers block (and eventually are susceptable to + stalling). + + If we are a writer, take the write lock, and then unlock. + If we are a reader, try the lock, then lock, then unlock. */ + if ((i % 8) != 0) + { + if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0) + { + if (ret == EBUSY) + xpthread_rwlock_wrlock (&onelock); + else + exit (EXIT_FAILURE); + } + } + else + xpthread_rwlock_rdlock (&onelock); + /* Thread does some work and then unlocks. */ + xpthread_rwlock_unlock (&onelock); + i++; + } + return NULL; +} + +int +do_test (void) +{ + int i; + pthread_t tids[NTHREADS]; + xpthread_rwlock_init (&onelock, NULL); + for (i = 0; i < NTHREADS; i++) + tids[i] = xpthread_create (NULL, run_loop, NULL); + /* Run for some amount of time. The pthread_rwlock_tryrwlock stall + is very easy to trigger and happens in seconds under the test + conditions. */ + sleep (10); + /* Then exit. */ + printf ("INFO: Exiting...\n"); + do_exit = 1; + /* If any readers stalled then we will timeout waiting for them. */ + for (i = 0; i < NTHREADS; i++) + xpthread_join (tids[i]); + printf ("INFO: Done.\n"); + xpthread_rwlock_destroy (&onelock); + printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n"); + return 0; +} + +#include diff --git a/posix/bits/types.h b/posix/bits/types.h index 27e065c3b..0de6c59bb 100644 --- a/posix/bits/types.h +++ b/posix/bits/types.h @@ -87,7 +87,7 @@ __extension__ typedef unsigned long long int __uintmax_t; 32 -- "natural" 32-bit type (always int) 64 -- "natural" 64-bit type (long or long long) LONG32 -- 32-bit type, traditionally long - QUAD -- 64-bit type, always long long + QUAD -- 64-bit type, traditionally long long WORD -- natural type of __WORDSIZE bits (int or long) LONGWORD -- type of __WORDSIZE bits, traditionally long @@ -113,14 +113,14 @@ __extension__ typedef unsigned long long int __uintmax_t; #define __SLONGWORD_TYPE long int #define __ULONGWORD_TYPE unsigned long int #if __WORDSIZE == 32 -# define __SQUAD_TYPE __quad_t -# define __UQUAD_TYPE __u_quad_t +# define __SQUAD_TYPE __int64_t +# define __UQUAD_TYPE __uint64_t # define __SWORD_TYPE int # define __UWORD_TYPE unsigned int # define __SLONG32_TYPE long int # define __ULONG32_TYPE unsigned long int -# define __S64_TYPE __quad_t -# define __U64_TYPE __u_quad_t +# define __S64_TYPE __int64_t +# define __U64_TYPE __uint64_t /* We want __extension__ before typedef's that use nonstandard base types such as `long long' in C89 mode. */ # define __STD_TYPE __extension__ typedef diff --git a/posix/regexec.c b/posix/regexec.c index 91d5a797b..084b1222d 100644 --- a/posix/regexec.c +++ b/posix/regexec.c @@ -1293,8 +1293,10 @@ proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs, else if (naccepted) { char *buf = (char *) re_string_get_buffer (&mctx->input); - if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, - naccepted) != 0) + if (mctx->input.valid_len - *pidx < naccepted + || (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) + != 0)) return -1; } } diff --git a/posix/sys/types.h b/posix/sys/types.h index 27129c5c2..0e37b1ce6 100644 --- a/posix/sys/types.h +++ b/posix/sys/types.h @@ -154,37 +154,20 @@ typedef unsigned int uint; #include -#if !__GNUC_PREREQ (2, 7) - /* These were defined by ISO C without the first `_'. */ -typedef unsigned char u_int8_t; -typedef unsigned short int u_int16_t; -typedef unsigned int u_int32_t; -# if __WORDSIZE == 64 -typedef unsigned long int u_int64_t; -# else -__extension__ typedef unsigned long long int u_int64_t; -# endif - -typedef int register_t; - -#else - -/* For GCC 2.7 and later, we can use specific type-size attributes. */ -# define __u_intN_t(N, MODE) \ - typedef unsigned int u_int##N##_t __attribute__ ((__mode__ (MODE))) - -__u_intN_t (8, __QI__); -__u_intN_t (16, __HI__); -__u_intN_t (32, __SI__); -__u_intN_t (64, __DI__); +typedef __uint8_t u_int8_t; +typedef __uint16_t u_int16_t; +typedef __uint32_t u_int32_t; +typedef __uint64_t u_int64_t; +#if __GNUC_PREREQ (2, 7) typedef int register_t __attribute__ ((__mode__ (__word__))); - +#else +typedef int register_t; +#endif /* Some code from BIND tests this macro to see if the types above are defined. */ -#endif #define __BIT_TYPES_DEFINED__ 1 diff --git a/posix/tst-mmap-offset.c b/posix/tst-mmap-offset.c index 676c52845..d4e53c3e6 100644 --- a/posix/tst-mmap-offset.c +++ b/posix/tst-mmap-offset.c @@ -1,4 +1,4 @@ -/* BZ #18877 and #21270 mmap offset test. +/* BZ #18877, BZ #21270, and BZ #24699 mmap offset test. Copyright (C) 2015-2019 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -76,7 +77,7 @@ do_test_bz18877 (void) /* Check if invalid offset are handled correctly by mmap. */ static int -do_test_bz21270 (void) +do_test_large_offset (void) { /* For architectures with sizeof (off_t) < sizeof (off64_t) mmap is implemented with __SYS_mmap2 syscall and the offset is represented in @@ -90,7 +91,7 @@ do_test_bz21270 (void) const size_t length = 4096; void *addr = mmap64 (NULL, length, prot, flags, fd, offset); - if (sizeof (off_t) < sizeof (off64_t)) + if (mmap64_maximum_offset (page_shift) < UINT64_MAX) { if ((addr != MAP_FAILED) && (errno != EINVAL)) FAIL_RET ("mmap succeed"); @@ -110,7 +111,7 @@ do_test (void) int ret = 0; ret += do_test_bz18877 (); - ret += do_test_bz21270 (); + ret += do_test_large_offset (); return ret; } diff --git a/scripts/check-installed-headers.sh b/scripts/check-installed-headers.sh index 8e7beffd8..63bc8d4fa 100644 --- a/scripts/check-installed-headers.sh +++ b/scripts/check-installed-headers.sh @@ -16,11 +16,9 @@ # License along with the GNU C Library; if not, see # . -# Check installed headers for cleanliness. For each header, confirm -# that it's possible to compile a file that includes that header and -# does nothing else, in several different compilation modes. Also, -# scan the header for a set of obsolete typedefs that should no longer -# appear. +# For each installed header, confirm that it's possible to compile a +# file that includes that header and does nothing else, in several +# different compilation modes. # These compilation switches assume GCC or compatible, which is probably # fine since we also assume that when _building_ glibc. @@ -31,13 +29,6 @@ cxx_modes="-std=c++98 -std=gnu++98 -std=c++11 -std=gnu++11" # These are probably the most commonly used three. lib_modes="-D_DEFAULT_SOURCE=1 -D_GNU_SOURCE=1 -D_XOPEN_SOURCE=700" -# sys/types.h+bits/types.h have to define the obsolete types. -# rpc(svc)/* have the obsolete types too deeply embedded in their API -# to remove. -skip_obsolete_type_check='*/sys/types.h|*/bits/types.h|*/rpc/*|*/rpcsvc/*' -obsolete_type_re=\ -'\<((__)?(quad_t|u(short|int|long|_(char|short|int([0-9]+_t)?|long|quad_t))))\>' - if [ $# -lt 3 ]; then echo "usage: $0 c|c++ \"compile command\" header header header..." >&2 exit 2 @@ -46,14 +37,10 @@ case "$1" in (c) lang_modes="$c_modes" cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.c) - already="$skip_obsolete_type_check" ;; (c++) lang_modes="$cxx_modes" cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.cc) - # The obsolete-type check can be skipped for C++; it is - # sufficient to do it for C. - already="*" ;; (*) echo "usage: $0 c|c++ \"compile command\" header header header..." >&2 @@ -151,22 +138,8 @@ $expanded_lib_mode int avoid_empty_translation_unit; EOF if $cc_cmd -fsyntax-only $lang_mode "$cih_test_c" 2>&1 - then - includes=$($cc_cmd -fsyntax-only -H $lang_mode \ - "$cih_test_c" 2>&1 | sed -ne 's/^[.][.]* //p') - for h in $includes; do - # Don't repeat work. - eval 'case "$h" in ('"$already"') continue;; esac' - - if grep -qE "$obsolete_type_re" "$h"; then - echo "*** Obsolete types detected:" - grep -HE "$obsolete_type_re" "$h" - failed=1 - fi - already="$already|$h" - done - else - failed=1 + then : + else failed=1 fi done done diff --git a/scripts/check-obsolete-constructs.py b/scripts/check-obsolete-constructs.py new file mode 100644 index 000000000..ce5c72251 --- /dev/null +++ b/scripts/check-obsolete-constructs.py @@ -0,0 +1,466 @@ +#! /usr/bin/python3 +# Copyright (C) 2019 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 +# . + +"""Verifies that installed headers do not use any obsolete constructs: + * legacy BSD typedefs superseded by : + ushort uint ulong u_char u_short u_int u_long u_intNN_t quad_t u_quad_t + (sys/types.h is allowed to _define_ these types, but not to use them + to define anything else). +""" + +import argparse +import collections +import re +import sys + +# Simplified lexical analyzer for C preprocessing tokens. +# Does not implement trigraphs. +# Does not implement backslash-newline in the middle of any lexical +# item other than a string literal. +# Does not implement universal-character-names in identifiers. +# Treats prefixed strings (e.g. L"...") as two tokens (L and "...") +# Accepts non-ASCII characters only within comments and strings. + +# Caution: The order of the outermost alternation matters. +# STRING must be before BAD_STRING, CHARCONST before BAD_CHARCONST, +# BLOCK_COMMENT before BAD_BLOCK_COM before PUNCTUATOR, and OTHER must +# be last. +# Caution: There should be no capturing groups other than the named +# captures in the outermost alternation. + +# For reference, these are all of the C punctuators as of C11: +# [ ] ( ) { } , ; ? ~ +# ! != * *= / /= ^ ^= = == +# # ## +# % %= %> %: %:%: +# & &= && +# | |= || +# + += ++ +# - -= -- -> +# . ... +# : :> +# < <% <: << <<= <= +# > >= >> >>= + +# The BAD_* tokens are not part of the official definition of pp-tokens; +# they match unclosed strings, character constants, and block comments, +# so that the regex engine doesn't have to backtrack all the way to the +# beginning of a broken construct and then emit dozens of junk tokens. + +PP_TOKEN_RE_ = re.compile(r""" + (?P \"(?:[^\"\\\r\n]|\\(?:[\r\n -~]|\r\n))*\") + |(?P \"(?:[^\"\\\r\n]|\\[ -~])*) + |(?P \'(?:[^\'\\\r\n]|\\(?:[\r\n -~]|\r\n))*\') + |(?P \'(?:[^\'\\\r\n]|\\[ -~])*) + |(?P /\*(?:\*(?!/)|[^*])*\*/) + |(?P /\*(?:\*(?!/)|[^*])*\*?) + |(?P //[^\r\n]*) + |(?P [_a-zA-Z][_a-zA-Z0-9]*) + |(?P \.?[0-9](?:[0-9a-df-oq-zA-DF-OQ-Z_.]|[eEpP][+-]?)*) + |(?P + [,;?~(){}\[\]] + | [!*/^=]=? + | \#\#? + | %(?:[=>]|:(?:%:)?)? + | &[=&]? + |\|[=|]? + |\+[=+]? + | -[=->]? + |\.(?:\.\.)? + | :>? + | <(?:[%:]|<(?:=|<=?)?)? + | >(?:=|>=?)?) + |(?P \\(?:\r|\n|\r\n)) + |(?P [ \t\n\r\v\f]+) + |(?P .) +""", re.DOTALL | re.VERBOSE) + +HEADER_NAME_RE_ = re.compile(r""" + < [^>\r\n]+ > + | " [^"\r\n]+ " +""", re.DOTALL | re.VERBOSE) + +ENDLINE_RE_ = re.compile(r"""\r|\n|\r\n""") + +# based on the sample code in the Python re documentation +Token_ = collections.namedtuple("Token", ( + "kind", "text", "line", "column", "context")) +Token_.__doc__ = """ + One C preprocessing token, comment, or chunk of whitespace. + 'kind' identifies the token type, which will be one of: + STRING, CHARCONST, BLOCK_COMMENT, LINE_COMMENT, IDENT, + PP_NUMBER, PUNCTUATOR, ESCNL, WHITESPACE, HEADER_NAME, + or OTHER. The BAD_* alternatives in PP_TOKEN_RE_ are + handled within tokenize_c, below. + + 'text' is the sequence of source characters making up the token; + no decoding whatsoever is performed. + + 'line' and 'column' give the position of the first character of the + token within the source file. They are both 1-based. + + 'context' indicates whether or not this token occurred within a + preprocessing directive; it will be None for running text, + '' for the leading '#' of a directive line (because '#' + all by itself on a line is a "null directive"), or the name of + the directive for tokens within a directive line, starting with + the IDENT for the name itself. +""" + +def tokenize_c(file_contents, reporter): + """Yield a series of Token objects, one for each preprocessing + token, comment, or chunk of whitespace within FILE_CONTENTS. + The REPORTER object is expected to have one method, + reporter.error(token, message), which will be called to + indicate a lexical error at the position of TOKEN. + If MESSAGE contains the four-character sequence '{!r}', that + is expected to be replaced by repr(token.text). + """ + + Token = Token_ + PP_TOKEN_RE = PP_TOKEN_RE_ + ENDLINE_RE = ENDLINE_RE_ + HEADER_NAME_RE = HEADER_NAME_RE_ + + line_num = 1 + line_start = 0 + pos = 0 + limit = len(file_contents) + directive = None + at_bol = True + while pos < limit: + if directive == "include": + mo = HEADER_NAME_RE.match(file_contents, pos) + if mo: + kind = "HEADER_NAME" + directive = "after_include" + else: + mo = PP_TOKEN_RE.match(file_contents, pos) + kind = mo.lastgroup + if kind != "WHITESPACE": + directive = "after_include" + else: + mo = PP_TOKEN_RE.match(file_contents, pos) + kind = mo.lastgroup + + text = mo.group() + line = line_num + column = mo.start() - line_start + adj_line_start = 0 + # only these kinds can contain a newline + if kind in ("WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT", + "STRING", "CHARCONST", "BAD_BLOCK_COM", "ESCNL"): + for tmo in ENDLINE_RE.finditer(text): + line_num += 1 + adj_line_start = tmo.end() + if adj_line_start: + line_start = mo.start() + adj_line_start + + # Track whether or not we are scanning a preprocessing directive. + if kind == "LINE_COMMENT" or (kind == "WHITESPACE" and adj_line_start): + at_bol = True + directive = None + else: + if kind == "PUNCTUATOR" and text == "#" and at_bol: + directive = "" + elif kind == "IDENT" and directive == "": + directive = text + at_bol = False + + # Report ill-formed tokens and rewrite them as their well-formed + # equivalents, so downstream processing doesn't have to know about them. + # (Rewriting instead of discarding provides better error recovery.) + if kind == "BAD_BLOCK_COM": + reporter.error(Token("BAD_BLOCK_COM", "", line, column+1, ""), + "unclosed block comment") + text += "*/" + kind = "BLOCK_COMMENT" + elif kind == "BAD_STRING": + reporter.error(Token("BAD_STRING", "", line, column+1, ""), + "unclosed string") + text += "\"" + kind = "STRING" + elif kind == "BAD_CHARCONST": + reporter.error(Token("BAD_CHARCONST", "", line, column+1, ""), + "unclosed char constant") + text += "'" + kind = "CHARCONST" + + tok = Token(kind, text, line, column+1, + "include" if directive == "after_include" else directive) + # Do not complain about OTHER tokens inside macro definitions. + # $ and @ appear in macros defined by headers intended to be + # included from assembly language, e.g. sysdeps/mips/sys/asm.h. + if kind == "OTHER" and directive != "define": + self.error(tok, "stray {!r} in program") + + yield tok + pos = mo.end() + +# +# Base and generic classes for individual checks. +# + +class ConstructChecker: + """Scan a stream of C preprocessing tokens and possibly report + problems with them. The REPORTER object passed to __init__ has + one method, reporter.error(token, message), which should be + called to indicate a problem detected at the position of TOKEN. + If MESSAGE contains the four-character sequence '{!r}' then that + will be replaced with a textual representation of TOKEN. + """ + def __init__(self, reporter): + self.reporter = reporter + + def examine(self, tok): + """Called once for each token in a header file. + Call self.reporter.error if a problem is detected. + """ + raise NotImplementedError + + def eof(self): + """Called once at the end of the stream. Subclasses need only + override this if it might have something to do.""" + pass + +class NoCheck(ConstructChecker): + """Generic checker class which doesn't do anything. Substitute this + class for a real checker when a particular check should be skipped + for some file.""" + + def examine(self, tok): + pass + +# +# Check for obsolete type names. +# + +# The obsolete type names we're looking for: +OBSOLETE_TYPE_RE_ = re.compile(r"""\A + (__)? + ( quad_t + | u(?: short | int | long + | _(?: char | short | int(?:[0-9]+_t)? | long | quad_t ))) +\Z""", re.VERBOSE) + +class ObsoleteNotAllowed(ConstructChecker): + """Don't allow any use of the obsolete typedefs.""" + def examine(self, tok): + if OBSOLETE_TYPE_RE_.match(tok.text): + self.reporter.error(tok, "use of {!r}") + +class ObsoletePrivateDefinitionsAllowed(ConstructChecker): + """Allow definitions of the private versions of the + obsolete typedefs; that is, 'typedef [anything] __obsolete;' + """ + def __init__(self, reporter): + super().__init__(reporter) + self.in_typedef = False + self.prev_token = None + + def examine(self, tok): + # bits/types.h hides 'typedef' in a macro sometimes. + if (tok.kind == "IDENT" + and tok.text in ("typedef", "__STD_TYPE") + and tok.context is None): + self.in_typedef = True + elif tok.kind == "PUNCTUATOR" and tok.text == ";" and self.in_typedef: + self.in_typedef = False + if self.prev_token.kind == "IDENT": + m = OBSOLETE_TYPE_RE_.match(self.prev_token.text) + if m and m.group(1) != "__": + self.reporter.error(self.prev_token, "use of {!r}") + self.prev_token = None + else: + self._check_prev() + + self.prev_token = tok + + def eof(self): + self._check_prev() + + def _check_prev(self): + if (self.prev_token is not None + and self.prev_token.kind == "IDENT" + and OBSOLETE_TYPE_RE_.match(self.prev_token.text)): + self.reporter.error(self.prev_token, "use of {!r}") + +class ObsoletePublicDefinitionsAllowed(ConstructChecker): + """Allow definitions of the public versions of the obsolete + typedefs. Only specific forms of definition are allowed: + + typedef __obsolete obsolete; // identifiers must agree + typedef __uintN_t u_intN_t; // N must agree + typedef unsigned long int ulong; + typedef unsigned short int ushort; + typedef unsigned int uint; + """ + def __init__(self, reporter): + super().__init__(reporter) + self.typedef_tokens = [] + + def examine(self, tok): + if tok.kind in ("WHITESPACE", "BLOCK_COMMENT", + "LINE_COMMENT", "NL", "ESCNL"): + pass + + elif (tok.kind == "IDENT" and tok.text == "typedef" + and tok.context is None): + if self.typedef_tokens: + self.reporter.error(tok, "typedef inside typedef") + self._reset() + self.typedef_tokens.append(tok) + + elif tok.kind == "PUNCTUATOR" and tok.text == ";": + self._finish() + + elif self.typedef_tokens: + self.typedef_tokens.append(tok) + + def eof(self): + self._reset() + + def _reset(self): + while self.typedef_tokens: + tok = self.typedef_tokens.pop(0) + if tok.kind == "IDENT" and OBSOLETE_TYPE_RE_.match(tok.text): + self.reporter.error(tok, "use of {!r}") + + def _finish(self): + if not self.typedef_tokens: return + if self.typedef_tokens[-1].kind == "IDENT": + m = OBSOLETE_TYPE_RE_.match(self.typedef_tokens[-1].text) + if m: + if self._permissible_public_definition(m): + self.typedef_tokens.clear() + self._reset() + + def _permissible_public_definition(self, m): + if m.group(1) == "__": return False + name = m.group(2) + toks = self.typedef_tokens + ntok = len(toks) + if ntok == 3 and toks[1].kind == "IDENT": + defn = toks[1].text + n = OBSOLETE_TYPE_RE_.match(defn) + if n and n.group(1) == "__" and n.group(2) == name: + return True + + if (name[:5] == "u_int" and name[-2:] == "_t" + and defn[:6] == "__uint" and defn[-2:] == "_t" + and name[5:-2] == defn[6:-2]): + return True + + return False + + if (name == "ulong" and ntok == 5 + and toks[1].kind == "IDENT" and toks[1].text == "unsigned" + and toks[2].kind == "IDENT" and toks[2].text == "long" + and toks[3].kind == "IDENT" and toks[3].text == "int"): + return True + + if (name == "ushort" and ntok == 5 + and toks[1].kind == "IDENT" and toks[1].text == "unsigned" + and toks[2].kind == "IDENT" and toks[2].text == "short" + and toks[3].kind == "IDENT" and toks[3].text == "int"): + return True + + if (name == "uint" and ntok == 4 + and toks[1].kind == "IDENT" and toks[1].text == "unsigned" + and toks[2].kind == "IDENT" and toks[2].text == "int"): + return True + + return False + +def ObsoleteTypedefChecker(reporter, fname): + """Factory: produce an instance of the appropriate + obsolete-typedef checker for FNAME.""" + + # The obsolete rpc/ and rpcsvc/ headers are allowed to use the + # obsolete types, because it would be more trouble than it's + # worth to remove them from headers that we intend to stop + # installing eventually anyway. + if (fname.startswith("rpc/") + or fname.startswith("rpcsvc/") + or "/rpc/" in fname + or "/rpcsvc/" in fname): + return NoCheck(reporter) + + # bits/types.h is allowed to define the __-versions of the + # obsolete types. + if (fname == "bits/types.h" + or fname.endswith("/bits/types.h")): + return ObsoletePrivateDefinitionsAllowed(reporter) + + # sys/types.h is allowed to use the __-versions of the + # obsolete types, but only to define the unprefixed versions. + if (fname == "sys/types.h" + or fname.endswith("/sys/types.h")): + return ObsoletePublicDefinitionsAllowed(reporter) + + return ObsoleteNotAllowed(reporter) + +# +# Master control +# + +class HeaderChecker: + """Perform all of the checks on each header. This is also the + "reporter" object expected by tokenize_c and ConstructChecker. + """ + def __init__(self): + self.fname = None + self.status = 0 + + def error(self, tok, message): + self.status = 1 + if '{!r}' in message: + message = message.format(tok.text) + sys.stderr.write("{}:{}:{}: error: {}\n".format( + self.fname, tok.line, tok.column, message)) + + def check(self, fname): + self.fname = fname + try: + with open(fname, "rt") as fp: + contents = fp.read() + except OSError as e: + sys.stderr.write("{}: {}\n".format(fname, e.strerror)) + self.status = 1 + return + + typedef_checker = ObsoleteTypedefChecker(self, self.fname) + + for tok in tokenize_c(contents, self): + typedef_checker.examine(tok) + +def main(): + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("headers", metavar="header", nargs="+", + help="one or more headers to scan for obsolete constructs") + args = ap.parse_args() + + checker = HeaderChecker() + for fname in args.headers: + # Headers whose installed name begins with "finclude/" contain + # Fortran, not C, and this program should completely ignore them. + if not (fname.startswith("finclude/") or "/finclude/" in fname): + checker.check(fname) + sys.exit(checker.status) + +main() diff --git a/string/memmem.c b/string/memmem.c index 4bf733f1f..83ee75e8c 100644 --- a/string/memmem.c +++ b/string/memmem.c @@ -15,17 +15,13 @@ License along with the GNU C Library; if not, see . */ -/* This particular implementation was written by Eric Blake, 2008. */ - #ifndef _LIBC # include #endif -/* Specification of memmem. */ #include #ifndef _LIBC -# define __builtin_expect(expr, val) (expr) # define __memmem memmem #endif @@ -36,51 +32,98 @@ #undef memmem -/* Return the first occurrence of NEEDLE in HAYSTACK. Return HAYSTACK - if NEEDLE_LEN is 0, otherwise NULL if NEEDLE is not found in - HAYSTACK. */ +/* Hash character pairs so a small shift table can be used. All bits of + p[0] are included, but not all bits from p[-1]. So if two equal hashes + match on p[-1], p[0] matches too. Hash collisions are harmless and result + in smaller shifts. */ +#define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift)) + +/* Fast memmem algorithm with guaranteed linear-time performance. + Small needles up to size 2 use a dedicated linear search. Longer needles + up to size 256 use a novel modified Horspool algorithm. It hashes pairs + of characters to quickly skip past mismatches. The main search loop only + exits if the last 2 characters match, avoiding unnecessary calls to memcmp + and allowing for a larger skip if there is no match. A self-adapting + filtering check is used to quickly detect mismatches in long needles. + By limiting the needle length to 256, the shift table can be reduced to 8 + bits per entry, lowering preprocessing overhead and minimizing cache effects. + The limit also implies worst-case performance is linear. + Needles larger than 256 characters use the linear-time Two-Way algorithm. */ void * -__memmem (const void *haystack_start, size_t haystack_len, - const void *needle_start, size_t needle_len) +__memmem (const void *haystack, size_t hs_len, + const void *needle, size_t ne_len) { - /* Abstract memory is considered to be an array of 'unsigned char' values, - not an array of 'char' values. See ISO C 99 section 6.2.6.1. */ - const unsigned char *haystack = (const unsigned char *) haystack_start; - const unsigned char *needle = (const unsigned char *) needle_start; - - if (needle_len == 0) - /* The first occurrence of the empty string is deemed to occur at - the beginning of the string. */ - return (void *) haystack; - - /* Sanity check, otherwise the loop might search through the whole - memory. */ - if (__glibc_unlikely (haystack_len < needle_len)) + const unsigned char *hs = (const unsigned char *) haystack; + const unsigned char *ne = (const unsigned char *) needle; + + if (ne_len == 0) + return (void *) hs; + if (ne_len == 1) + return (void *) memchr (hs, ne[0], hs_len); + + /* Ensure haystack length is >= needle length. */ + if (hs_len < ne_len) return NULL; - /* Use optimizations in memchr when possible, to reduce the search - size of haystack using a linear algorithm with a smaller - coefficient. However, avoid memchr for long needles, since we - can often achieve sublinear performance. */ - if (needle_len < LONG_NEEDLE_THRESHOLD) + const unsigned char *end = hs + hs_len - ne_len; + + if (ne_len == 2) + { + uint32_t nw = ne[0] << 16 | ne[1], hw = hs[0] << 16 | hs[1]; + for (hs++; hs <= end && hw != nw; ) + hw = hw << 16 | *++hs; + return hw == nw ? (void *)hs - 1 : NULL; + } + + /* Use Two-Way algorithm for very long needles. */ + if (__builtin_expect (ne_len > 256, 0)) + return two_way_long_needle (hs, hs_len, ne, ne_len); + + uint8_t shift[256]; + size_t tmp, shift1; + size_t m1 = ne_len - 1; + size_t offset = 0; + + memset (shift, 0, sizeof (shift)); + for (int i = 1; i < m1; i++) + shift[hash2 (ne + i)] = i; + /* Shift1 is the amount we can skip after matching the hash of the + needle end but not the full needle. */ + shift1 = m1 - shift[hash2 (ne + m1)]; + shift[hash2 (ne + m1)] = m1; + + for ( ; hs <= end; ) { - haystack = memchr (haystack, *needle, haystack_len); - if (!haystack || __builtin_expect (needle_len == 1, 0)) - return (void *) haystack; - haystack_len -= haystack - (const unsigned char *) haystack_start; - if (haystack_len < needle_len) - return NULL; - /* Check whether we have a match. This improves performance since we - avoid the initialization overhead of the two-way algorithm. */ - if (memcmp (haystack, needle, needle_len) == 0) - return (void *) haystack; - return two_way_short_needle (haystack, haystack_len, needle, needle_len); + /* Skip past character pairs not in the needle. */ + do + { + hs += m1; + tmp = shift[hash2 (hs)]; + } + while (tmp == 0 && hs <= end); + + /* If the match is not at the end of the needle, shift to the end + and continue until we match the hash of the needle end. */ + hs -= tmp; + if (tmp < m1) + continue; + + /* Hash of the last 2 characters matches. If the needle is long, + try to quickly filter out mismatches. */ + if (m1 < 15 || memcmp (hs + offset, ne + offset, 8) == 0) + { + if (memcmp (hs, ne, m1) == 0) + return (void *) hs; + + /* Adjust filter offset when it doesn't find the mismatch. */ + offset = (offset >= 8 ? offset : m1) - 8; + } + + /* Skip based on matching the hash of the needle end. */ + hs += shift1; } - else - return two_way_long_needle (haystack, haystack_len, needle, needle_len); + return NULL; } libc_hidden_def (__memmem) weak_alias (__memmem, memmem) libc_hidden_weak (memmem) - -#undef LONG_NEEDLE_THRESHOLD diff --git a/string/str-two-way.h b/string/str-two-way.h index b5011baaf..f43c613f5 100644 --- a/string/str-two-way.h +++ b/string/str-two-way.h @@ -221,7 +221,7 @@ critical_factorization (const unsigned char *needle, size_t needle_len, most 2 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching. If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching. */ -static RETURN_TYPE +static inline RETURN_TYPE two_way_short_needle (const unsigned char *haystack, size_t haystack_len, const unsigned char *needle, size_t needle_len) { @@ -382,8 +382,11 @@ two_way_short_needle (const unsigned char *haystack, size_t haystack_len, and sublinear performance O(HAYSTACK_LEN / NEEDLE_LEN) is possible. If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching, and - sublinear performance is not possible. */ -static RETURN_TYPE + sublinear performance is not possible. + + Since this function is large and complex, block inlining to avoid + slowing down the common case of small needles. */ +__attribute__((noinline)) static RETURN_TYPE two_way_long_needle (const unsigned char *haystack, size_t haystack_len, const unsigned char *needle, size_t needle_len) { diff --git a/string/strstr.c b/string/strstr.c index 64e478b9e..408cce8ed 100644 --- a/string/strstr.c +++ b/string/strstr.c @@ -16,29 +16,17 @@ License along with the GNU C Library; if not, see . */ -/* This particular implementation was written by Eric Blake, 2008. */ - #ifndef _LIBC # include #endif -/* Specification of strstr. */ #include -#include - -#ifndef _LIBC -# define __builtin_expect(expr, val) (expr) -#endif - #define RETURN_TYPE char * #define AVAILABLE(h, h_l, j, n_l) \ (((j) + (n_l) <= (h_l)) \ || ((h_l) += __strnlen ((void*)((h) + (h_l)), (n_l) + 512), \ (j) + (n_l) <= (h_l))) -#define CHECK_EOL (1) -#define RET0_IF_0(a) if (!a) goto ret0 -#define FASTSEARCH(S,C,N) (void*) strchr ((void*)(S), (C)) #include "str-two-way.h" #undef strstr @@ -47,47 +35,128 @@ #define STRSTR strstr #endif -/* Return the first occurrence of NEEDLE in HAYSTACK. Return HAYSTACK - if NEEDLE is empty, otherwise NULL if NEEDLE is not found in - HAYSTACK. */ -char * -STRSTR (const char *haystack, const char *needle) +static inline char * +strstr2 (const unsigned char *hs, const unsigned char *ne) { - size_t needle_len; /* Length of NEEDLE. */ - size_t haystack_len; /* Known minimum length of HAYSTACK. */ - - /* Handle empty NEEDLE special case. */ - if (needle[0] == '\0') - return (char *) haystack; + uint32_t h1 = (ne[0] << 16) | ne[1]; + uint32_t h2 = 0; + for (int c = hs[0]; h1 != h2 && c != 0; c = *++hs) + h2 = (h2 << 16) | c; + return h1 == h2 ? (char *)hs - 2 : NULL; +} - /* Skip until we find the first matching char from NEEDLE. */ - haystack = strchr (haystack, needle[0]); - if (haystack == NULL || needle[1] == '\0') - return (char *) haystack; +static inline char * +strstr3 (const unsigned char *hs, const unsigned char *ne) +{ + uint32_t h1 = ((uint32_t)ne[0] << 24) | (ne[1] << 16) | (ne[2] << 8); + uint32_t h2 = 0; + for (int c = hs[0]; h1 != h2 && c != 0; c = *++hs) + h2 = (h2 | c) << 8; + return h1 == h2 ? (char *)hs - 3 : NULL; +} - /* Ensure HAYSTACK length is at least as long as NEEDLE length. - Since a match may occur early on in a huge HAYSTACK, use strnlen +/* Hash character pairs so a small shift table can be used. All bits of + p[0] are included, but not all bits from p[-1]. So if two equal hashes + match on p[-1], p[0] matches too. Hash collisions are harmless and result + in smaller shifts. */ +#define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift)) + +/* Fast strstr algorithm with guaranteed linear-time performance. + Small needles up to size 3 use a dedicated linear search. Longer needles + up to size 256 use a novel modified Horspool algorithm. It hashes pairs + of characters to quickly skip past mismatches. The main search loop only + exits if the last 2 characters match, avoiding unnecessary calls to memcmp + and allowing for a larger skip if there is no match. A self-adapting + filtering check is used to quickly detect mismatches in long needles. + By limiting the needle length to 256, the shift table can be reduced to 8 + bits per entry, lowering preprocessing overhead and minimizing cache effects. + The limit also implies worst-case performance is linear. + Needles larger than 256 characters use the linear-time Two-Way algorithm. */ +char * +STRSTR (const char *haystack, const char *needle) +{ + const unsigned char *hs = (const unsigned char *) haystack; + const unsigned char *ne = (const unsigned char *) needle; + + /* Handle short needle special cases first. */ + if (ne[0] == '\0') + return (char *)hs; + hs = (const unsigned char *)strchr ((const char*)hs, ne[0]); + if (hs == NULL || ne[1] == '\0') + return (char*)hs; + if (ne[2] == '\0') + return strstr2 (hs, ne); + if (ne[3] == '\0') + return strstr3 (hs, ne); + + /* Ensure haystack length is at least as long as needle length. + Since a match may occur early on in a huge haystack, use strnlen and read ahead a few cachelines for improved performance. */ - needle_len = strlen (needle); - haystack_len = __strnlen (haystack, needle_len + 256); - if (haystack_len < needle_len) + size_t ne_len = strlen ((const char*)ne); + size_t hs_len = __strnlen ((const char*)hs, ne_len | 512); + if (hs_len < ne_len) return NULL; - /* Check whether we have a match. This improves performance since we avoid - the initialization overhead of the two-way algorithm. */ - if (memcmp (haystack, needle, needle_len) == 0) - return (char *) haystack; - - /* Perform the search. Abstract memory is considered to be an array - of 'unsigned char' values, not an array of 'char' values. See - ISO C 99 section 6.2.6.1. */ - if (needle_len < LONG_NEEDLE_THRESHOLD) - return two_way_short_needle ((const unsigned char *) haystack, - haystack_len, - (const unsigned char *) needle, needle_len); - return two_way_long_needle ((const unsigned char *) haystack, haystack_len, - (const unsigned char *) needle, needle_len); + /* Check whether we have a match. This improves performance since we + avoid initialization overheads. */ + if (memcmp (hs, ne, ne_len) == 0) + return (char *) hs; + + /* Use Two-Way algorithm for very long needles. */ + if (__glibc_unlikely (ne_len > 256)) + return two_way_long_needle (hs, hs_len, ne, ne_len); + + const unsigned char *end = hs + hs_len - ne_len; + uint8_t shift[256]; + size_t tmp, shift1; + size_t m1 = ne_len - 1; + size_t offset = 0; + + /* Initialize bad character shift hash table. */ + memset (shift, 0, sizeof (shift)); + for (int i = 1; i < m1; i++) + shift[hash2 (ne + i)] = i; + /* Shift1 is the amount we can skip after matching the hash of the + needle end but not the full needle. */ + shift1 = m1 - shift[hash2 (ne + m1)]; + shift[hash2 (ne + m1)] = m1; + + while (1) + { + if (__glibc_unlikely (hs > end)) + { + end += __strnlen ((const char*)end + m1 + 1, 2048); + if (hs > end) + return NULL; + } + + /* Skip past character pairs not in the needle. */ + do + { + hs += m1; + tmp = shift[hash2 (hs)]; + } + while (tmp == 0 && hs <= end); + + /* If the match is not at the end of the needle, shift to the end + and continue until we match the hash of the needle end. */ + hs -= tmp; + if (tmp < m1) + continue; + + /* Hash of the last 2 characters matches. If the needle is long, + try to quickly filter out mismatches. */ + if (m1 < 15 || memcmp (hs + offset, ne + offset, 8) == 0) + { + if (memcmp (hs, ne, m1) == 0) + return (void *) hs; + + /* Adjust filter offset when it doesn't find the mismatch. */ + offset = (offset >= 8 ? offset : m1) - 8; + } + + /* Skip based on matching the hash of the needle end. */ + hs += shift1; + } } libc_hidden_builtin_def (strstr) - -#undef LONG_NEEDLE_THRESHOLD diff --git a/support/Makefile b/support/Makefile index 432cf2fe6..8d61de6c5 100644 --- a/support/Makefile +++ b/support/Makefile @@ -63,6 +63,7 @@ libsupport-routines = \ support_record_failure \ support_run_diff \ support_shared_allocate \ + support_subprocess \ support_test_compare_blob \ support_test_compare_failure \ support_test_compare_string \ @@ -129,6 +130,7 @@ libsupport-routines = \ xpthread_mutexattr_settype \ xpthread_once \ xpthread_rwlock_init \ + xpthread_rwlock_destroy \ xpthread_rwlock_rdlock \ xpthread_rwlock_unlock \ xpthread_rwlock_wrlock \ @@ -147,6 +149,9 @@ libsupport-routines = \ xsignal \ xsigstack \ xsocket \ + xposix_spawn \ + xposix_spawn_file_actions_addclose \ + xposix_spawn_file_actions_adddup2 \ xstrdup \ xstrndup \ xsymlink \ @@ -222,4 +227,6 @@ endif $(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so +tst-support_capture_subprocess-ARGS = -- $(host-test-program-cmd) + include ../Rules diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h index 2dce42e3a..2832cfc63 100644 --- a/support/capture_subprocess.h +++ b/support/capture_subprocess.h @@ -35,6 +35,12 @@ struct support_capture_subprocess struct support_capture_subprocess support_capture_subprocess (void (*callback) (void *), void *closure); +/* Issue FILE with ARGV arguments by using posix_spawn and capture standard + output, standard error, and the exit status. The out.buffer and err.buffer + are handle as support_capture_subprocess. */ +struct support_capture_subprocess support_capture_subprogram + (const char *file, char *const argv[]); + /* Deallocate the subprocess data captured by support_capture_subprocess. */ void support_capture_subprocess_free (struct support_capture_subprocess *); diff --git a/support/subprocess.h b/support/subprocess.h new file mode 100644 index 000000000..c031878d9 --- /dev/null +++ b/support/subprocess.h @@ -0,0 +1,49 @@ +/* Create a subprocess. + Copyright (C) 2019 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 + . */ + +#ifndef SUPPORT_SUBPROCESS_H +#define SUPPORT_SUBPROCESS_H + +#include + +struct support_subprocess +{ + int stdout_pipe[2]; + int stderr_pipe[2]; + pid_t pid; +}; + +/* Invoke CALLBACK (CLOSURE) in a subprocess created with fork and return + its PID, a pipe redirected to STDOUT, and a pipe redirected to STDERR. */ +struct support_subprocess support_subprocess + (void (*callback) (void *), void *closure); + +/* Issue FILE with ARGV arguments by using posix_spawn and return is PID, a + pipe redirected to STDOUT, and a pipe redirected to STDERR. */ +struct support_subprocess support_subprogram + (const char *file, char *const argv[]); + +/* Wait for the subprocess indicated by PROC::PID. Return the status + indicate by waitpid call. */ +int support_process_wait (struct support_subprocess *proc); + +/* Terminate the subprocess indicated by PROC::PID, first with a SIGTERM and + then with a SIGKILL. Return the status as for waitpid call. */ +int support_process_terminate (struct support_subprocess *proc); + +#endif diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c index 167514faf..948ce5a0c 100644 --- a/support/support_capture_subprocess.c +++ b/support/support_capture_subprocess.c @@ -16,6 +16,7 @@ License along with the GNU C Library; if not, see . */ +#include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include static void transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream) @@ -50,59 +52,53 @@ transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream) } } -struct support_capture_subprocess -support_capture_subprocess (void (*callback) (void *), void *closure) +static void +support_capture_poll (struct support_capture_subprocess *result, + struct support_subprocess *proc) { - struct support_capture_subprocess result; - xopen_memstream (&result.out); - xopen_memstream (&result.err); - - int stdout_pipe[2]; - xpipe (stdout_pipe); - TEST_VERIFY (stdout_pipe[0] > STDERR_FILENO); - TEST_VERIFY (stdout_pipe[1] > STDERR_FILENO); - int stderr_pipe[2]; - xpipe (stderr_pipe); - TEST_VERIFY (stderr_pipe[0] > STDERR_FILENO); - TEST_VERIFY (stderr_pipe[1] > STDERR_FILENO); - - TEST_VERIFY (fflush (stdout) == 0); - TEST_VERIFY (fflush (stderr) == 0); - - pid_t pid = xfork (); - if (pid == 0) - { - xclose (stdout_pipe[0]); - xclose (stderr_pipe[0]); - xdup2 (stdout_pipe[1], STDOUT_FILENO); - xdup2 (stderr_pipe[1], STDERR_FILENO); - xclose (stdout_pipe[1]); - xclose (stderr_pipe[1]); - callback (closure); - _exit (0); - } - xclose (stdout_pipe[1]); - xclose (stderr_pipe[1]); - struct pollfd fds[2] = { - { .fd = stdout_pipe[0], .events = POLLIN }, - { .fd = stderr_pipe[0], .events = POLLIN }, + { .fd = proc->stdout_pipe[0], .events = POLLIN }, + { .fd = proc->stderr_pipe[0], .events = POLLIN }, }; do { xpoll (fds, 2, -1); - transfer ("stdout", &fds[0], &result.out); - transfer ("stderr", &fds[1], &result.err); + transfer ("stdout", &fds[0], &result->out); + transfer ("stderr", &fds[1], &result->err); } while (fds[0].events != 0 || fds[1].events != 0); - xclose (stdout_pipe[0]); - xclose (stderr_pipe[0]); - xfclose_memstream (&result.out); - xfclose_memstream (&result.err); - xwaitpid (pid, &result.status, 0); + xfclose_memstream (&result->out); + xfclose_memstream (&result->err); + + result->status = support_process_wait (proc); +} + +struct support_capture_subprocess +support_capture_subprocess (void (*callback) (void *), void *closure) +{ + struct support_capture_subprocess result; + xopen_memstream (&result.out); + xopen_memstream (&result.err); + + struct support_subprocess proc = support_subprocess (callback, closure); + + support_capture_poll (&result, &proc); + return result; +} + +struct support_capture_subprocess +support_capture_subprogram (const char *file, char *const argv[]) +{ + struct support_capture_subprocess result; + xopen_memstream (&result.out); + xopen_memstream (&result.err); + + struct support_subprocess proc = support_subprogram (file, argv); + + support_capture_poll (&result, &proc); return result; } diff --git a/support/support_subprocess.c b/support/support_subprocess.c new file mode 100644 index 000000000..0c8cc6af3 --- /dev/null +++ b/support/support_subprocess.c @@ -0,0 +1,152 @@ +/* Create subprocess. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct support_subprocess +support_suprocess_init (void) +{ + struct support_subprocess result; + + xpipe (result.stdout_pipe); + TEST_VERIFY (result.stdout_pipe[0] > STDERR_FILENO); + TEST_VERIFY (result.stdout_pipe[1] > STDERR_FILENO); + + xpipe (result.stderr_pipe); + TEST_VERIFY (result.stderr_pipe[0] > STDERR_FILENO); + TEST_VERIFY (result.stderr_pipe[1] > STDERR_FILENO); + + TEST_VERIFY (fflush (stdout) == 0); + TEST_VERIFY (fflush (stderr) == 0); + + return result; +} + +struct support_subprocess +support_subprocess (void (*callback) (void *), void *closure) +{ + struct support_subprocess result = support_suprocess_init (); + + result.pid = xfork (); + if (result.pid == 0) + { + xclose (result.stdout_pipe[0]); + xclose (result.stderr_pipe[0]); + xdup2 (result.stdout_pipe[1], STDOUT_FILENO); + xdup2 (result.stderr_pipe[1], STDERR_FILENO); + xclose (result.stdout_pipe[1]); + xclose (result.stderr_pipe[1]); + callback (closure); + _exit (0); + } + xclose (result.stdout_pipe[1]); + xclose (result.stderr_pipe[1]); + + return result; +} + +struct support_subprocess +support_subprogram (const char *file, char *const argv[]) +{ + struct support_subprocess result = support_suprocess_init (); + + posix_spawn_file_actions_t fa; + /* posix_spawn_file_actions_init does not fail. */ + posix_spawn_file_actions_init (&fa); + + xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[0]); + xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[0]); + xposix_spawn_file_actions_adddup2 (&fa, result.stdout_pipe[1], STDOUT_FILENO); + xposix_spawn_file_actions_adddup2 (&fa, result.stderr_pipe[1], STDERR_FILENO); + xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]); + xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]); + + result.pid = xposix_spawn (file, &fa, NULL, argv, NULL); + + xclose (result.stdout_pipe[1]); + xclose (result.stderr_pipe[1]); + + return result; +} + +int +support_process_wait (struct support_subprocess *proc) +{ + xclose (proc->stdout_pipe[0]); + xclose (proc->stderr_pipe[0]); + + int status; + xwaitpid (proc->pid, &status, 0); + return status; +} + + +static bool +support_process_kill (int pid, int signo, int *status) +{ + /* Kill the whole process group. */ + kill (-pid, signo); + /* In case setpgid failed in the child, kill it individually too. */ + kill (pid, signo); + + /* Wait for it to terminate. */ + pid_t killed; + for (int i = 0; i < 5; ++i) + { + int status; + killed = xwaitpid (pid, &status, WNOHANG|WUNTRACED); + if (killed != 0) + break; + + /* Delay, give the system time to process the kill. If the + nanosleep() call return prematurely, all the better. We + won't restart it since this probably means the child process + finally died. */ + nanosleep (&((struct timespec) { 0, 100000000 }), NULL); + } + if (killed != 0 && killed != pid) + return false; + + return true; +} + +int +support_process_terminate (struct support_subprocess *proc) +{ + xclose (proc->stdout_pipe[0]); + xclose (proc->stderr_pipe[0]); + + int status; + pid_t killed = xwaitpid (proc->pid, &status, WNOHANG|WUNTRACED); + if (killed != 0 && killed == proc->pid) + return status; + + /* Subprocess is still running, terminate it. */ + if (!support_process_kill (proc->pid, SIGTERM, &status) ) + support_process_kill (proc->pid, SIGKILL, &status); + + return status; +} diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c index d8ba42ea8..ab363e41a 100644 --- a/support/tst-support_capture_subprocess.c +++ b/support/tst-support_capture_subprocess.c @@ -23,8 +23,20 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include + +/* Nonzero if the program gets called via 'exec'. */ +static int restart; + +/* Hold the four initial argument used to respawn the process. */ +static char *initial_argv[5]; /* Write one byte at *P to FD and advance *P. Do nothing if *P is '\0'. */ @@ -42,6 +54,30 @@ transfer (const unsigned char **p, int fd) enum write_mode { out_first, err_first, interleave, write_mode_last = interleave }; +static const char * +write_mode_to_str (enum write_mode mode) +{ + switch (mode) + { + case out_first: return "out_first"; + case err_first: return "err_first"; + case interleave: return "interleave"; + default: return "write_mode_last"; + } +} + +static enum write_mode +str_to_write_mode (const char *mode) +{ + if (strcmp (mode, "out_first") == 0) + return out_first; + else if (strcmp (mode, "err_first") == 0) + return err_first; + else if (strcmp (mode, "interleave") == 0) + return interleave; + return write_mode_last; +} + /* Describe what to write in the subprocess. */ struct test { @@ -52,11 +88,9 @@ struct test int status; }; -/* For use with support_capture_subprocess. */ -static void -callback (void *closure) +_Noreturn static void +test_common (const struct test *test) { - const struct test *test = closure; bool mode_ok = false; switch (test->write_mode) { @@ -95,6 +129,40 @@ callback (void *closure) exit (test->status); } +static int +parse_int (const char *str) +{ + char *endptr; + long int ret = strtol (str, &endptr, 10); + TEST_COMPARE (errno, 0); + TEST_VERIFY (ret >= 0 && ret <= INT_MAX); + return ret; +} + +/* For use with support_capture_subprogram. */ +_Noreturn static void +handle_restart (char *out, char *err, const char *write_mode, + const char *signal, const char *status) +{ + struct test test = + { + out, + err, + str_to_write_mode (write_mode), + parse_int (signal), + parse_int (status) + }; + test_common (&test); +} + +/* For use with support_capture_subprocess. */ +_Noreturn static void +callback (void *closure) +{ + const struct test *test = closure; + test_common (test); +} + /* Create a heap-allocated random string of letters. */ static char * random_string (size_t length) @@ -130,12 +198,59 @@ check_stream (const char *what, const struct xmemstream *stream, } } +static struct support_capture_subprocess +do_subprocess (struct test *test) +{ + return support_capture_subprocess (callback, test); +} + +static struct support_capture_subprocess +do_subprogram (const struct test *test) +{ + /* Three digits per byte plus null terminator. */ + char signalstr[3 * sizeof(int) + 1]; + snprintf (signalstr, sizeof (signalstr), "%d", test->signal); + char statusstr[3 * sizeof(int) + 1]; + snprintf (statusstr, sizeof (statusstr), "%d", test->status); + + int argc = 0; + enum { + /* 4 elements from initial_argv (path to ld.so, '--library-path', the + path', and application name'), 2 for restart argument ('--direct', + '--restart'), 5 arguments plus NULL. */ + argv_size = 12 + }; + char *args[argv_size]; + + for (char **arg = initial_argv; *arg != NULL; arg++) + args[argc++] = *arg; + + args[argc++] = (char*) "--direct"; + args[argc++] = (char*) "--restart"; + + args[argc++] = test->out; + args[argc++] = test->err; + args[argc++] = (char*) write_mode_to_str (test->write_mode); + args[argc++] = signalstr; + args[argc++] = statusstr; + args[argc] = NULL; + TEST_VERIFY (argc < argv_size); + + return support_capture_subprogram (args[0], args); +} + +enum test_type +{ + subprocess, + subprogram, +}; + static int -do_test (void) +do_multiple_tests (enum test_type type) { const int lengths[] = {0, 1, 17, 512, 20000, -1}; - /* Test multiple combinations of support_capture_subprocess. + /* Test multiple combinations of support_capture_sub{process,program}. length_idx_stdout: Index into the lengths array above, controls how many bytes are written by the subprocess to @@ -164,8 +279,10 @@ do_test (void) TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]); TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]); - struct support_capture_subprocess result - = support_capture_subprocess (callback, &test); + struct support_capture_subprocess result + = type == subprocess ? do_subprocess (&test) + : do_subprogram (&test); + check_stream ("stdout", &result.out, test.out); check_stream ("stderr", &result.err, test.err); @@ -199,4 +316,54 @@ do_test (void) return 0; } +static int +do_test (int argc, char *argv[]) +{ + /* We must have either: + + - one or four parameters if called initially: + + argv[1]: path for ld.so optional + + argv[2]: "--library-path" optional + + argv[3]: the library path optional + + argv[4]: the application name + + - six parameters left if called through re-execution: + + argv[1]: the application name + + argv[2]: the stdout to print + + argv[3]: the stderr to print + + argv[4]: the write mode to use + + argv[5]: the signal to issue + + argv[6]: the exit status code to use + + * When built with --enable-hardcoded-path-in-tests or issued without + using the loader directly. + */ + + if (argc != (restart ? 6 : 5) && argc != (restart ? 6 : 2)) + FAIL_EXIT1 ("wrong number of arguments (%d)", argc); + + if (restart) + { + handle_restart (argv[1], /* stdout */ + argv[2], /* stderr */ + argv[3], /* write_mode */ + argv[4], /* signal */ + argv[5]); /* status */ + } + + initial_argv[0] = argv[1]; /* path for ld.so */ + initial_argv[1] = argv[2]; /* "--library-path" */ + initial_argv[2] = argv[3]; /* the library path */ + initial_argv[3] = argv[4]; /* the application name */ + initial_argv[4] = NULL; + + do_multiple_tests (subprocess); + do_multiple_tests (subprogram); + + return 0; +} + +#define CMDLINE_OPTIONS \ + { "restart", no_argument, &restart, 1 }, +#define TEST_FUNCTION_ARGV do_test #include diff --git a/support/xposix_spawn.c b/support/xposix_spawn.c new file mode 100644 index 000000000..e84601763 --- /dev/null +++ b/support/xposix_spawn.c @@ -0,0 +1,32 @@ +/* xposix_spawn implementation. + Copyright (C) 2019 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 + . */ + +#include +#include + +pid_t +xposix_spawn (const char *file, const posix_spawn_file_actions_t *fa, + const posix_spawnattr_t *attr, char *const args[], + char *const envp[]) +{ + pid_t pid; + int status = posix_spawn (&pid, file, fa, attr, args, envp); + if (status != 0) + FAIL_EXIT1 ("posix_spawn to %s file failed: %m", file); + return pid; +} diff --git a/support/xposix_spawn_file_actions_addclose.c b/support/xposix_spawn_file_actions_addclose.c new file mode 100644 index 000000000..eed54a651 --- /dev/null +++ b/support/xposix_spawn_file_actions_addclose.c @@ -0,0 +1,29 @@ +/* xposix_spawn_file_actions_addclose implementation. + Copyright (C) 2019 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 + . */ + +#include +#include + +int +xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *fa, int fd) +{ + int status = posix_spawn_file_actions_addclose (fa, fd); + if (status == -1) + FAIL_EXIT1 ("posix_spawn_file_actions_addclose failed: %m\n"); + return status; +} diff --git a/io/tst-copy_file_range-compat.c b/support/xposix_spawn_file_actions_adddup2.c similarity index 57% rename from io/tst-copy_file_range-compat.c rename to support/xposix_spawn_file_actions_adddup2.c index fe6de8ac6..a43b6490b 100644 --- a/io/tst-copy_file_range-compat.c +++ b/support/xposix_spawn_file_actions_adddup2.c @@ -1,5 +1,5 @@ -/* Test the fallback implementation of copy_file_range. - Copyright (C) 2017-2019 Free Software Foundation, Inc. +/* xposix_spawn_file_actions_adddup2 implementation. + Copyright (C) 2019 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 @@ -16,15 +16,15 @@ License along with the GNU C Library; if not, see . */ -/* Get the declaration of the official copy_of_range function. */ -#include +#include +#include -/* Compile a local version of copy_file_range. */ -#define COPY_FILE_RANGE_DECL static -#define COPY_FILE_RANGE copy_file_range_compat -#include - -/* Re-use the test, but run it against copy_file_range_compat defined - above. */ -#define copy_file_range copy_file_range_compat -#include "tst-copy_file_range.c" +int +xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *fa, int fd, + int newfd) +{ + int status = posix_spawn_file_actions_adddup2 (fa, fd, newfd); + if (status == -1) + FAIL_EXIT1 ("posix_spawn_file_actions_adddup2 failed: %m\n"); + return status; +} diff --git a/support/xpthread_rwlock_destroy.c b/support/xpthread_rwlock_destroy.c new file mode 100644 index 000000000..6d6e95356 --- /dev/null +++ b/support/xpthread_rwlock_destroy.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_destroy with error checking. + Copyright (C) 2019 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 + . */ + +#include + +void +xpthread_rwlock_destroy (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_destroy", + pthread_rwlock_destroy (rwlock)); +} diff --git a/support/xspawn.h b/support/xspawn.h new file mode 100644 index 000000000..bbf89132e --- /dev/null +++ b/support/xspawn.h @@ -0,0 +1,34 @@ +/* posix_spawn with support checks. + Copyright (C) 2019 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 + . */ + +#ifndef SUPPORT_XSPAWN_H +#define SUPPORT_XSPAWN_H + +#include + +__BEGIN_DECLS + +int xposix_spawn_file_actions_addclose (posix_spawn_file_actions_t *, int); +int xposix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *, int, int); + +pid_t xposix_spawn (const char *, const posix_spawn_file_actions_t *, + const posix_spawnattr_t *, char *const [], char *const []); + +__END_DECLS + +#endif diff --git a/support/xthread.h b/support/xthread.h index 47c23235f..9fe1f68b3 100644 --- a/support/xthread.h +++ b/support/xthread.h @@ -84,6 +84,7 @@ void xpthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref); void xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock); void xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock); void xpthread_rwlock_unlock (pthread_rwlock_t *rwlock); +void xpthread_rwlock_destroy (pthread_rwlock_t *rwlock); __END_DECLS diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h index 823eefba4..d3e87d6dc 100644 --- a/sysdeps/aarch64/dl-machine.h +++ b/sysdeps/aarch64/dl-machine.h @@ -388,10 +388,37 @@ elf_machine_lazy_rel (struct link_map *map, /* Check for unexpected PLT reloc type. */ if (__builtin_expect (r_type == AARCH64_R(JUMP_SLOT), 1)) { - if (__builtin_expect (map->l_mach.plt, 0) == 0) - *reloc_addr += l_addr; - else - *reloc_addr = map->l_mach.plt; + if (map->l_mach.plt == 0) + { + /* Prelinking. */ + *reloc_addr += l_addr; + return; + } + + if (1) /* DT_AARCH64_VARIANT_PCS is not available, so always check. */ + { + /* Check the symbol table for variant PCS symbols. */ + const Elf_Symndx symndx = ELFW (R_SYM) (reloc->r_info); + const ElfW (Sym) *symtab = + (const void *)D_PTR (map, l_info[DT_SYMTAB]); + const ElfW (Sym) *sym = &symtab[symndx]; + if (__glibc_unlikely (sym->st_other & STO_AARCH64_VARIANT_PCS)) + { + /* Avoid lazy resolution of variant PCS symbols. */ + const struct r_found_version *version = NULL; + if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL) + { + const ElfW (Half) *vernum = + (const void *)D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]); + version = &map->l_versions[vernum[symndx] & 0x7fff]; + } + elf_machine_rela (map, reloc, sym, version, reloc_addr, + skip_ifunc); + return; + } + } + + *reloc_addr = map->l_mach.plt; } else if (__builtin_expect (r_type == AARCH64_R(TLSDESC), 1)) { diff --git a/sysdeps/alpha/fpu/libm-test-ulps b/sysdeps/alpha/fpu/libm-test-ulps index 0f77dd2ed..89c4527a8 100644 --- a/sysdeps/alpha/fpu/libm-test-ulps +++ b/sysdeps/alpha/fpu/libm-test-ulps @@ -1084,10 +1084,10 @@ ildouble: 1 ldouble: 1 Function: Real part of "cpow_downward": -double: 2 -float: 4 -idouble: 2 -ifloat: 4 +double: 5 +float: 8 +idouble: 5 +ifloat: 8 ildouble: 6 ldouble: 6 @@ -1100,10 +1100,10 @@ ildouble: 2 ldouble: 2 Function: Real part of "cpow_towardzero": -double: 2 -float: 4 -idouble: 2 -ifloat: 4 +double: 5 +float: 8 +idouble: 5 +ifloat: 8 ildouble: 6 ldouble: 6 diff --git a/sysdeps/arm/stap-probe-machine.h b/sysdeps/arm/stap-probe-machine.h new file mode 100644 index 000000000..d27ca2204 --- /dev/null +++ b/sysdeps/arm/stap-probe-machine.h @@ -0,0 +1,22 @@ +/* Macros for customizing Systemtap . Arm version. + Copyright (C) 2019 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 + . */ + +/* The default "nor" constraint produces unparseable memory references + for constants. Omit the problematic "o" constraint. See bug 24164 + and GCC PR 89146. */ +#define STAP_SDT_ARG_CONSTRAINT nr diff --git a/sysdeps/generic/mmap_info.h b/sysdeps/generic/mmap_info.h new file mode 100644 index 000000000..b3087df2d --- /dev/null +++ b/sysdeps/generic/mmap_info.h @@ -0,0 +1,16 @@ +/* As default architectures with sizeof (off_t) < sizeof (off64_t) the mmap is + implemented with __SYS_mmap2 syscall and the offset is represented in + multiples of page size. For offset larger than + '1 << (page_shift + 8 * sizeof (off_t))' (that is, 1<<44 on system with + page size of 4096 bytes) the system call silently truncates the offset. + For this case, glibc mmap implementation returns EINVAL. */ + +/* Return the maximum value expected as offset argument in mmap64 call. */ +static inline uint64_t +mmap64_maximum_offset (long int page_shift) +{ + if (sizeof (off_t) < sizeof (off64_t)) + return (UINT64_C(1) << (page_shift + (8 * sizeof (off_t)))) - 1; + else + return UINT64_MAX; +} diff --git a/sysdeps/generic/stap-probe-machine.h b/sysdeps/generic/stap-probe-machine.h new file mode 100644 index 000000000..2e5790c3b --- /dev/null +++ b/sysdeps/generic/stap-probe-machine.h @@ -0,0 +1,19 @@ +/* Macros for customizing Systemtap . Generic version. + Copyright (C) 2019 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 + . */ + +/* By default, there are no customizations. */ diff --git a/sysdeps/hppa/fpu/libm-test-ulps b/sysdeps/hppa/fpu/libm-test-ulps index d0c4dea00..2c61a7ae9 100644 --- a/sysdeps/hppa/fpu/libm-test-ulps +++ b/sysdeps/hppa/fpu/libm-test-ulps @@ -544,9 +544,9 @@ idouble: 1 ifloat: 1 Function: Imaginary part of "ccos_downward": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "ccos_towardzero": @@ -556,9 +556,9 @@ idouble: 1 ifloat: 2 Function: Imaginary part of "ccos_towardzero": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "ccos_upward": @@ -588,27 +588,27 @@ idouble: 1 ifloat: 1 Function: Real part of "ccosh_downward": -double: 1 +double: 2 float: 3 -idouble: 1 +idouble: 2 ifloat: 3 Function: Imaginary part of "ccosh_downward": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "ccosh_towardzero": -double: 1 +double: 2 float: 3 -idouble: 1 +idouble: 2 ifloat: 3 Function: Imaginary part of "ccosh_towardzero": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "ccosh_upward": @@ -636,27 +636,27 @@ idouble: 1 ifloat: 2 Function: Real part of "cexp_downward": -double: 1 +double: 2 float: 2 -idouble: 1 +idouble: 2 ifloat: 2 Function: Imaginary part of "cexp_downward": -double: 1 +double: 3 float: 3 -idouble: 1 +idouble: 3 ifloat: 3 Function: Real part of "cexp_towardzero": -double: 1 +double: 2 float: 2 -idouble: 1 +idouble: 2 ifloat: 2 Function: Imaginary part of "cexp_towardzero": -double: 1 +double: 3 float: 3 -idouble: 1 +idouble: 3 ifloat: 3 Function: Real part of "cexp_upward": @@ -666,9 +666,9 @@ idouble: 1 ifloat: 2 Function: Imaginary part of "cexp_upward": -double: 1 +double: 3 float: 2 -idouble: 1 +idouble: 3 ifloat: 2 Function: Real part of "clog": @@ -800,21 +800,21 @@ idouble: 1 ifloat: 1 Function: "cosh_downward": -double: 1 +double: 2 float: 1 -idouble: 1 +idouble: 2 ifloat: 1 Function: "cosh_towardzero": -double: 1 +double: 2 float: 1 -idouble: 1 +idouble: 2 ifloat: 1 Function: "cosh_upward": -double: 1 +double: 2 float: 2 -idouble: 1 +idouble: 2 ifloat: 2 Function: Real part of "cpow": @@ -834,9 +834,9 @@ ildouble: 2 ldouble: 2 Function: Real part of "cpow_downward": -double: 4 +double: 5 float: 8 -idouble: 4 +idouble: 5 ifloat: 8 Function: Imaginary part of "cpow_downward": @@ -846,9 +846,9 @@ idouble: 2 ifloat: 2 Function: Real part of "cpow_towardzero": -double: 4 +double: 5 float: 8 -idouble: 4 +idouble: 5 ifloat: 8 Function: Imaginary part of "cpow_towardzero": @@ -876,9 +876,9 @@ idouble: 1 ifloat: 1 Function: Real part of "csin_downward": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Imaginary part of "csin_downward": @@ -888,9 +888,9 @@ idouble: 1 ifloat: 2 Function: Real part of "csin_towardzero": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Imaginary part of "csin_towardzero": @@ -930,9 +930,9 @@ idouble: 2 ifloat: 2 Function: Imaginary part of "csinh_downward": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "csinh_towardzero": @@ -942,9 +942,9 @@ idouble: 2 ifloat: 2 Function: Imaginary part of "csinh_towardzero": -double: 2 +double: 3 float: 3 -idouble: 2 +idouble: 3 ifloat: 3 Function: Real part of "csinh_upward": @@ -1172,15 +1172,15 @@ ildouble: 6 ldouble: 6 Function: "exp10_downward": -double: 2 +double: 3 float: 1 -idouble: 2 +idouble: 3 ifloat: 1 Function: "exp10_towardzero": -double: 2 +double: 3 float: 1 -idouble: 2 +idouble: 3 ifloat: 1 Function: "exp10_upward": diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c index bd68f18b4..14b69a6f8 100644 --- a/sysdeps/nptl/fork.c +++ b/sysdeps/nptl/fork.c @@ -55,7 +55,7 @@ __libc_fork (void) but our current fork implementation is not. */ bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads); - __run_fork_handlers (atfork_run_prepare); + __run_fork_handlers (atfork_run_prepare, multiple_threads); /* If we are not running multiple threads, we do not have to preserve lock state. If fork runs from a signal handler, only @@ -134,7 +134,7 @@ __libc_fork (void) __rtld_lock_initialize (GL(dl_load_lock)); /* Run the handlers registered for the child. */ - __run_fork_handlers (atfork_run_child); + __run_fork_handlers (atfork_run_child, multiple_threads); } else { @@ -149,7 +149,7 @@ __libc_fork (void) } /* Run the handlers registered for the parent. */ - __run_fork_handlers (atfork_run_parent); + __run_fork_handlers (atfork_run_parent, multiple_threads); } return pid; diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h index a1c3b26b6..99ed76034 100644 --- a/sysdeps/nptl/fork.h +++ b/sysdeps/nptl/fork.h @@ -52,9 +52,11 @@ enum __run_fork_handler_type - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal lock. - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal - lock. */ -extern void __run_fork_handlers (enum __run_fork_handler_type who) - attribute_hidden; + lock. + + Perform locking only if DO_LOCKING. */ +extern void __run_fork_handlers (enum __run_fork_handler_type who, + _Bool do_locking) attribute_hidden; /* C library side function to register new fork handlers. */ extern int __register_atfork (void (*__prepare) (void), diff --git a/sysdeps/powerpc/dl-procinfo.h b/sysdeps/powerpc/dl-procinfo.h index f542f7318..dfc3b33a7 100644 --- a/sysdeps/powerpc/dl-procinfo.h +++ b/sysdeps/powerpc/dl-procinfo.h @@ -225,7 +225,7 @@ _dl_procinfo (unsigned int type, unsigned long int word) break; } default: - /* This should not happen. */ + /* Fallback to generic output mechanism. */ return -1; } _dl_printf ("\n"); diff --git a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h index b4b81fc70..99697ae64 100644 --- a/sysdeps/s390/dl-procinfo.h +++ b/sysdeps/s390/dl-procinfo.h @@ -57,7 +57,8 @@ enum }; #define HWCAP_IMPORTANT (HWCAP_S390_ZARCH | HWCAP_S390_LDISP \ - | HWCAP_S390_EIMM | HWCAP_S390_DFP) + | HWCAP_S390_EIMM | HWCAP_S390_DFP \ + | HWCAP_S390_VX | HWCAP_S390_VXE) /* We cannot provide a general printing function. */ #define _dl_procinfo(type, word) -1 diff --git a/sysdeps/sparc/dl-procinfo.h b/sysdeps/sparc/dl-procinfo.h index 282b8c511..64ee267fc 100644 --- a/sysdeps/sparc/dl-procinfo.h +++ b/sysdeps/sparc/dl-procinfo.h @@ -31,8 +31,8 @@ _dl_procinfo (unsigned int type, unsigned long int word) { int i; - /* Fallback to unknown output mechanism. */ - if (type == AT_HWCAP2) + /* Fallback to generic output mechanism. */ + if (type != AT_HWCAP) return -1; _dl_printf ("AT_HWCAP: "); diff --git a/sysdeps/unix/alpha/getegid.S b/sysdeps/unix/sysv/linux/alpha/getegid.S similarity index 100% rename from sysdeps/unix/alpha/getegid.S rename to sysdeps/unix/sysv/linux/alpha/getegid.S diff --git a/sysdeps/unix/alpha/geteuid.S b/sysdeps/unix/sysv/linux/alpha/geteuid.S similarity index 100% rename from sysdeps/unix/alpha/geteuid.S rename to sysdeps/unix/sysv/linux/alpha/geteuid.S diff --git a/sysdeps/unix/alpha/getppid.S b/sysdeps/unix/sysv/linux/alpha/getppid.S similarity index 100% rename from sysdeps/unix/alpha/getppid.S rename to sysdeps/unix/sysv/linux/alpha/getppid.S diff --git a/sysdeps/unix/sysv/linux/alpha/kernel-features.h b/sysdeps/unix/sysv/linux/alpha/kernel-features.h index 0b0a94af2..fc8407c8a 100644 --- a/sysdeps/unix/sysv/linux/alpha/kernel-features.h +++ b/sysdeps/unix/sysv/linux/alpha/kernel-features.h @@ -26,8 +26,10 @@ #undef __ASSUME_STATFS64 #define __ASSUME_STATFS64 0 -/* Alpha defines SysV ipc shmat syscall with a different name. */ -#define __NR_shmat __NR_osf_shmat +/* Alpha used to define SysV ipc shmat syscall with a different name. */ +#ifndef __NR_shmat +# define __NR_shmat __NR_osf_shmat +#endif #define __ASSUME_RECV_SYSCALL 1 #define __ASSUME_SEND_SYSCALL 1 @@ -45,7 +47,6 @@ /* Support for copy_file_range, statx was added in kernel 4.13. */ #if __LINUX_KERNEL_VERSION < 0x040D00 # undef __ASSUME_MLOCK2 -# undef __ASSUME_COPY_FILE_RANGE # undef __ASSUME_STATX #endif diff --git a/sysdeps/unix/sysv/linux/alpha/sysdep.h b/sysdeps/unix/sysv/linux/alpha/sysdep.h index dcf2d1e3d..d61d4df55 100644 --- a/sysdeps/unix/sysv/linux/alpha/sysdep.h +++ b/sysdeps/unix/sysv/linux/alpha/sysdep.h @@ -51,7 +51,9 @@ * Some syscalls no Linux program should know about: */ #define __NR_osf_sigprocmask 48 -#define __NR_osf_shmat 209 +#ifndef __NR_osf_shmat +# define __NR_osf_shmat 209 +#endif #define __NR_osf_getsysinfo 256 #define __NR_osf_setsysinfo 257 diff --git a/sysdeps/unix/sysv/linux/arm/dl-procinfo.h b/sysdeps/unix/sysv/linux/arm/dl-procinfo.h index 66c00297b..05c62c868 100644 --- a/sysdeps/unix/sysv/linux/arm/dl-procinfo.h +++ b/sysdeps/unix/sysv/linux/arm/dl-procinfo.h @@ -67,7 +67,7 @@ _dl_procinfo (unsigned int type, unsigned long int word) break; } default: - /* This should not happen. */ + /* Fallback to generic output mechanism. */ return -1; } _dl_printf ("\n"); diff --git a/sysdeps/unix/sysv/linux/arm/kernel-features.h b/sysdeps/unix/sysv/linux/arm/kernel-features.h index 2d2d35584..4220adff3 100644 --- a/sysdeps/unix/sysv/linux/arm/kernel-features.h +++ b/sysdeps/unix/sysv/linux/arm/kernel-features.h @@ -45,7 +45,6 @@ present in 32-bit kernels from 4.4 and 4.5 respectively. */ #if __LINUX_KERNEL_VERSION < 0x040700 # undef __ASSUME_MLOCK2 -# undef __ASSUME_COPY_FILE_RANGE #endif #undef __ASSUME_CLONE_DEFAULT diff --git a/sysdeps/unix/sysv/linux/copy_file_range.c b/sysdeps/unix/sysv/linux/copy_file_range.c index 70961007a..e950db3bf 100644 --- a/sysdeps/unix/sysv/linux/copy_file_range.c +++ b/sysdeps/unix/sysv/linux/copy_file_range.c @@ -20,27 +20,16 @@ #include #include -/* Include the fallback implementation. */ -#ifndef __ASSUME_COPY_FILE_RANGE -#define COPY_FILE_RANGE_DECL static -#define COPY_FILE_RANGE copy_file_range_compat -#include -#endif - ssize_t copy_file_range (int infd, __off64_t *pinoff, int outfd, __off64_t *poutoff, size_t length, unsigned int flags) { #ifdef __NR_copy_file_range - ssize_t ret = SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff, - length, flags); -# ifndef __ASSUME_COPY_FILE_RANGE - if (ret == -1 && errno == ENOSYS) - ret = copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags); -# endif - return ret; -#else /* !__NR_copy_file_range */ - return copy_file_range_compat (infd, pinoff, outfd, poutoff, length, flags); + return SYSCALL_CANCEL (copy_file_range, infd, pinoff, outfd, poutoff, + length, flags); +#else + __set_errno (ENOSYS); + return -1; #endif } diff --git a/sysdeps/unix/sysv/linux/i386/dl-procinfo.h b/sysdeps/unix/sysv/linux/i386/dl-procinfo.h index 22b43431b..0585cdaa9 100644 --- a/sysdeps/unix/sysv/linux/i386/dl-procinfo.h +++ b/sysdeps/unix/sysv/linux/i386/dl-procinfo.h @@ -30,8 +30,8 @@ _dl_procinfo (unsigned int type, unsigned long int word) in the kernel sources. */ int i; - /* Fallback to unknown output mechanism. */ - if (type == AT_HWCAP2) + /* Fallback to generic output mechanism. */ + if (type != AT_HWCAP) return -1; _dl_printf ("AT_HWCAP: "); diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h index 0db1b987a..abe98522d 100644 --- a/sysdeps/unix/sysv/linux/kernel-features.h +++ b/sysdeps/unix/sysv/linux/kernel-features.h @@ -103,10 +103,6 @@ # define __ASSUME_MLOCK2 1 #endif -#if __LINUX_KERNEL_VERSION >= 0x040500 -# define __ASSUME_COPY_FILE_RANGE 1 -#endif - /* Support for statx was added in kernel 4.11. */ #if __LINUX_KERNEL_VERSION >= 0x040B00 # define __ASSUME_STATX 1 diff --git a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h index 8df19400a..a78740929 100644 --- a/sysdeps/unix/sysv/linux/microblaze/kernel-features.h +++ b/sysdeps/unix/sysv/linux/microblaze/kernel-features.h @@ -60,11 +60,6 @@ # undef __ASSUME_MLOCK2 #endif -/* Support for the copy_file_range syscall was added in 4.10. */ -#if __LINUX_KERNEL_VERSION < 0x040A00 -# undef __ASSUME_COPY_FILE_RANGE -#endif - /* Support for statx was added in kernel 4.12. */ #if __LINUX_KERNEL_VERSION < 0X040C00 # undef __ASSUME_STATX diff --git a/sysdeps/unix/sysv/linux/mips/mmap_info.h b/sysdeps/unix/sysv/linux/mips/mmap_info.h new file mode 100644 index 000000000..07c9e3a04 --- /dev/null +++ b/sysdeps/unix/sysv/linux/mips/mmap_info.h @@ -0,0 +1,13 @@ +/* mips64n32 uses __NR_mmap for mmap64 while still having sizeof (off_t) + smaller than sizeof (off64_t). So it allows mapping large offsets + using mmap64 than 32-bit archs which uses __NR_mmap2. */ + +static inline uint64_t +mmap64_maximum_offset (long int page_shift) +{ +#if _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 + return UINT64_MAX; +#else + return (UINT64_C(1) << (page_shift + (8 * sizeof (off_t)))) - 1; +#endif +} diff --git a/sysdeps/unix/sysv/linux/mmap64.c b/sysdeps/unix/sysv/linux/mmap64.c index cb5654011..671db2b65 100644 --- a/sysdeps/unix/sysv/linux/mmap64.c +++ b/sysdeps/unix/sysv/linux/mmap64.c @@ -23,11 +23,18 @@ #include #include +#ifdef __NR_mmap2 /* To avoid silent truncation of offset when using mmap2, do not accept offset larger than 1 << (page_shift + off_t bits). For archictures with 32 bits off_t and page size of 4096 it would be 1^44. */ -#define MMAP_OFF_HIGH_MASK \ +# define MMAP_OFF_HIGH_MASK \ ((-(MMAP2_PAGE_UNIT << 1) << (8 * sizeof (off_t) - 1))) +#else +/* Some ABIs might use __NR_mmap while having sizeof (off_t) smaller than + sizeof (off64_t) (currently only MIPS64n32). For this case just set + zero the higher bits so mmap with large offset does not fail. */ +# define MMAP_OFF_HIGH_MASK 0x0 +#endif #define MMAP_OFF_MASK (MMAP_OFF_HIGH_MASK | MMAP_OFF_LOW_MASK) diff --git a/sysdeps/unix/sysv/linux/riscv/vfork.S b/sysdeps/unix/sysv/linux/riscv/vfork.S index 67373f181..dc173d6b4 100644 --- a/sysdeps/unix/sysv/linux/riscv/vfork.S +++ b/sysdeps/unix/sysv/linux/riscv/vfork.S @@ -21,9 +21,12 @@ #include #include #define __ASSEMBLY__ -#include #include +#define CLONE_VM 0x00000100 /* Set if VM shared between processes. */ +#define CLONE_VFORK 0x00004000 /* Set if the parent wants the child to + wake it up on mm_release. */ + .text LEAF (__libc_vfork) diff --git a/sysdeps/unix/sysv/linux/s390/dl-procinfo.h b/sysdeps/unix/sysv/linux/s390/dl-procinfo.h index 19329a335..d67fde368 100644 --- a/sysdeps/unix/sysv/linux/s390/dl-procinfo.h +++ b/sysdeps/unix/sysv/linux/s390/dl-procinfo.h @@ -32,8 +32,8 @@ _dl_procinfo (unsigned int type, unsigned long int word) in the kernel sources. */ int i; - /* Fallback to unknown output mechanism. */ - if (type == AT_HWCAP2) + /* Fallback to generic output mechanism. */ + if (type != AT_HWCAP) return -1; _dl_printf ("AT_HWCAP: "); diff --git a/sysdeps/unix/sysv/linux/sh/kernel-features.h b/sysdeps/unix/sysv/linux/sh/kernel-features.h index 767df721b..22cdc8049 100644 --- a/sysdeps/unix/sysv/linux/sh/kernel-features.h +++ b/sysdeps/unix/sysv/linux/sh/kernel-features.h @@ -49,7 +49,6 @@ # undef __ASSUME_RENAMEAT2 # undef __ASSUME_EXECVEAT # undef __ASSUME_MLOCK2 -# undef __ASSUME_COPY_FILE_RANGE #endif /* sh does not support the statx system call. */ diff --git a/sysdeps/x86_64/memcmp.S b/sysdeps/x86_64/memcmp.S index 1fc487caa..1322bb3b9 100644 --- a/sysdeps/x86_64/memcmp.S +++ b/sysdeps/x86_64/memcmp.S @@ -21,14 +21,18 @@ .text ENTRY (memcmp) - test %rdx, %rdx +#ifdef __ILP32__ + /* Clear the upper 32 bits. */ + movl %edx, %edx +#endif + test %RDX_LP, %RDX_LP jz L(finz) cmpq $1, %rdx - jle L(finr1b) + jbe L(finr1b) subq %rdi, %rsi movq %rdx, %r10 cmpq $32, %r10 - jge L(gt32) + jae L(gt32) /* Handle small chunks and last block of less than 32 bytes. */ L(small): testq $1, %r10 @@ -156,7 +160,7 @@ L(A32): movq %r11, %r10 andq $-32, %r10 cmpq %r10, %rdi - jge L(mt16) + jae L(mt16) /* Pre-unroll to be ready for unrolled 64B loop. */ testq $32, %rdi jz L(A64) @@ -178,7 +182,7 @@ L(A64): movq %r11, %r10 andq $-64, %r10 cmpq %r10, %rdi - jge L(mt32) + jae L(mt32) L(A64main): movdqu (%rdi,%rsi), %xmm0 @@ -216,7 +220,7 @@ L(mt32): movq %r11, %r10 andq $-32, %r10 cmpq %r10, %rdi - jge L(mt16) + jae L(mt16) L(A32main): movdqu (%rdi,%rsi), %xmm0 @@ -254,7 +258,7 @@ L(ATR): movq %r11, %r10 andq $-32, %r10 cmpq %r10, %rdi - jge L(mt16) + jae L(mt16) testq $16, %rdi jz L(ATR32) @@ -325,7 +329,7 @@ L(ATR64main): movq %r11, %r10 andq $-32, %r10 cmpq %r10, %rdi - jge L(mt16) + jae L(mt16) L(ATR32res): movdqa (%rdi,%rsi), %xmm0 diff --git a/sysdeps/x86_64/x32/Makefile b/sysdeps/x86_64/x32/Makefile index 1557724b0..874895656 100644 --- a/sysdeps/x86_64/x32/Makefile +++ b/sysdeps/x86_64/x32/Makefile @@ -8,7 +8,8 @@ endif ifeq ($(subdir),string) tests += tst-size_t-memchr tst-size_t-memcmp tst-size_t-memcpy \ tst-size_t-memrchr tst-size_t-memset tst-size_t-strncasecmp \ - tst-size_t-strncmp tst-size_t-strncpy tst-size_t-strnlen + tst-size_t-strncmp tst-size_t-strncpy tst-size_t-strnlen \ + tst-size_t-memcmp-2 endif ifeq ($(subdir),wcsmbs) diff --git a/sysdeps/x86_64/x32/tst-size_t-memcmp-2.c b/sysdeps/x86_64/x32/tst-size_t-memcmp-2.c new file mode 100644 index 000000000..d8ae1a081 --- /dev/null +++ b/sysdeps/x86_64/x32/tst-size_t-memcmp-2.c @@ -0,0 +1,79 @@ +/* Test memcmp with size_t in the lower 32 bits of 64-bit register. + Copyright (C) 2019 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 + . */ + +#define TEST_MAIN +#ifdef WIDE +# define TEST_NAME "wmemcmp" +#else +# define TEST_NAME "memcmp" +#endif + +#include "test-size_t.h" + +#ifdef WIDE +# include +# include + +# define MEMCMP wmemcmp +# define CHAR wchar_t +#else +# define MEMCMP memcmp +# define CHAR char +#endif + +IMPL (MEMCMP, 1) + +typedef int (*proto_t) (const CHAR *, const CHAR *, size_t); + +static int +__attribute__ ((noinline, noclone)) +do_memcmp (parameter_t a, parameter_t b) +{ + return CALL (&b, a.p, b.p, a.len); +} + +static int +test_main (void) +{ + test_init (); + + parameter_t dest = { { page_size / sizeof (CHAR) }, buf1 }; + parameter_t src = { { 0 }, buf2 }; + + memcpy (buf1, buf2, page_size); + + CHAR *p = (CHAR *) buf1; + p[page_size / sizeof (CHAR) - 1] = (CHAR) 1; + + int ret = 0; + FOR_EACH_IMPL (impl, 0) + { + src.fn = impl->fn; + int res = do_memcmp (dest, src); + if (res >= 0) + { + error (0, 0, "Wrong result in function %s: %i >= 0", + impl->name, res); + ret = 1; + } + } + + return ret ? EXIT_FAILURE : EXIT_SUCCESS; +} + +#include -- 2.30.2