GIT update of git://sourceware.org/git/glibc.git/release/2.25/master from glibc-2.25 diff --git a/ChangeLog b/ChangeLog index f140ee67de..a3db9c09f2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,750 @@ +2017-12-30 Aurelien Jarno + Dmitry V. Levin + + [BZ #22625] + * elf/dl-load.c (fillin_rpath): Check for empty tokens before dynamic + string token expansion. Check for NULL pointer or empty string possibly + returned by expand_dynamic_string_token. + (decompose_rpath): Check for empty path after dynamic string + token expansion. + +2017-12-18 Dmitry V. Levin + + [BZ #22627] + * elf/dl-load.c (_dl_init_paths): Remove _dl_dst_substitute preparatory + code and invocation. + +2017-12-14 Florian Weimer + + [BZ #22607] + CVE-2017-1000409 + * elf/dl-load.c (_dl_init_paths): Compute number of components in + the expanded path string. + +2017-12-14 Florian Weimer + + [BZ #22606] + CVE-2017-1000408 + * elf/dl-load.c (system_dirs): Update comment. + (nsystem_dirs_len): Use array_length. + (_dl_init_paths): Use nsystem_dirs_len to compute the array size. + +2017-11-02 Florian Weimer + + Add array_length and array_end macros. + * include/array_length.h: New file. + +2017-12-12 James Clarke + + * sysdeps/unix/sysv/linux/ia64/ipc_priv.h: New file defining + __IPC_64 to 0 to avoid IPC_64 being set. + +2017-11-02 Florian Weimer + + [BZ #22332] + * posix/tst-glob-tilde.c (do_noescape): New variable. + (one_test): Process it. + (do_test): Set do_noescape. Add unescaping test case. + +2017-10-22 Paul Eggert + + [BZ #22332] + * posix/glob.c (__glob): Fix buffer overflow during GLOB_TILDE + unescaping. + +2017-10-21 Florian Weimer + + * posix/Makefile (tests): Add tst-glob-tilde. + (tests-special): Add tst-glob-tilde-mem.out + (tst-glob-tilde-ENV): Set MALLOC_TRACE. + (tst-glob-tilde-mem.out): Add mtrace check. + * posix/tst-glob-tilde.c: New file. + +2017-10-20 Paul Eggert + + [BZ #22320] + CVE-2017-15670 + * posix/glob.c (__glob): Fix one-byte overflow. + +2017-09-08 Adhemerval Zanella + + [BZ #1062] + CVE-2017-15671 + * posix/Makefile (routines): Add globfree, globfree64, and + glob_pattern_p. + * posix/flexmember.h: New file. + * posix/glob_internal.h: Likewise. + * posix/glob_pattern_p.c: Likewise. + * posix/globfree.c: Likewise. + * posix/globfree64.c: Likewise. + * sysdeps/gnu/globfree64.c: Likewise. + * sysdeps/unix/sysv/linux/alpha/globfree.c: Likewise. + * sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c: Likewise. + * sysdeps/unix/sysv/linux/oldglob.c: Likewise. + * sysdeps/unix/sysv/linux/wordsize-64/globfree64.c: Likewise. + * sysdeps/unix/sysv/linux/x86_64/x32/globfree.c: Likewise. + * sysdeps/wordsize-64/globfree.c: Likewise. + * sysdeps/wordsize-64/globfree64.c: Likewise. + * posix/glob.c (HAVE_CONFIG_H): Use !_LIBC instead. + [NDEBUG): Remove comments. + (GLOB_ONLY_P, _AMIGA, VMS): Remove define. + (dirent_type): New type. Use uint_fast8_t not + uint8_t, as C99 does not require uint8_t. + (DT_UNKNOWN, DT_DIR, DT_LNK): New macros. + (struct readdir_result): Use dirent_type. Do not define skip_entry + unless it is needed; this saves a byte on platforms lacking d_ino. + (readdir_result_type, readdir_result_skip_entry): + New functions, replacing ... + (readdir_result_might_be_symlink, readdir_result_might_be_dir): + these functions, which were removed. This makes the callers + easier to read. All callers changed. + (D_INO_TO_RESULT): Now empty if there is no d_ino. + (size_add_wrapv, glob_use_alloca): New static functions. + (glob, glob_in_dir): Check for size_t overflow in several places, + and fix some size_t checks that were not quite right. + Remove old code using SHELL since Bash no longer + uses this. + (glob, prefix_array): Separate MS code better. + (glob_in_dir): Remove old Amiga and VMS code. + (globfree, __glob_pattern_type, __glob_pattern_p): Move to + separate files. + (glob_in_dir): Do not rely on undefined behavior in accessing + struct members beyond their bounds. Use a flexible array member + instead + (link_stat): Rename from link_exists2_p and return -1/0 instead of + 0/1. Caller changed. + (glob): Fix memory leaks. + * posix/glob64 (globfree64): Move to separate file. + * sysdeps/gnu/glob64.c (NO_GLOB_PATTERN_P): Remove define. + (globfree64): Remove hidden alias. + * sysdeps/unix/sysv/linux/Makefile (sysdeps_routines): Add + oldglob. + * sysdeps/unix/sysv/linux/alpha/glob.c (__new_globfree): Move to + separate file. + * sysdeps/unix/sysv/linux/i386/glob64.c (NO_GLOB_PATTERN_P): Remove + define. + Move compat code to separate file. + * sysdeps/wordsize-64/glob.c (globfree): Move definitions to + separate file. + +2017-08-20 H.J. Lu + + [BZ #18822] + * sysdeps/unix/sysv/linux/i386/glob64.c (__old_glob64): Add + libc_hidden_proto and libc_hidden_def. + +2017-03-14 Adhemerval Zanella + + [BZ #21232] + * sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c: Add + posix_fadvise64 weak_alias for static build. + +2017-10-23 Adhemerval Zanella + + * sysdeps/unix/sysv/linux/spawni.c (__spawnix): Use 0 instead of + WNOHANG in waitpid call. + +2017-10-20 Adhemerval Zanella + + [BZ #22273] + * sysdeps/unix/sysv/linux/spawni.c (__spawnix): Handle the case where + the auxiliary process is terminated by a signal before calling _exit + or execve. + +2017-08-09 Andreas Schwab + + * nptl/Makefile (tests) [$(build-shared) = yes]: Add + tst-compat-forwarder. + (modules-names): Add tst-compat-forwarder-mod. + ($(objpfx)tst-compat-forwarder): Depend on + $(objpfx)tst-compat-forwarder-mod.so. + * nptl/tst-compat-forwarder.c: New file. + * nptl/tst-compat-forwarder-mod.c: New file. + +2017-08-09 Andreas Schwab + + * sysdeps/unix/sysv/linux/s390/pt-longjmp.c: Update reference to + renamed alias. + +2017-08-08 Andreas Schwab + + [BZ #21041] + * nptl/pt-longjmp.c (longjmp, siglongjmp): Don't use IFUNC resolver. + * nptl/pt-system.c (system): Likewise. + +2017-10-13 James Clarke + + * sysdeps/powerpc/powerpc32/dl-machine.h (elf_machine_rela): + Assign sym_map to be map for local symbols, as TLS relocations + use sym_map to determine whether the symbol is defined and to + extract the TLS information. + * sysdeps/sparc/sparc32/dl-machine.h (elf_machine_rela): Likewise. + * sysdeps/sparc/sparc64/dl-machine.h (elf_machine_rela): Likewise. + +2017-10-19 Joseph Myers + + [BZ #22322] + * sysdeps/mips/bits/long-double.h: Move to .... + * sysdeps/mips/ieee754/bits/long-double.h: ... here. + +2017-10-22 H.J. Lu + + [BZ #21265] + * sysdeps/x86/cpu-features-offsets.sym (XSAVE_STATE_SIZE_OFFSET): + New. + * sysdeps/x86/cpu-features.c: Include . + (get_common_indeces): Set xsave_state_size and + bit_arch_XSAVEC_Usable if needed. + (init_cpu_features): Remove bit_arch_Use_dl_runtime_resolve_slow + and bit_arch_Use_dl_runtime_resolve_opt. + * sysdeps/x86/cpu-features.h (bit_arch_Use_dl_runtime_resolve_opt): + Removed. + (bit_arch_Use_dl_runtime_resolve_slow): Likewise. + (bit_arch_Prefer_No_AVX512): Updated. + (bit_arch_MathVec_Prefer_No_AVX512): Likewise. + (bit_arch_XSAVEC_Usable): New. + (STATE_SAVE_OFFSET): Likewise. + (STATE_SAVE_MASK): Likewise. + [__ASSEMBLER__]: Include . + (cpu_features): Add xsave_state_size. + (index_arch_Use_dl_runtime_resolve_opt): Removed. + (index_arch_Use_dl_runtime_resolve_slow): Likewise. + (index_arch_XSAVEC_Usable): New. + * sysdeps/x86_64/dl-machine.h (elf_machine_runtime_setup): + Replace _dl_runtime_resolve_sse, _dl_runtime_resolve_avx, + _dl_runtime_resolve_avx_slow, _dl_runtime_resolve_avx_opt, + _dl_runtime_resolve_avx512 and _dl_runtime_resolve_avx512_opt + with _dl_runtime_resolve_fxsave, _dl_runtime_resolve_xsave and + _dl_runtime_resolve_xsavec. + * sysdeps/x86_64/dl-trampoline.S (DL_RUNTIME_UNALIGNED_VEC_SIZE): + Removed. + (DL_RUNTIME_RESOLVE_REALIGN_STACK): Check STATE_SAVE_ALIGNMENT + instead of VEC_SIZE. + (REGISTER_SAVE_BND0): Removed. + (REGISTER_SAVE_BND1): Likewise. + (REGISTER_SAVE_BND3): Likewise. + (REGISTER_SAVE_RAX): Always defined to 0. + (VMOV): Removed. + (_dl_runtime_resolve_avx): Likewise. + (_dl_runtime_resolve_avx_slow): Likewise. + (_dl_runtime_resolve_avx_opt): Likewise. + (_dl_runtime_resolve_avx512): Likewise. + (_dl_runtime_resolve_avx512_opt): Likewise. + (_dl_runtime_resolve_sse): Likewise. + (_dl_runtime_resolve_sse_vex): Likewise. + (USE_FXSAVE): New. + (_dl_runtime_resolve_fxsave): Likewise. + (USE_XSAVE): Likewise. + (_dl_runtime_resolve_xsave): Likewise. + (USE_XSAVEC): Likewise. + (_dl_runtime_resolve_xsavec): Likewise. + * sysdeps/x86_64/dl-trampoline.h (_dl_runtime_resolve_avx512): + Removed. + (_dl_runtime_resolve_avx512_opt): Likewise. + (_dl_runtime_resolve_avx): Likewise. + (_dl_runtime_resolve_avx_opt): Likewise. + (_dl_runtime_resolve_sse): Likewise. + (_dl_runtime_resolve_sse_vex): Likewise. + (_dl_runtime_resolve_fxsave): New. + (_dl_runtime_resolve_xsave): Likewise. + (_dl_runtime_resolve_xsavec): Likewise. + +2017-10-19 H.J. Lu + + * sysdeps/x86_64/Makefile (tests): Add tst-sse, tst-avx and + tst-avx512. + (test-extras): Add tst-avx-aux and tst-avx512-aux. + (extra-test-objs): Add tst-avx-aux.o and tst-avx512-aux.o. + (modules-names): Add tst-ssemod, tst-avxmod and tst-avx512mod. + ($(objpfx)tst-sse): New rule. + ($(objpfx)tst-avx): Likewise. + ($(objpfx)tst-avx512): Likewise. + (CFLAGS-tst-avx-aux.c): New. + (CFLAGS-tst-avxmod.c): Likewise. + (CFLAGS-tst-avx512-aux.c): Likewise. + (CFLAGS-tst-avx512mod.c): Likewise. + * sysdeps/x86_64/tst-avx-aux.c: New file. + * sysdeps/x86_64/tst-avx.c: Likewise. + * sysdeps/x86_64/tst-avx512-aux.c: Likewise. + * sysdeps/x86_64/tst-avx512.c: Likewise. + * sysdeps/x86_64/tst-avx512mod.c: Likewise. + * sysdeps/x86_64/tst-avxmod.c: Likewise. + * sysdeps/x86_64/tst-sse.c: Likewise. + * sysdeps/x86_64/tst-ssemod.c: Likewise. + +2017-07-19 DJ Delorie + + [BZ #21654] + * grp/grp-merge.c (libc_hidden_def): Fix cast-after-dereference. + +2017-07-14 DJ Delorie + + [BZ #21654] + * grp/grp_merge.c (__copy_grp): Align char** to minimum pointer + alignment not char alignment. + (__merge_grp): Likewise. + +2017-08-22 Joseph Myers + + [BZ #21987] + * sysdeps/unix/sysv/linux/sparc/bits/long-double.h: Remove file + and copy to ... + * sysdeps/unix/sysv/linux/sparc/sparc32/bits/long-double.h: + ... here. + * sysdeps/unix/sysv/linux/sparc/sparc64/bits/long-double.h: + ... and here. + +2017-09-11 H.J. Lu + Florian Weimer + + * configure.ac (find_cxx_header): Suppress compiler error message. + * configure: Regenerated. + + [BZ #21573] + * Makerules [$(c++-bits-std_abs-h) != ""] (before-compile): Add + $(common-objpfx)bits/std_abs.h. + [$(c++-bits-std_abs-h) != ""] ($(common-objpfx)bits/std_abs.h): + New target. + * config.make.in (c++-bits-std_abs-h): New. + * configure.ac (find_cxx_header): Use "\,$1," with sed. + (CXX_BITS_STD_ABS_H): New. + (AC_SUBST(CXX_BITS_STD_ABS_H)): Likewise. + * configure: Regenerated. + +2017-09-11 H.J. Lu + + [BZ #21982] + * string/stratcliff.c (do_test): Declare size, nchars, inner, + middle and outer with size_t instead of int. Repleace %d and + %Zd with %zu in printf. Update "MAX (0, nchars - 128)" and + "MAX (outer, nchars - 64)" to support unsigned outer and + nchars. Also exit loop when outer == 0. + +2017-09-07 H.J. Lu + + * resolv/tst-resolv-qtypes.c (domain): Changed to + "const char domain[] =". + +2017-08-31 H.J. Lu + + [BZ #22051] + * Makerules (build-module-helper-objlist): Filter out + $(elf-objpfx)sofini.os. + (build-shlib-objlist): Append $(elf-objpfx)sofini.os if it is + needed. + +2017-07-29 Torvald Riegel + Carlos O'Donell + + [BZ 21778] + * nptl/pthread_mutex_timedlock.c (__pthread_mutex_timedlock): Update + oldval if the CAS fails. + * nptl/pthread_mutex_lock.c (__pthread_mutex_lock_full): Likewise. + * nptl/tst-mutex7.c: Add comments explaining template test. + (ROBUST, DELAY_NSEC, ROUNDS, N): New. + (tf, do_test): Use them. + * nptl/tst-mutex7robust.c: New file. + * nptl/Makefile (tests): Add new test. + +2017-07-28 Torvald Riegel + Carlos O'Donell + + [BZ #21298] + * nptl/Makefile (tests): Add tst-rwlock20. + * nptl/pthread_rwlock_common.c (__pthread_rwlock_rdlock_full): Fix + explicit hand-over. + (__pthread_rwlock_wrlock_full): Likewise. + * nptl/tst-rwlock20.c: New file. + +2017-08-21 Florian Weimer + + [BZ #21972] + * assert/assert.h (assert): Use static_cast (bool) for C++. + Use the ternary operator in the warning branch for GNU C. + * assert/Makefile (tests): Add tst-assert-c++, tst-assert-g++. + (CFLAGS-tst-assert-c++.o): Compile in C++11 mode. + (CFLAGS-tst-assert-g++.o): Compile in GnU C++11 mode. + (LDLIBS-tst-assert-c++, LDLIBS-tst-assert-g++): Link with libstdc++. + * assert/tst-assert-c++.cc, assert/tst-assert-g++.cc: New files. + +2017-04-13 Florian Weimer + + [BZ #21361] + Limit EDNS buffer size to 1200 bytes. + * include/resolv.h (__res_nopt): Remove declaration. + * resolv/Makefile (tests): tst-resolv-edns. + (tst-resolv-edns): Link with -lresolv, -lpthread. + * resolv/res_mkquery.c (__res_ntop): Limit EDNS buffer size to the + interval [512, 1200]. + * resolv/res_query.c (__libc_res_nquery): Use 1200 buffer size if + we can resize the buffer. + * resolv/resolv-internal.h (RESOLV_EDNS_BUFFER_SIZE): Define. + (__res_nopt): Declare. + * resolv/tst-resolv-edns.c: New file. + * resolv/resolv_test.h (struct resolv_edns_info): Define. + (struct resolv_response_context): Add edns member. + * resolv/resolv_test.c (struct query_info): Add edns member. + (parse_query): Extract EDNS information from the query. + (server_thread_udp_process_one): Propagate EDNS data. + (server_thread_tcp_client): Likewise. + +2017-08-12 John David Anglin + + [BZ 19170] + * sysdeps/hppa/dl-trampoline.S (_dl_runtime_resolve): Return to caller + if _dl_fixup fails. + +2017-08-12 John David Anglin + Adhemerval Zanella + + [BZ #21512] + * sysdeps/unix/sysv/linux/aarch64/clone.S (__clone): Call exit + syscall instead of jump to _exit. + * sysdeps/unix/sysv/linux/hppa/localplt.data: Remove _exit entry. + +2017-08-12 Adhemerval Zanella + + * sysdeps/unix/sysv/linux/hppa/ipc_priv.h: New file. + +2017-08-12 John David Anglin + + * sysdeps/unix/sysv/linux/hppa/clone.S (__clone): Add .cfi annotation. + * sysdeps/unix/sysv/linux/hppa/getcontext.S (__getcontext): Likewise. + * sysdeps/unix/sysv/linux/hppa/pt-vfork.S (__vfork): Likewise. + * sysdeps/unix/sysv/linux/hppa/setcontext.S (__setcontext): Likewise. + + * sysdeps/unix/sysv/linux/hppa/getcontext.S (__getcontext): Fix stack + offset for r19 load. + + * sysdeps/unix/sysv/linux/hppa/setcontext.S (__setcontext): Return 0. + + * sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h (PSEUDO): Fix CFA offset. + Use .cfi_def_cfa_offset instead of .cfi_offset. Don't record stack + pointer offset. Correct PIC register offset. Don't mention frame + related instructions in epilogue. + (PUSHARGS_1): Correct offset. + (PUSHARGS_2): Likewise. + (PUSHARGS_3): Likewise. + (PUSHARGS_4): Likewise. + (PUSHARGS_5): Likewise. + (PUSHARGS_6): Likewise. + (POPARGS_1): Don't mention register restore. + (POPARGS_2): Likewise. + (POPARGS_3): Likewise. + (POPARGS_4): Likewise. + (POPARGS_5): Likewise. + (POPARGS_6): Likewise. + * sysdeps/unix/sysv/linux/hppa/sysdep.h (SAVE_PIC): Don't mention + copy of PIC register. + (LOAD_PIC): Likewise don't mention restore. + (DO_CALL): Fix CFA offset. Use .cfi_def_cfa_offset instead of + .cfi_offset. Don't record stack pointer offset. Correct PIC register + offset. Don't mention frame related instructions in epilogue. + + [BZ 20098] + * sysdeps/hppa/dl-fptr.c (_dl_read_access_allowed): New. + (_dl_lookup_address): Return address if it is not consistent with + being a linker defined function pointer. Likewise, return address + if address and function descriptor addresses are not accessible. + + [BZ locale/19838] + * sysdeps/unix/sysv/linux/hppa/bits/shm.h (SHMLBA): Set to page size. + + * nptl/allocatestack.c (allocate_stack): Align old and new guard + addresses to page boundaries when the stack grows up. + + * sysdeps/hppa/math-tests.h: New. + + [BZ #21016] + * sysdeps/hppa/nptl/bits/pthreadtypes.h: Update pthread_cond_t typedef. + * sysdeps/unix/sysv/linux/hppa/pthread.h: Include + bits/types/struct_timespec.h. + (PTHREAD_MUTEX_INITIALIZER): Revise define. + (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP): Likewise. + (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP): Likewise. + (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP): Likewise. + (PTHREAD_RWLOCK_INITIALIZER): Likewise. + (PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP): Likewise. + (PTHREAD_COND_INITIALIZER): Likewise. + Remove old definitions. + * sysdeps/unix/sysv/linux/hppa/internaltypes.h: Delete. + * sysdeps/unix/sysv/linux/hppa/pthread_cond_broadcast.c: Delete. + * sysdeps/unix/sysv/linux/hppa/pthread_cond_destroy.c: Delete. + * sysdeps/unix/sysv/linux/hppa/pthread_cond_init.c: Delete. + * sysdeps/unix/sysv/linux/hppa/pthread_cond_signal.c: Delete. + * sysdeps/unix/sysv/linux/hppa/pthread_cond_wait.c: Delete. + +2017-08-11 Florian Weimer + + [BZ #21242] + * assert/assert.h [__GNUC__ && !__STRICT_ANSI__] (assert): + Suppress pedantic warning resulting from statement expression. + (__ASSERT_FUNCTION): Add missing __extension__. + +2017-08-08 Helge Deller + + [BZ #21049] + * sysdeps/hppa/__longjmp.c (__longjmp): Move call to CHECK_SP up + to avoid clobbering r26. + +2017-08-06 H.J. Lu + + [BZ #21871] + * sysdeps/x86/cpu-features.c (init_cpu_features): Set + bit_arch_Use_dl_runtime_resolve_opt only with AVX512F. + +2017-08-04 Aurelien Jarno + + * sysdeps/i386/i686/fpu/multiarch/libm-test-ulps: Regenerated. + +2017-08-03 Aurelien Jarno + + * stdlib/getentropy.c (getentropy): Change return type to int. + +2017-04-28 Tulio Magno Quites Machado Filho + + [BZ #21280] + * sysdeps/powerpc/power7/fpu/s_logbl.c (__logbl): Ignore the + signal of subnormals and adjust the exponent of power of 2 down + when low part has opposite sign. + +2017-07-26 H.J. Lu + + [BZ #21666] + * misc/regexp.c (loc1): Add __attribute__ ((nocommon)); + (loc2): Likewise. + (locs): Likewise. + +2017-07-12 Szabolcs Nagy + + * sysdeps/aarch64/dl-machine.h (RTLD_START_1): Change _dl_argv to the + hidden __GI__dl_argv symbol. + +2017-07-06 Florian Weimer + H.J. Lu + + [BZ #21609] + * sysdeps/x86_64/Makefile (sysdep-dl-routines): Add tls_get_addr. + (gen-as-const-headers): Add rtld-offsets.sym. + * sysdeps/x86_64/dl-tls.c: New file. + * sysdeps/x86_64/rtld-offsets.sym: Likwise. + * sysdeps/x86_64/tls_get_addr.S: Likewise. + * sysdeps/x86_64/dl-tls.h: Add multiple inclusion guards. + * sysdeps/x86_64/tlsdesc.sym (TI_MODULE_OFFSET): New. + (TI_OFFSET_OFFSET): Likwise. + +2017-06-14 Florian Weimer + + * sysdeps/i386/i686/multiarch/strcspn-c.c: Add IS_IN (libc) guard. + * sysdeps/i386/i686/multiarch/varshift.c: Likewise. + +2017-03-07 Siddhesh Poyarekar + + [BZ #21209] + * elf/rtld.c (process_envvars): Ignore LD_HWCAP_MASK for + AT_SECURE processes. + * sysdeps/generic/unsecvars.h: Add LD_HWCAP_MASK. + * elf/tst-env-setuid.c (test_parent): Test LD_HWCAP_MASK. + (test_child): Likewise. + * elf/Makefile (tst-env-setuid-ENV): Add LD_HWCAP_MASK. + +2017-06-19 Florian Weimer + + * elf/rtld.c (audit_list_string): New variable. + (audit_list): Update comment. + (struct audit_list_iter): Define. + (audit_list_iter_init, audit_list_iter_next): New function. + (dl_main): Use struct audit_list_iter to process audit modules. + (process_dl_audit): Call dso_name_valid_for_suid. + (process_envvars): Set audit_list_string instead of calling + process_dl_audit. + +2017-06-19 Florian Weimer + + * elf/rtld.c (SECURE_NAME_LIMIT, SECURE_PATH_LIMIT): Define. + (dso_name_valid_for_suid): New function. + (handle_ld_preload): Likewise. + (dl_main): Call it. Remove alloca. + +2017-06-19 Florian Weimer + + [BZ #21624] + CVE-2017-1000366 + * elf/rtld.c (process_envvars): Ignore LD_LIBRARY_PATH for + __libc_enable_secure. + +2017-05-12 Florian Weimer + + [BZ #21386] + * sysdeps/nptl/fork.c (__libc_fork): Remove assertions on the + parent PID. The assertion in the child is incorrect with PID + namespaces. + +2017-03-15 Joseph Myers + + * sysdeps/x86/fpu/test-math-vector-sincos.h (INIT_VEC_PTRS_LOOP): + Use a union when storing pointers. + (VECTOR_WRAPPER_fFF_2): Do not take address of integer vector and + cast result when passing to INIT_VEC_PTRS_LOOP. + (VECTOR_WRAPPER_fFF_3): Likewise. + (VECTOR_WRAPPER_fFF_4): Likewise. + +2017-05-01 Adhemerval Zanella + + [BZ# 21182] + * string/test-memchr.c (do_test): Add BZ#21182 checks for address + near end of a page. + * sysdeps/i386/i686/multiarch/memchr-sse2.S (__memchr): Fix + overflow calculation. + +2017-04-28 H.J. Lu + + [BZ #21396] + * sysdeps/x86/cpu-features.c (init_cpu_features): Set + Prefer_No_AVX512 if AVX512ER isn't available. + * sysdeps/x86/cpu-features.h (bit_arch_Prefer_No_AVX512): New. + (index_arch_Prefer_No_AVX512): Likewise. + * sysdeps/x86_64/multiarch/memcpy.S (__new_memcpy): Don't use + AVX512 version if Prefer_No_AVX512 is set. + * sysdeps/x86_64/multiarch/memcpy_chk.S (__memcpy_chk): + Likewise. + * sysdeps/x86_64/multiarch/memmove.S (__libc_memmove): Likewise. + * sysdeps/x86_64/multiarch/memmove_chk.S (__memmove_chk): + Likewise. + * sysdeps/x86_64/multiarch/mempcpy.S (__mempcpy): Likewise. + * sysdeps/x86_64/multiarch/mempcpy_chk.S (__mempcpy_chk): + Likewise. + * sysdeps/x86_64/multiarch/memset.S (memset): Likewise. + * sysdeps/x86_64/multiarch/memset_chk.S (__memset_chk): + Likewise. + +2017-04-28 H.J. Lu + + * sysdeps/x86/cpu-features.c (init_cpu_features): Set + Prefer_No_VZEROUPPER if AVX512ER is available. + * sysdeps/x86/cpu-features.h + (bit_cpu_AVX512PF): New. + (bit_cpu_AVX512ER): Likewise. + (bit_cpu_AVX512CD): Likewise. + (bit_cpu_AVX512BW): Likewise. + (bit_cpu_AVX512VL): Likewise. + (index_cpu_AVX512PF): Likewise. + (index_cpu_AVX512ER): Likewise. + (index_cpu_AVX512CD): Likewise. + (index_cpu_AVX512BW): Likewise. + (index_cpu_AVX512VL): Likewise. + (reg_AVX512PF): Likewise. + (reg_AVX512ER): Likewise. + (reg_AVX512CD): Likewise. + (reg_AVX512BW): Likewise. + (reg_AVX512VL): Likewise. + +2017-04-11 Adhemerval Zanella + + * posix/globtest.sh: Add cleanup routine on trap 0. + +2017-04-07 H.J. Lu + + [BZ #21258] + * sysdeps/x86_64/dl-trampoline.S (_dl_runtime_resolve_opt): + Define only if _dl_runtime_resolve is defined to + _dl_runtime_resolve_sse_vex. + * sysdeps/x86_64/dl-trampoline.h (_dl_runtime_resolve_opt): + Fallthrough to _dl_runtime_resolve_sse_vex. + +2017-04-03 Mike Frysinger + + [BZ #21253] + * sysdeps/unix/sysv/linux/spawni.c (__spawnix): Increase argv_size + slack space by 32KiB. + +2017-04-03 Wladimir van der Laan + + [BZ# 21338] + * malloc/malloc.c: Call do_set_arena_max for M_ARENA_MAX + instead of incorrect do_set_arena_test + +2017-03-31 Slava Barinov + + [BZ #21289] + * io/fts.h (fts_set): Replace __REDIRECT with __REDIRECT_NTH. + +2017-03-20 Mike Frysinger + + [BZ #21275] + * sysdeps/unix/sysv/linux/spawni.c [__ia64__] (CLONE): Rename + __stack to __stackbase. + (STACK): Invert _STACK_GROWS_DOWN and _STACK_GROWS_UP order of + checks so we can include defined(__ia64__) first. + +2017-03-15 Mike Frysinger + + * sysdeps/x86_64/mempcpy_chk.S (__mempcpy_chk): Check for SHARED + instead of PIC. + +2017-03-15 John David Anglin + + * sysdeps/hppa/dl-machine.h (DL_STACK_END): Define. + (RTLD_START): Don't record stack end address in _dl_start_user. + +2017-03-02 Florian Weimer + + [BZ #21015] + * manual/install.texi (Configuring and compiling): Document + --enable-bind-now. + * Makeconfig [bind-now] (LDFLAGS-lib.so): Set. + (build-shlib-helper): Use $(LDFLAGS-lib.so). + (format.lds): Likewise. + [bind-now] (LDFLAGS-c.so): Remove. + * sysdeps/x86_64/localplt.data (libm.so): matherr relocation can + be R_X86_64_GLOB_DAT. + * sysdeps/unix/sysv/linux/i386/localplt.data (libm.so): matherr + relocation can be R_386_GLOB_DAT. + * sysdeps/unix/sysv/linux/alpha/localplt.data (libm.so): matherr + relocaiton can be R_ALPHA_GLOB_DAT. + * iconvdata/Makefile [bind-now] (LDFLAGS.so): Add -Wl,-z,now. + +2017-02-28 Florian Weimer + + [BZ #20257] + * inet/Makefile (routines): Add deadline. + (tests-static): Add tst-deadline. + * inet/net-internal.h (struct deadline_current_time) + (__deadline_current_time, struct deadline, __deadline_is_infinite) + (__deadline_elapsed, __deadline_first, __deadline_from_timeval) + (__deadline_to_ms, __is_timeval_valid_timeout): Declare. + * inet/deadline.c: New file. + * inet/tst-deadline.c: Likewise. + * sunrpc/Makefile (tests): Add tst-udp-nonblocking, + tst-udp-timeout, tst-udp-garbage. + (tst-udp-nonblocking, tst-udp-timeout): Link against libc.so + explicitly. + (tst-udp-garbage): Likewise. Also link against thread library. + * sunrpc/clnt_udp.c (struct cu_data): Mention in comment that the + struct layout is part of the ABI. + (clntudp_call): Rework timeout handling. + * sunrpc/tst-udp-garbage.c: New file. + * sunrpc/tst-udp-nonblocking.c: Likewise. + * sunrpc/tst-udp-timeout.c: Likewise. + +2017-02-27 Florian Weimer + + [BZ #21115] + * sunrpc/clnt_udp.c (clntudp_call): Free ancillary data later. + * sunrpc/Makefile (tests): Add tst-udp-error. + (tst-udp-error): Link against libc.so explicitly. + * sunrpc/tst-udp-error: New file. + +2017-02-08 Siddhesh Poyarekar + + [BZ #21109] + * elf/dl-tunable-types.h (tunable_callback_t): Accept + tunable_val_t as argument. + * elf/dl-tunables.c (__tunable_set_val): Add comment. + * malloc/arena.c (set_mallopt_check): Take tunable_val_t as + argument. + (DL_TUNABLE_CALLBACK_FNDECL): Likewise. + 2017-02-05 Siddhesh Poyarekar * version.h (RELEASE): Set to "stable" diff --git a/INSTALL b/INSTALL index 3b3fd121b2..e77cb2d4e2 100644 --- a/INSTALL +++ b/INSTALL @@ -146,6 +146,12 @@ will be used, and CFLAGS sets optimization options for the compiler. of routines called directly from assembler are excluded from this protection. +'--enable-bind-now' + Disable lazy binding for installed shared objects. This provides + additional security hardening because it enables full RELRO and a + read-only global offset table (GOT), at the cost of slightly + increased program load times. + '--enable-pt_chown' The file 'pt_chown' is a helper binary for 'grantpt' (*note Pseudo-Terminals: Allocation.) that is installed setuid root to fix diff --git a/Makeconfig b/Makeconfig index 97a15b569e..1c815113b9 100644 --- a/Makeconfig +++ b/Makeconfig @@ -386,6 +386,13 @@ LDFLAGS.so += $(hashstyle-LDFLAGS) LDFLAGS-rtld += $(hashstyle-LDFLAGS) endif +# If lazy relocations are disabled, add the -z now flag. Use +# LDFLAGS-lib.so instead of LDFLAGS.so, to avoid adding the flag to +# test modules. +ifeq ($(bind-now),yes) +LDFLAGS-lib.so += -Wl,-z,now +endif + # Command to run after every final link (executable or shared object). # This is invoked with $(call after-link,...), so it should operate on # the file $1. This can be set to do some sort of post-processing on diff --git a/Makerules b/Makerules index e9194e54cf..43343f03ee 100644 --- a/Makerules +++ b/Makerules @@ -127,6 +127,14 @@ $(common-objpfx)cstdlib: $(c++-cstdlib-header) $(common-objpfx)cmath: $(c++-cmath-header) $(INSTALL_DATA) $< $@T $(move-if-change) $@T $@ +ifneq (,$(c++-bits-std_abs-h)) +# Also make a copy of from GCC 7 to prevent it from +# including /usr/include/stdlib.h. +before-compile := $(common-objpfx)bits/std_abs.h $(before-compile) +$(common-objpfx)bits/std_abs.h: $(c++-bits-std_abs-h) + $(INSTALL_DATA) $< $@T + $(move-if-change) $@T $@ +endif endif before-compile := $(common-objpfx)libc-abis.h $(before-compile) @@ -588,7 +596,7 @@ $(LINK.o) -shared -static-libgcc -Wl,-O1 $(sysdep-LDFLAGS) \ $(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \ $(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \ -Wl,-soname=lib$(libprefix)$(@F:lib%.so=%).so$($(@F)-version) \ - $(LDFLAGS.so) $(LDFLAGS-$(@F:lib%.so=%).so) \ + $(LDFLAGS.so) $(LDFLAGS-lib.so) $(LDFLAGS-$(@F:lib%.so=%).so) \ -L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link) endef @@ -669,14 +677,17 @@ $(build-module-helper) -o $@ $(shlib-lds-flags) \ $(call after-link,$@) endef +# sofini.os must be placed last since it terminates .eh_frame section. build-module-helper-objlist = \ $(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\ $(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \ + $(elf-objpfx)sofini.os \ $(link-libc-deps),$^)) build-module-objlist = $(build-module-helper-objlist) $(LDLIBS-$(@F:%.so=%).so) build-shlib-objlist = $(build-module-helper-objlist) \ - $(LDLIBS-$(@F:lib%.so=%).so) + $(LDLIBS-$(@F:lib%.so=%).so) \ + $(filter $(elf-objpfx)sofini.os,$^) # Don't try to use -lc when making libc.so itself. # Also omits crti.o and crtn.o, which we do not want @@ -686,10 +697,6 @@ LDFLAGS-c.so = -nostdlib -nostartfiles LDLIBS-c.so += $(libc.so-gnulib) # Give libc.so an entry point and make it directly runnable itself. LDFLAGS-c.so += -e __libc_main -# If lazy relocation is disabled add the -z now flag. -ifeq ($(bind-now),yes) -LDFLAGS-c.so += -Wl,-z,now -endif # Pre-link the objects of libc_pic.a so that we can locally resolve # COMMON symbols before we link against ld.so. This is because ld.so # contains some of libc_pic.a already, which will prevent the COMMONs @@ -1104,7 +1111,8 @@ $(common-objpfx)format.lds: $(..)scripts/output-format.sed \ ifneq (unknown,$(output-format)) echo > $@.new 'OUTPUT_FORMAT($(output-format))' else - $(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) $(LDFLAGS.so) \ + $(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) \ + $(LDFLAGS.so) $(LDFLAGS-lib.so) \ -x c /dev/null -o $@.so -Wl,--verbose -v 2>&1 \ | sed -n -f $< > $@.new test -s $@.new diff --git a/NEWS b/NEWS index ec15dde761..7c44ba2a8a 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,60 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. +Version 2.25.1 + +Security related changes: + +* The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes, + to avoid fragmentation-based spoofing attacks. + + CVE-2017-15670: The glob function, when invoked with GLOB_TILDE, suffered + from a one-byte overflow during ~ operator processing (either on the stack + or the heap, depending on the length of the user name). + + CVE-2017-15671: The glob function, when invoked with GLOB_TILDE, + would sometimes fail to free memory allocated during ~ operator + processing, leading to a memory leak and, potentially, to a denial + of service. + + CVE-2017-15804: The glob function, when invoked with GLOB_TILDE and + without GLOB_NOESCAPE, could write past the end of a buffer while + unescaping user names. Reported by Tim Rühsen. + + CVE-2017-1000408: Incorrect array size computation in _dl_init_paths leads + to the allocation of too much memory. (This is not a security bug per se, + it is mentioned here only because of the CVE assignment.) Reported by + Qualys. + + CVE-2017-1000409: Buffer overflow in _dl_init_paths due to miscomputation + of the number of search path components. (This is not a security + vulnerability per se because no trust boundary is crossed if the fix for + CVE-2017-1000366 has been applied, but it is mentioned here only because + of the CVE assignment.) Reported by Qualys. + + CVE-2017-16997: Incorrect handling of RPATH or RUNPATH containing $ORIGIN + for AT_SECURE or SUID binaries could be used to load libraries from the + current directory. + +The following bugs are resolved with this release: + + [20257] sunrpc: clntudp_call does not enforce timeout when receiving data + [21015] Document and fix --enable-bind-now + [21109] Tunables broken on big-endian + [21115] sunrpc: Use-after-free in error path in clntudp_call + [21209] Ignore and remove LD_HWCAP_MASK for AT_SECURE programs + [21242] assert: Suppress pedantic warning caused by statement expression + [21265] x86-64: Use fxsave/xsave/xsavec in _dl_runtime_resolve + [21289] Fix symbol redirect for fts_set + [21298] rwlock can deadlock on frequent reader/writer phase switching + [21386] Assertion in fork for distinct parent PID is incorrect + [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366) + [21654] nss: Fix invalid cast in group merging + [21778] Robust mutex may deadlock + [21972] assert macro requires operator== (int) for its argument type + [22322] libc: [mips64] wrong bits/long-double.h installed + [22627] $ORIGIN in $LD_LIBRARY_PATH is substituted twice + Version 2.25 * The feature test macro __STDC_WANT_LIB_EXT2__, from ISO/IEC TR diff --git a/assert/Makefile b/assert/Makefile index 1c3be9b01f..9ec1be81a9 100644 --- a/assert/Makefile +++ b/assert/Makefile @@ -25,6 +25,15 @@ include ../Makeconfig headers := assert.h routines := assert assert-perr __assert -tests := test-assert test-assert-perr +tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++ include ../Rules + +ifeq ($(have-cxx-thread_local),yes) +CFLAGS-tst-assert-c++.o = -std=c++11 +LDLIBS-tst-assert-c++ = -lstdc++ +CFLAGS-tst-assert-g++.o = -std=gnu++11 +LDLIBS-tst-assert-g++ = -lstdc++ +else +tests-unsupported += tst-assert-c++ tst-assert-g++ +endif diff --git a/assert/assert.h b/assert/assert.h index 22f019537c..640c95c063 100644 --- a/assert/assert.h +++ b/assert/assert.h @@ -85,19 +85,29 @@ __END_DECLS /* When possible, define assert so that it does not add extra parentheses around EXPR. Otherwise, those added parentheses would suppress warnings we'd expect to be detected by gcc's -Wparentheses. */ -# if !defined __GNUC__ || defined __STRICT_ANSI__ +# if defined __cplusplus +# define assert(expr) \ + (static_cast (expr) \ + ? void (0) \ + : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION)) +# elif !defined __GNUC__ || defined __STRICT_ANSI__ # define assert(expr) \ ((expr) \ ? __ASSERT_VOID_CAST (0) \ : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION)) # else +/* The first occurrence of EXPR is not evaluated due to the sizeof, + but will trigger any pedantic warnings masked by the __extension__ + for the second occurrence. The ternary operator is required to + support function pointers and bit fields in this context, and to + suppress the evaluation of variable length arrays. */ # define assert(expr) \ - ({ \ + ((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \ if (expr) \ ; /* empty */ \ else \ __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \ - }) + })) # endif # ifdef __USE_GNU @@ -113,7 +123,7 @@ __END_DECLS C9x has a similar variable called __func__, but prefer the GCC one since it demangles C++ function names. */ # if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4) -# define __ASSERT_FUNCTION __PRETTY_FUNCTION__ +# define __ASSERT_FUNCTION __extension__ __PRETTY_FUNCTION__ # else # if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L # define __ASSERT_FUNCTION __func__ diff --git a/assert/tst-assert-c++.cc b/assert/tst-assert-c++.cc new file mode 100644 index 0000000000..12a5e690cb --- /dev/null +++ b/assert/tst-assert-c++.cc @@ -0,0 +1,78 @@ +/* Tests for interactions between C++ and assert. + Copyright (C) 2017 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 + +/* The C++ standard requires that if the assert argument is a constant + subexpression, then the assert itself is one, too. */ +constexpr int +check_constexpr () +{ + return (assert (true), 1); +} + +/* Objects of this class can be contextually converted to bool, but + cannot be compared to int. */ +struct no_int +{ + no_int () = default; + no_int (const no_int &) = delete; + + explicit operator bool () const + { + return true; + } + + bool operator! () const; /* No definition. */ + template bool operator== (T) const; /* No definition. */ + template bool operator!= (T) const; /* No definition. */ +}; + +/* This class tests that operator== is not used by assert. */ +struct bool_and_int +{ + bool_and_int () = default; + bool_and_int (const no_int &) = delete; + + explicit operator bool () const + { + return true; + } + + bool operator! () const; /* No definition. */ + template bool operator== (T) const; /* No definition. */ + template bool operator!= (T) const; /* No definition. */ +}; + +static int +do_test () +{ + { + no_int value; + assert (value); + } + + { + bool_and_int value; + assert (value); + } + + return 0; +} + +#include diff --git a/assert/tst-assert-g++.cc b/assert/tst-assert-g++.cc new file mode 100644 index 0000000000..8c06402825 --- /dev/null +++ b/assert/tst-assert-g++.cc @@ -0,0 +1,19 @@ +/* Tests for interactions between C++ and assert. GNU C++11 version. + Copyright (C) 2017 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 diff --git a/config.make.in b/config.make.in index 5836b32a72..709527da4f 100644 --- a/config.make.in +++ b/config.make.in @@ -47,6 +47,7 @@ sysincludes = @SYSINCLUDES@ c++-sysincludes = @CXX_SYSINCLUDES@ c++-cstdlib-header = @CXX_CSTDLIB_HEADER@ c++-cmath-header = @CXX_CMATH_HEADER@ +c++-bits-std_abs-h = @CXX_BITS_STD_ABS_H@ all-warnings = @all_warnings@ enable-werror = @enable_werror@ diff --git a/configure b/configure index eecd0ace74..ee637a7caf 100755 --- a/configure +++ b/configure @@ -634,6 +634,7 @@ BISON INSTALL_INFO PERL BASH_SHELL +CXX_BITS_STD_ABS_H CXX_CMATH_HEADER CXX_CSTDLIB_HEADER CXX_SYSINCLUDES @@ -5318,14 +5319,17 @@ fi # copy of those headers in Makerules. if test -n "$CXX"; then find_cxx_header () { - echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}" + echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \ + | sed -n "\,$1:,{s/:\$//;p}" } CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)" CXX_CMATH_HEADER="$(find_cxx_header cmath)" + CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)" fi + # Test if LD_LIBRARY_PATH contains the notation for the current directory # since this would lead to problems installing/building glibc. # LD_LIBRARY_PATH contains the current directory if one of the following diff --git a/configure.ac b/configure.ac index 4a77411b71..d288ff43cd 100644 --- a/configure.ac +++ b/configure.ac @@ -1176,13 +1176,16 @@ AC_SUBST(CXX_SYSINCLUDES) # copy of those headers in Makerules. if test -n "$CXX"; then find_cxx_header () { - echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}" + echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \ + | sed -n "\,$1:,{s/:\$//;p}" } CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)" CXX_CMATH_HEADER="$(find_cxx_header cmath)" + CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)" fi AC_SUBST(CXX_CSTDLIB_HEADER) AC_SUBST(CXX_CMATH_HEADER) +AC_SUBST(CXX_BITS_STD_ABS_H) # Test if LD_LIBRARY_PATH contains the notation for the current directory # since this would lead to problems installing/building glibc. diff --git a/elf/Makefile b/elf/Makefile index 61abeb59ee..cc4aeb25b6 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1398,6 +1398,7 @@ $(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \ $(objpfx)tst-nodelete-dlclose-plugin.so -tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 +tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \ + LD_HWCAP_MASK=0xffffffff tst-env-setuid-tunables-ENV = \ GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096 diff --git a/elf/dl-load.c b/elf/dl-load.c index a5318f9c8d..58e7220050 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -103,7 +104,9 @@ static size_t ncapstr attribute_relro; static size_t max_capstrlen attribute_relro; -/* Get the generated information about the trusted directories. */ +/* Get the generated information about the trusted directories. Use + an array of concatenated strings to avoid relocations. See + gen-trusted-dirs.awk. */ #include "trusted-dirs.h" static const char system_dirs[] = SYSTEM_DIRS; @@ -111,9 +114,7 @@ static const size_t system_dirs_len[] = { SYSTEM_DIRS_LEN }; -#define nsystem_dirs_len \ - (sizeof (system_dirs_len) / sizeof (system_dirs_len[0])) - +#define nsystem_dirs_len array_length (system_dirs_len) static bool is_trusted_path (const char *path, size_t len) @@ -433,31 +434,40 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, { char *cp; size_t nelems = 0; - char *to_free; while ((cp = __strsep (&rpath, sep)) != NULL) { struct r_search_path_elem *dirp; + char *to_free = NULL; + size_t len = 0; - to_free = cp = expand_dynamic_string_token (l, cp, 1); + /* `strsep' can pass an empty string. */ + if (*cp != '\0') + { + to_free = cp = expand_dynamic_string_token (l, cp, 1); - size_t len = strlen (cp); + /* expand_dynamic_string_token can return NULL in case of empty + path or memory allocation failure. */ + if (cp == NULL) + continue; - /* `strsep' can pass an empty string. This has to be - interpreted as `use the current directory'. */ - if (len == 0) - { - static const char curwd[] = "./"; - cp = (char *) curwd; - } + /* Compute the length after dynamic string token expansion and + ignore empty paths. */ + len = strlen (cp); + if (len == 0) + { + free (to_free); + continue; + } - /* Remove trailing slashes (except for "/"). */ - while (len > 1 && cp[len - 1] == '/') - --len; + /* Remove trailing slashes (except for "/"). */ + while (len > 1 && cp[len - 1] == '/') + --len; - /* Now add one if there is none so far. */ - if (len > 0 && cp[len - 1] != '/') - cp[len++] = '/'; + /* Now add one if there is none so far. */ + if (len > 0 && cp[len - 1] != '/') + cp[len++] = '/'; + } /* Make sure we don't use untrusted directories if we run SUID. */ if (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len)) @@ -621,6 +631,14 @@ decompose_rpath (struct r_search_path_struct *sps, necessary. */ free (copy); + /* There is no path after expansion. */ + if (result[0] == NULL) + { + free (result); + sps->dirs = (struct r_search_path_elem **) -1; + return false; + } + sps->dirs = result; /* The caller will change this value if we haven't used a real malloc. */ sps->malloced = 1; @@ -688,9 +706,8 @@ _dl_init_paths (const char *llp) + ncapstr * sizeof (enum r_dir_status)) / sizeof (struct r_search_path_elem)); - rtld_search_dirs.dirs[0] = (struct r_search_path_elem *) - malloc ((sizeof (system_dirs) / sizeof (system_dirs[0])) - * round_size * sizeof (struct r_search_path_elem)); + rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size + * sizeof (*rtld_search_dirs.dirs[0])); if (rtld_search_dirs.dirs[0] == NULL) { errstring = N_("cannot create cache for search path"); @@ -776,37 +793,14 @@ _dl_init_paths (const char *llp) if (llp != NULL && *llp != '\0') { - size_t nllp; - const char *cp = llp; - char *llp_tmp; - -#ifdef SHARED - /* Expand DSTs. */ - size_t cnt = DL_DST_COUNT (llp, 1); - if (__glibc_likely (cnt == 0)) - llp_tmp = strdupa (llp); - else - { - /* Determine the length of the substituted string. */ - size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt); - - /* Allocate the necessary memory. */ - llp_tmp = (char *) alloca (total + 1); - llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1); - } -#else - llp_tmp = strdupa (llp); -#endif + char *llp_tmp = strdupa (llp); /* Decompose the LD_LIBRARY_PATH contents. First determine how many elements it has. */ - nllp = 1; - while (*cp) - { - if (*cp == ':' || *cp == ';') - ++nllp; - ++cp; - } + size_t nllp = 1; + for (const char *cp = llp_tmp; *cp != '\0'; ++cp) + if (*cp == ':' || *cp == ';') + ++nllp; env_path_list.dirs = (struct r_search_path_elem **) malloc ((nllp + 1) * sizeof (struct r_search_path_elem *)); diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h index a986f0b593..37a4e8021f 100644 --- a/elf/dl-tunable-types.h +++ b/elf/dl-tunable-types.h @@ -21,8 +21,6 @@ # define _TUNABLE_TYPES_H_ #include -typedef void (*tunable_callback_t) (void *); - typedef enum { TUNABLE_TYPE_INT_32, @@ -43,6 +41,8 @@ typedef union const char *strval; } tunable_val_t; +typedef void (*tunable_callback_t) (tunable_val_t *); + /* Security level for tunables. This decides what to do with individual tunables for AT_SECURE binaries. */ typedef enum diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c index a8d53d6a31..e42aa67003 100644 --- a/elf/dl-tunables.c +++ b/elf/dl-tunables.c @@ -455,6 +455,8 @@ __tunable_set_val (tunable_id_t id, void *valp, tunable_callback_t callback) if (cur->strval == NULL) return; + /* Caller does not need the value, just call the callback with our tunable + value. */ if (valp == NULL) goto cb; diff --git a/elf/rtld.c b/elf/rtld.c index a036ece956..9362a21e73 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -99,14 +99,121 @@ uintptr_t __pointer_chk_guard_local strong_alias (__pointer_chk_guard_local, __pointer_chk_guard) #endif +/* Length limits for names and paths, to protect the dynamic linker, + particularly when __libc_enable_secure is active. */ +#ifdef NAME_MAX +# define SECURE_NAME_LIMIT NAME_MAX +#else +# define SECURE_NAME_LIMIT 255 +#endif +#ifdef PATH_MAX +# define SECURE_PATH_LIMIT PATH_MAX +#else +# define SECURE_PATH_LIMIT 1024 +#endif + +/* Check that AT_SECURE=0, or that the passed name does not contain + directories and is not overly long. Reject empty names + unconditionally. */ +static bool +dso_name_valid_for_suid (const char *p) +{ + if (__glibc_unlikely (__libc_enable_secure)) + { + /* Ignore pathnames with directories for AT_SECURE=1 + programs, and also skip overlong names. */ + size_t len = strlen (p); + if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL) + return false; + } + return *p != '\0'; +} -/* List of auditing DSOs. */ +/* LD_AUDIT variable contents. Must be processed before the + audit_list below. */ +const char *audit_list_string; + +/* Cyclic list of auditing DSOs. audit_list->next is the first + element. */ static struct audit_list { const char *name; struct audit_list *next; } *audit_list; +/* Iterator for audit_list_string followed by audit_list. */ +struct audit_list_iter +{ + /* Tail of audit_list_string still needing processing, or NULL. */ + const char *audit_list_tail; + + /* The list element returned in the previous iteration. NULL before + the first element. */ + struct audit_list *previous; + + /* Scratch buffer for returning a name which is part of + audit_list_string. */ + char fname[SECURE_NAME_LIMIT]; +}; + +/* Initialize an audit list iterator. */ +static void +audit_list_iter_init (struct audit_list_iter *iter) +{ + iter->audit_list_tail = audit_list_string; + iter->previous = NULL; +} + +/* Iterate through both audit_list_string and audit_list. */ +static const char * +audit_list_iter_next (struct audit_list_iter *iter) +{ + if (iter->audit_list_tail != NULL) + { + /* First iterate over audit_list_string. */ + while (*iter->audit_list_tail != '\0') + { + /* Split audit list at colon. */ + size_t len = strcspn (iter->audit_list_tail, ":"); + if (len > 0 && len < sizeof (iter->fname)) + { + memcpy (iter->fname, iter->audit_list_tail, len); + iter->fname[len] = '\0'; + } + else + /* Do not return this name to the caller. */ + iter->fname[0] = '\0'; + + /* Skip over the substring and the following delimiter. */ + iter->audit_list_tail += len; + if (*iter->audit_list_tail == ':') + ++iter->audit_list_tail; + + /* If the name is valid, return it. */ + if (dso_name_valid_for_suid (iter->fname)) + return iter->fname; + /* Otherwise, wrap around and try the next name. */ + } + /* Fall through to the procesing of audit_list. */ + } + + if (iter->previous == NULL) + { + if (audit_list == NULL) + /* No pre-parsed audit list. */ + return NULL; + /* Start of audit list. The first list element is at + audit_list->next (cyclic list). */ + iter->previous = audit_list->next; + return iter->previous->name; + } + if (iter->previous == audit_list) + /* Cyclic list wrap-around. */ + return NULL; + iter->previous = iter->previous->next; + return iter->previous->name; +} + #ifndef HAVE_INLINED_SYSCALLS /* Set nonzero during loading and initialization of executable and libraries, cleared before the executable's entry point runs. This @@ -716,6 +823,42 @@ static const char *preloadlist attribute_relro; /* Nonzero if information about versions has to be printed. */ static int version_info attribute_relro; +/* The LD_PRELOAD environment variable gives list of libraries + separated by white space or colons that are loaded before the + executable's dependencies and prepended to the global scope list. + (If the binary is running setuid all elements containing a '/' are + ignored since it is insecure.) Return the number of preloads + performed. */ +unsigned int +handle_ld_preload (const char *preloadlist, struct link_map *main_map) +{ + unsigned int npreloads = 0; + const char *p = preloadlist; + char fname[SECURE_PATH_LIMIT]; + + while (*p != '\0') + { + /* Split preload list at space/colon. */ + size_t len = strcspn (p, " :"); + if (len > 0 && len < sizeof (fname)) + { + memcpy (fname, p, len); + fname[len] = '\0'; + } + else + fname[0] = '\0'; + + /* Skip over the substring and the following delimiter. */ + p += len; + if (*p != '\0') + ++p; + + if (dso_name_valid_for_suid (fname)) + npreloads += do_preload (fname, main_map, "LD_PRELOAD"); + } + return npreloads; +} + static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum, @@ -1238,11 +1381,13 @@ of this helper program; chances are you did not intend to run this program.\n\ GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); /* If we have auditing DSOs to load, do it now. */ - if (__glibc_unlikely (audit_list != NULL)) + bool need_security_init = true; + if (__glibc_unlikely (audit_list != NULL) + || __glibc_unlikely (audit_list_string != NULL)) { - /* Iterate over all entries in the list. The order is important. */ struct audit_ifaces *last_audit = NULL; - struct audit_list *al = audit_list->next; + struct audit_list_iter al_iter; + audit_list_iter_init (&al_iter); /* Since we start using the auditing DSOs right away we need to initialize the data structures now. */ @@ -1253,9 +1398,14 @@ of this helper program; chances are you did not intend to run this program.\n\ use different values (especially the pointer guard) and will fail later on. */ security_init (); + need_security_init = false; - do + while (true) { + const char *name = audit_list_iter_next (&al_iter); + if (name == NULL) + break; + int tls_idx = GL(dl_tls_max_dtv_idx); /* Now it is time to determine the layout of the static TLS @@ -1264,7 +1414,7 @@ of this helper program; chances are you did not intend to run this program.\n\ no DF_STATIC_TLS bit is set. The reason is that we know glibc will use the static model. */ struct dlmopen_args dlmargs; - dlmargs.fname = al->name; + dlmargs.fname = name; dlmargs.map = NULL; const char *objname; @@ -1277,7 +1427,7 @@ of this helper program; chances are you did not intend to run this program.\n\ not_loaded: _dl_error_printf ("\ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", - al->name, err_str); + name, err_str); if (malloced) free ((char *) err_str); } @@ -1381,10 +1531,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", goto not_loaded; } } - - al = al->next; } - while (al != audit_list->next); /* If we have any auditing modules, announce that we already have two objects loaded. */ @@ -1462,23 +1609,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", if (__glibc_unlikely (preloadlist != NULL)) { - /* The LD_PRELOAD environment variable gives list of libraries - separated by white space or colons that are loaded before the - executable's dependencies and prepended to the global scope - list. If the binary is running setuid all elements - containing a '/' are ignored since it is insecure. */ - char *list = strdupa (preloadlist); - char *p; - HP_TIMING_NOW (start); - - /* Prevent optimizing strsep. Speed is not important here. */ - while ((p = (strsep) (&list, " :")) != NULL) - if (p[0] != '\0' - && (__builtin_expect (! __libc_enable_secure, 1) - || strchr (p, '/') == NULL)) - npreloads += do_preload (p, main_map, "LD_PRELOAD"); - + npreloads += handle_ld_preload (preloadlist, main_map); HP_TIMING_NOW (stop); HP_TIMING_DIFF (diff, start, stop); HP_TIMING_ACCUM_NT (load_time, diff); @@ -1663,7 +1795,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", if (tcbp == NULL) tcbp = init_tls (); - if (__glibc_likely (audit_list == NULL)) + if (__glibc_likely (need_security_init)) /* Initialize security features. But only if we have not done it earlier. */ security_init (); @@ -2294,9 +2426,7 @@ process_dl_audit (char *str) char *p; while ((p = (strsep) (&str, ":")) != NULL) - if (p[0] != '\0' - && (__builtin_expect (! __libc_enable_secure, 1) - || strchr (p, '/') == NULL)) + if (dso_name_valid_for_suid (p)) { /* This is using the local malloc, not the system malloc. The memory can never be freed. */ @@ -2360,7 +2490,7 @@ process_envvars (enum mode *modep) break; } if (memcmp (envline, "AUDIT", 5) == 0) - process_dl_audit (&envline[6]); + audit_list_string = &envline[6]; break; case 7: @@ -2404,7 +2534,8 @@ process_envvars (enum mode *modep) case 10: /* Mask for the important hardware capabilities. */ - if (memcmp (envline, "HWCAP_MASK", 10) == 0) + if (!__libc_enable_secure + && memcmp (envline, "HWCAP_MASK", 10) == 0) GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL, 0, 0); break; @@ -2418,7 +2549,8 @@ process_envvars (enum mode *modep) case 12: /* The library search path. */ - if (memcmp (envline, "LIBRARY_PATH", 12) == 0) + if (!__libc_enable_secure + && memcmp (envline, "LIBRARY_PATH", 12) == 0) { library_path = &envline[13]; break; diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c index 6ec3fa5874..eec408eb5d 100644 --- a/elf/tst-env-setuid.c +++ b/elf/tst-env-setuid.c @@ -213,6 +213,12 @@ test_child (void) return 1; } + if (getenv ("LD_HWCAP_MASK") != NULL) + { + printf ("LD_HWCAP_MASK still set\n"); + return 1; + } + return 0; } #endif @@ -233,6 +239,12 @@ test_parent (void) return 1; } + if (getenv ("LD_HWCAP_MASK") == NULL) + { + printf ("LD_HWCAP_MASK lost\n"); + return 1; + } + return 0; } #endif diff --git a/grp/grp-merge.c b/grp/grp-merge.c index 77c494d159..035e7a604b 100644 --- a/grp/grp-merge.c +++ b/grp/grp-merge.c @@ -85,6 +85,14 @@ __copy_grp (const struct group srcgrp, const size_t buflen, } members[i] = NULL; + /* Align for pointers. We can't simply align C because we need to + align destbuf[c]. */ + if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0) + { + uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1); + c += __alignof__(char **) - mis_align; + } + /* Copy the pointers from the members array into the buffer and assign them to the gr_mem member of destgrp. */ destgrp->gr_mem = (char **) &destbuf[c]; @@ -129,7 +137,7 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, /* Get the count of group members from the last sizeof (size_t) bytes in the mergegrp buffer. */ - savedmemcount = (size_t) *(savedend - sizeof (size_t)); + savedmemcount = *(size_t *) (savedend - sizeof (size_t)); /* Get the count of new members to add. */ for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++) @@ -168,6 +176,14 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, /* Add the NULL-terminator. */ members[savedmemcount + memcount] = NULL; + /* Align for pointers. We can't simply align C because we need to + align savedbuf[c]. */ + if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0) + { + uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1); + c += __alignof__(char **) - mis_align; + } + /* Copy the member array back into the buffer after the member list and free the member array. */ savedgrp->gr_mem = (char **) &savedbuf[c]; diff --git a/iconvdata/Makefile b/iconvdata/Makefile index 04157b25c5..e4845871f5 100644 --- a/iconvdata/Makefile +++ b/iconvdata/Makefile @@ -63,6 +63,11 @@ modules := ISO8859-1 ISO8859-2 ISO8859-3 ISO8859-4 ISO8859-5 \ MAC-CENTRALEUROPE KOI8-RU ISO8859-9E \ CP770 CP771 CP772 CP773 CP774 +# If lazy binding is disabled, use BIND_NOW for the gconv modules. +ifeq ($(bind-now),yes) +LDFLAGS.so += -Wl,-z,now +endif + modules.so := $(addsuffix .so, $(modules)) ifeq (yes,$(build-shared)) diff --git a/include/array_length.h b/include/array_length.h new file mode 100644 index 0000000000..cb4a8b2a56 --- /dev/null +++ b/include/array_length.h @@ -0,0 +1,36 @@ +/* The array_length and array_end macros. + Copyright (C) 2017 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 _ARRAY_LENGTH_H +#define _ARRAY_LENGTH_H + +/* array_length (VAR) is the number of elements in the array VAR. VAR + must evaluate to an array, not a pointer. */ +#define array_length(var) \ + __extension__ ({ \ + _Static_assert (!__builtin_types_compatible_p \ + (__typeof (var), __typeof (&(var)[0])), \ + "argument must be an array"); \ + sizeof (var) / sizeof ((var)[0]); \ + }) + +/* array_end (VAR) is a pointer one past the end of the array VAR. + VAR must evaluate to an array, not a pointer. */ +#define array_end(var) (&(var)[array_length (var)]) + +#endif /* _ARRAY_LENGTH_H */ diff --git a/include/resolv.h b/include/resolv.h index 95dcd3ca37..e8f477cd86 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -37,8 +37,6 @@ extern void res_pquery (const res_state __statp, const unsigned char *__msg, extern int res_ourserver_p (const res_state __statp, const struct sockaddr_in6 *__inp); extern void __res_iclose (res_state statp, bool free_addr); -extern int __res_nopt(res_state statp, int n0, unsigned char *buf, int buflen, - int anslen); libc_hidden_proto (__res_ninit) libc_hidden_proto (__res_maybe_init) libc_hidden_proto (__res_nclose) @@ -91,7 +89,6 @@ libresolv_hidden_proto (__res_nameinquery) libresolv_hidden_proto (__res_queriesmatch) libresolv_hidden_proto (__res_nsend) libresolv_hidden_proto (__b64_ntop) -libresolv_hidden_proto (__res_nopt) libresolv_hidden_proto (__dn_count_labels) libresolv_hidden_proto (__p_secstodate) diff --git a/inet/Makefile b/inet/Makefile index 010792af8f..6a7d3e0664 100644 --- a/inet/Makefile +++ b/inet/Makefile @@ -45,14 +45,18 @@ routines := htonl htons \ in6_addr getnameinfo if_index ifaddrs inet6_option \ getipv4sourcefilter setipv4sourcefilter \ getsourcefilter setsourcefilter inet6_opt inet6_rth \ - inet6_scopeid_pton + inet6_scopeid_pton deadline aux := check_pf check_native ifreq tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \ tst-gethnm test-ifaddrs bug-if1 test-inet6_opt tst-ether_line \ tst-getni1 tst-getni2 tst-inet6_rth tst-checks tst-checks-posix \ - tst-sockaddr tst-inet6_scopeid_pton test-hnto-types + tst-sockaddr tst-inet6_scopeid_pton test-hnto-types tst-deadline + +# tst-deadline must be linked statically so that we can access +# internal functions. +tests-static += tst-deadline include ../Rules diff --git a/inet/deadline.c b/inet/deadline.c new file mode 100644 index 0000000000..c1fa415a39 --- /dev/null +++ b/inet/deadline.c @@ -0,0 +1,122 @@ +/* Computing deadlines for timeouts. + Copyright (C) 2017 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 + +struct deadline_current_time internal_function +__deadline_current_time (void) +{ + struct deadline_current_time result; + if (__clock_gettime (CLOCK_MONOTONIC, &result.current) != 0) + { + struct timeval current_tv; + if (__gettimeofday (¤t_tv, NULL) == 0) + __libc_fatal ("Fatal error: gettimeofday system call failed\n"); + result.current.tv_sec = current_tv.tv_sec; + result.current.tv_nsec = current_tv.tv_usec * 1000; + } + assert (result.current.tv_sec >= 0); + return result; +} + +/* A special deadline value for which __deadline_is_infinite is + true. */ +static inline struct deadline +infinite_deadline (void) +{ + return (struct deadline) { { -1, -1 } }; +} + +struct deadline internal_function +__deadline_from_timeval (struct deadline_current_time current, + struct timeval tv) +{ + assert (__is_timeval_valid_timeout (tv)); + + /* Compute second-based deadline. Perform the addition in + uintmax_t, which is unsigned, to simply overflow detection. */ + uintmax_t sec = current.current.tv_sec; + sec += tv.tv_sec; + if (sec < (uintmax_t) tv.tv_sec) + return infinite_deadline (); + + /* Compute nanosecond deadline. */ + int nsec = current.current.tv_nsec + tv.tv_usec * 1000; + if (nsec >= 1000 * 1000 * 1000) + { + /* Carry nanosecond overflow to seconds. */ + nsec -= 1000 * 1000 * 1000; + if (sec + 1 < sec) + return infinite_deadline (); + ++sec; + } + /* This uses a GCC extension, otherwise these casts for detecting + overflow would not be defined. */ + if ((time_t) sec < 0 || sec != (uintmax_t) (time_t) sec) + return infinite_deadline (); + + return (struct deadline) { { sec, nsec } }; +} + +int internal_function +__deadline_to_ms (struct deadline_current_time current, + struct deadline deadline) +{ + if (__deadline_is_infinite (deadline)) + return INT_MAX; + + if (current.current.tv_sec > deadline.absolute.tv_sec + || (current.current.tv_sec == deadline.absolute.tv_sec + && current.current.tv_nsec >= deadline.absolute.tv_nsec)) + return 0; + time_t sec = deadline.absolute.tv_sec - current.current.tv_sec; + if (sec >= INT_MAX) + /* This value will overflow below. */ + return INT_MAX; + int nsec = deadline.absolute.tv_nsec - current.current.tv_nsec; + if (nsec < 0) + { + /* Borrow from the seconds field. */ + assert (sec > 0); + --sec; + nsec += 1000 * 1000 * 1000; + } + + /* Prepare for rounding up to milliseconds. */ + nsec += 999999; + if (nsec > 1000 * 1000 * 1000) + { + assert (sec < INT_MAX); + ++sec; + nsec -= 1000 * 1000 * 1000; + } + + unsigned int msec = nsec / (1000 * 1000); + if (sec > INT_MAX / 1000) + return INT_MAX; + msec += sec * 1000; + if (msec > INT_MAX) + return INT_MAX; + return msec; +} diff --git a/inet/net-internal.h b/inet/net-internal.h index 087597ed99..2b2632c7ba 100644 --- a/inet/net-internal.h +++ b/inet/net-internal.h @@ -20,11 +20,100 @@ #define _NET_INTERNAL_H 1 #include +#include #include +#include int __inet6_scopeid_pton (const struct in6_addr *address, const char *scope, uint32_t *result) internal_function attribute_hidden; libc_hidden_proto (__inet6_scopeid_pton) + +/* Deadline handling for enforcing timeouts. + + Code should call __deadline_current_time to obtain the current time + and cache it locally. The cache needs updating after every + long-running or potentially blocking operation. Deadlines relative + to the current time can be computed using __deadline_from_timeval. + The deadlines may have to be recomputed in response to certain + events (such as an incoming packet), but they are absolute (not + relative to the current time). A timeout suitable for use with the + poll function can be computed from such a deadline using + __deadline_to_ms. + + The fields in the structs defined belowed should only be used + within the implementation. */ + +/* Cache of the current time. Used to compute deadlines from relative + timeouts and vice versa. */ +struct deadline_current_time +{ + struct timespec current; +}; + +/* Return the current time. Terminates the process if the current + time is not available. */ +struct deadline_current_time __deadline_current_time (void) + internal_function attribute_hidden; + +/* Computed absolute deadline. */ +struct deadline +{ + struct timespec absolute; +}; + + +/* For internal use only. */ +static inline bool +__deadline_is_infinite (struct deadline deadline) +{ + return deadline.absolute.tv_nsec < 0; +} + +/* Return true if the current time is at the deadline or past it. */ +static inline bool +__deadline_elapsed (struct deadline_current_time current, + struct deadline deadline) +{ + return !__deadline_is_infinite (deadline) + && (current.current.tv_sec > deadline.absolute.tv_sec + || (current.current.tv_sec == deadline.absolute.tv_sec + && current.current.tv_nsec >= deadline.absolute.tv_nsec)); +} + +/* Return the deadline which occurs first. */ +static inline struct deadline +__deadline_first (struct deadline left, struct deadline right) +{ + if (__deadline_is_infinite (right) + || left.absolute.tv_sec < right.absolute.tv_sec + || (left.absolute.tv_sec == right.absolute.tv_sec + && left.absolute.tv_nsec < right.absolute.tv_nsec)) + return left; + else + return right; +} + +/* Add TV to the current time and return it. Returns a special + infinite absolute deadline on overflow. */ +struct deadline __deadline_from_timeval (struct deadline_current_time, + struct timeval tv) + internal_function attribute_hidden; + +/* Compute the number of milliseconds until the specified deadline, + from the current time in the argument. The result is mainly for + use with poll. If the deadline has already passed, return 0. If + the result would overflow an int, return INT_MAX. */ +int __deadline_to_ms (struct deadline_current_time, struct deadline) + internal_function attribute_hidden; + +/* Return true if TV.tv_sec is non-negative and TV.tv_usec is in the + interval [0, 999999]. */ +static inline bool +__is_timeval_valid_timeout (struct timeval tv) +{ + return tv.tv_sec >= 0 && tv.tv_usec >= 0 && tv.tv_usec < 1000 * 1000; +} + #endif /* _NET_INTERNAL_H */ diff --git a/inet/tst-deadline.c b/inet/tst-deadline.c new file mode 100644 index 0000000000..ed04345c35 --- /dev/null +++ b/inet/tst-deadline.c @@ -0,0 +1,188 @@ +/* Tests for computing deadlines for timeouts. + Copyright (C) 2017 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 + +/* Find the maximum value which can be represented in a time_t. */ +static time_t +time_t_max (void) +{ + _Static_assert (0 > (time_t) -1, "time_t is signed"); + uintmax_t current = 1; + while (true) + { + uintmax_t next = current * 2; + /* This cannot happen because time_t is signed. */ + TEST_VERIFY_EXIT (next > current); + ++next; + if ((time_t) next < 0 || next != (uintmax_t) (time_t) next) + /* Value cannot be represented in time_t. Return the previous + value. */ + return current; + current = next; + } +} + +static int +do_test (void) +{ + { + struct deadline_current_time current_time = __deadline_current_time (); + TEST_VERIFY (current_time.current.tv_sec >= 0); + current_time = __deadline_current_time (); + /* Due to CLOCK_MONOTONIC, either seconds or nanoseconds are + greater than zero. This is also true for the gettimeofday + fallback. */ + TEST_VERIFY (current_time.current.tv_sec >= 0); + TEST_VERIFY (current_time.current.tv_sec > 0 + || current_time.current.tv_nsec > 0); + } + + /* Check basic computations of deadlines. */ + struct deadline_current_time current_time = { { 1, 123456789 } }; + struct deadline deadline = __deadline_from_timeval + (current_time, (struct timeval) { 0, 1 }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 123457789); + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1); + + deadline = __deadline_from_timeval + (current_time, ((struct timeval) { 0, 2 })); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 123458789); + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1); + + deadline = __deadline_from_timeval + (current_time, ((struct timeval) { 1, 0 })); + TEST_VERIFY (deadline.absolute.tv_sec == 2); + TEST_VERIFY (deadline.absolute.tv_nsec == 123456789); + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000); + + /* Check if timeouts are correctly rounded up to the next + millisecond. */ + for (int i = 0; i < 999999; ++i) + { + ++current_time.current.tv_nsec; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000); + } + + /* A full millisecond has elapsed, so the time to the deadline is + now less than 1000. */ + ++current_time.current.tv_nsec; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999); + + /* Check __deadline_to_ms carry-over. */ + current_time = (struct deadline_current_time) { { 9, 123456789 } }; + deadline = (struct deadline) { { 10, 122456789 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999); + deadline = (struct deadline) { { 10, 122456790 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000); + deadline = (struct deadline) { { 10, 123456788 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000); + deadline = (struct deadline) { { 10, 123456789 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000); + + /* Check __deadline_to_ms overflow. */ + deadline = (struct deadline) { { INT_MAX - 1, 1 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == INT_MAX); + + /* Check __deadline_to_ms for elapsed deadlines. */ + current_time = (struct deadline_current_time) { { 9, 123456789 } }; + deadline.absolute = current_time.current; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0); + current_time = (struct deadline_current_time) { { 9, 123456790 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0); + current_time = (struct deadline_current_time) { { 10, 0 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0); + current_time = (struct deadline_current_time) { { 10, 123456788 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0); + current_time = (struct deadline_current_time) { { 10, 123456789 } }; + TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0); + + /* Check carry-over in __deadline_from_timeval. */ + current_time = (struct deadline_current_time) { { 9, 998000001 } }; + for (int i = 0; i < 2000; ++i) + { + deadline = __deadline_from_timeval + (current_time, (struct timeval) { 1, i }); + TEST_VERIFY (deadline.absolute.tv_sec == 10); + TEST_VERIFY (deadline.absolute.tv_nsec == 998000001 + i * 1000); + } + for (int i = 2000; i < 3000; ++i) + { + deadline = __deadline_from_timeval + (current_time, (struct timeval) { 2, i }); + TEST_VERIFY (deadline.absolute.tv_sec == 12); + TEST_VERIFY (deadline.absolute.tv_nsec == 1 + (i - 2000) * 1000); + } + + /* Check infinite deadlines. */ + deadline = __deadline_from_timeval + ((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1000 } }, + (struct timeval) { time_t_max (), 1 }); + TEST_VERIFY (__deadline_is_infinite (deadline)); + deadline = __deadline_from_timeval + ((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1001 } }, + (struct timeval) { time_t_max (), 1 }); + TEST_VERIFY (!__deadline_is_infinite (deadline)); + deadline = __deadline_from_timeval + ((struct deadline_current_time) + { { time_t_max (), 1000 * 1000 * 1000 - 1000 } }, + (struct timeval) { 0, 1 }); + TEST_VERIFY (__deadline_is_infinite (deadline)); + deadline = __deadline_from_timeval + ((struct deadline_current_time) + { { time_t_max () / 2 + 1, 0 } }, + (struct timeval) { time_t_max () / 2 + 1, 0 }); + TEST_VERIFY (__deadline_is_infinite (deadline)); + + /* Check __deadline_first behavior. */ + deadline = __deadline_first + ((struct deadline) { { 1, 2 } }, + (struct deadline) { { 1, 3 } }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 2); + deadline = __deadline_first + ((struct deadline) { { 1, 3 } }, + (struct deadline) { { 1, 2 } }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 2); + deadline = __deadline_first + ((struct deadline) { { 1, 2 } }, + (struct deadline) { { 2, 1 } }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 2); + deadline = __deadline_first + ((struct deadline) { { 1, 2 } }, + (struct deadline) { { 2, 4 } }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 2); + deadline = __deadline_first + ((struct deadline) { { 2, 4 } }, + (struct deadline) { { 1, 2 } }); + TEST_VERIFY (deadline.absolute.tv_sec == 1); + TEST_VERIFY (deadline.absolute.tv_nsec == 2); + + return 0; +} + +#include diff --git a/io/fts.h b/io/fts.h index b9cff534e9..ab15567001 100644 --- a/io/fts.h +++ b/io/fts.h @@ -193,7 +193,7 @@ FTS *__REDIRECT (fts_open, (char * const *, int, int (*)(const FTSENT **, const FTSENT **)), fts64_open); FTSENT *__REDIRECT (fts_read, (FTS *), fts64_read); -int __REDIRECT (fts_set, (FTS *, FTSENT *, int), fts64_set) __THROW; +int __REDIRECT_NTH (fts_set, (FTS *, FTSENT *, int), fts64_set); # else # define fts_children fts64_children # define fts_close fts64_close diff --git a/localedata/ChangeLog b/localedata/ChangeLog index 0cdb097ab6..127c1cfb35 100644 --- a/localedata/ChangeLog +++ b/localedata/ChangeLog @@ -1,3 +1,11 @@ +2017-06-11 Santhosh Thottingal + + [BZ #19922] + * locales/iso14651_t1_common: Add collation rules for U+07DA to U+07DF. + + [BZ #19919] + * locales/iso14651_t1_common: Correct collation of U+0D36 and U+0D37. + 2017-01-01 Joseph Myers * All files with FSF copyright notices: Update copyright dates diff --git a/localedata/locales/iso14651_t1_common b/localedata/locales/iso14651_t1_common index eef75ba65e..0e64f26a12 100644 --- a/localedata/locales/iso14651_t1_common +++ b/localedata/locales/iso14651_t1_common @@ -1042,9 +1042,9 @@ collating-element from "" collating-element from "" collating-element from "" collating-element from "" -collating-element from "" collating-element from "" collating-element from "" +collating-element from "" collating-element from "" collating-element from "" collating-element from "" @@ -1103,8 +1103,8 @@ collating-symbol collating-symbol collating-symbol collating-symbol -collating-symbol collating-symbol +collating-symbol collating-symbol collating-symbol collating-symbol @@ -1126,6 +1126,12 @@ collating-symbol collating-symbol collating-symbol collating-symbol +collating-symbol +collating-symbol +collating-symbol +collating-symbol +collating-symbol +collating-symbol # # # @@ -4552,6 +4558,12 @@ collating-symbol + + + + + + # # # @@ -7252,6 +7264,7 @@ order_start ;forward;forward;forward;forward,position ;;;IGNORE ;;;IGNORE "";;;IGNORE + "";;;IGNORE "";;;IGNORE "";;;IGNORE "";;;IGNORE @@ -7280,6 +7293,7 @@ order_start ;forward;forward;forward;forward,position "";;;IGNORE "";;;IGNORE "";;;IGNORE # ണ്‍ = ണ + ് + zwj + "";;;IGNORE "";;;IGNORE # ണ = ണ + ് + അ "";;;IGNORE "";;;IGNORE @@ -7290,6 +7304,7 @@ order_start ;forward;forward;forward;forward,position "";;;IGNORE "";;;IGNORE "";;;IGNORE # ന്‍= ന + ് + zwj + "";;;IGNORE "";;;IGNORE #ന = ന + ് + അ "";;;IGNORE "";;;IGNORE @@ -7305,20 +7320,23 @@ order_start ;forward;forward;forward;forward,position "";;;IGNORE "";;;IGNORE "";;;IGNORE # ര = ര + ് + zwj + "";;;IGNORE "";;;IGNORE # ര = ര + ് + അ ;;;IGNORE # ല്‍ = ല + ് + zwj + "";;;IGNORE "";;;IGNORE # ല = ല + ് + അ "";;;IGNORE "";;;IGNORE "";;;IGNORE - "";;;IGNORE + "";;;IGNORE "";;;IGNORE - "";;;IGNORE + "";;;IGNORE "";;;IGNORE "";;;IGNORE "";;;IGNORE "";;;IGNORE "";;;IGNORE # ള്‍ = ള + ് + zwj + "";;;IGNORE "";;;IGNORE # ള = ള + ് + അ "";;;IGNORE "";;;IGNORE diff --git a/malloc/arena.c b/malloc/arena.c index b91d7d6b16..d49e4a21c8 100644 --- a/malloc/arena.c +++ b/malloc/arena.c @@ -212,9 +212,9 @@ __malloc_fork_unlock_child (void) #if HAVE_TUNABLES static inline int do_set_mallopt_check (int32_t value); void -DL_TUNABLE_CALLBACK (set_mallopt_check) (void *valp) +DL_TUNABLE_CALLBACK (set_mallopt_check) (tunable_val_t *valp) { - int32_t value = *(int32_t *) valp; + int32_t value = (int32_t) valp->numval; do_set_mallopt_check (value); if (check_action != 0) __malloc_check_init (); @@ -223,9 +223,9 @@ DL_TUNABLE_CALLBACK (set_mallopt_check) (void *valp) # define DL_TUNABLE_CALLBACK_FNDECL(__name, __type) \ static inline int do_ ## __name (__type value); \ void \ -DL_TUNABLE_CALLBACK (__name) (void *valp) \ +DL_TUNABLE_CALLBACK (__name) (tunable_val_t *valp) \ { \ - __type value = *(__type *) valp; \ + __type value = (__type) (valp)->numval; \ do_ ## __name (value); \ } diff --git a/malloc/malloc.c b/malloc/malloc.c index 4885793905..4e076638b0 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -4902,7 +4902,7 @@ __libc_mallopt (int param_number, int value) case M_ARENA_MAX: if (value > 0) - do_set_arena_test (value); + do_set_arena_max (value); break; } __libc_lock_unlock (av->mutex); diff --git a/misc/regexp.c b/misc/regexp.c index 19d76c0c37..eaea7c3b89 100644 --- a/misc/regexp.c +++ b/misc/regexp.c @@ -29,14 +29,15 @@ #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_23) -/* Define the variables used for the interface. */ -char *loc1; -char *loc2; +/* Define the variables used for the interface. Avoid .symver on common + symbol, which just creates a new common symbol, not an alias. */ +char *loc1 __attribute__ ((nocommon)); +char *loc2 __attribute__ ((nocommon)); compat_symbol (libc, loc1, loc1, GLIBC_2_0); compat_symbol (libc, loc2, loc2, GLIBC_2_0); /* Although we do not support the use we define this variable as well. */ -char *locs; +char *locs __attribute__ ((nocommon)); compat_symbol (libc, locs, locs, GLIBC_2_0); diff --git a/nptl/Makefile b/nptl/Makefile index 6d48c0cfc8..8def69ae22 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -224,6 +224,7 @@ tests = tst-typesizes \ tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \ tst-mutex7 tst-mutex8 tst-mutex9 tst-mutex5a tst-mutex7a \ + tst-mutex7robust \ tst-mutexpi1 tst-mutexpi2 tst-mutexpi3 tst-mutexpi4 tst-mutexpi5 \ tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a tst-mutexpi8 \ tst-mutexpi9 \ @@ -241,7 +242,7 @@ tests = tst-typesizes \ tst-rwlock4 tst-rwlock5 tst-rwlock6 tst-rwlock7 tst-rwlock8 \ tst-rwlock9 tst-rwlock10 tst-rwlock11 tst-rwlock12 tst-rwlock13 \ tst-rwlock14 tst-rwlock15 tst-rwlock16 tst-rwlock17 tst-rwlock18 \ - tst-rwlock19 \ + tst-rwlock19 tst-rwlock20 \ tst-once1 tst-once2 tst-once3 tst-once4 tst-once5 \ tst-key1 tst-key2 tst-key3 tst-key4 \ tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 tst-sem6 tst-sem7 \ @@ -355,7 +356,7 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \ tst-oncex3 tst-oncex4 ifeq ($(build-shared),yes) tests += tst-atfork2 tst-tls3 tst-tls3-malloc tst-tls4 tst-tls5 tst-_res1 \ - tst-fini1 tst-stackguard1 + tst-fini1 tst-stackguard1 tst-compat-forwarder tests-nolibpthread += tst-fini1 ifeq ($(have-z-execstack),yes) tests += tst-execstack @@ -366,7 +367,7 @@ modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \ tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \ tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \ tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \ - tst-join7mod + tst-join7mod tst-compat-forwarder-mod extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \ tst-cleanup4aux.o tst-cleanupx4aux.o test-extras += $(modules-names) tst-cleanup4aux tst-cleanupx4aux @@ -704,6 +705,8 @@ $(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1 $(evaluate-test) endif +$(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so + # The tests here better do not run in parallel ifneq ($(filter %tests,$(MAKECMDGOALS)),) .NOTPARALLEL: diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c index 8a228ab254..368fe3c36b 100644 --- a/nptl/allocatestack.c +++ b/nptl/allocatestack.c @@ -683,8 +683,14 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp, prot) != 0) goto mprot_error; #elif _STACK_GROWS_UP - if (mprotect ((char *) pd - pd->guardsize, - pd->guardsize - guardsize, prot) != 0) + char *new_guard = (char *)(((uintptr_t) pd - guardsize) + & ~pagesize_m1); + char *old_guard = (char *)(((uintptr_t) pd - pd->guardsize) + & ~pagesize_m1); + /* The guard size difference might be > 0, but once rounded + to the nearest page the size difference might be zero. */ + if (new_guard > old_guard + && mprotect (old_guard, new_guard - old_guard, prot) != 0) goto mprot_error; #endif diff --git a/nptl/pt-longjmp.c b/nptl/pt-longjmp.c index 2ef757e687..8f3c6b3a09 100644 --- a/nptl/pt-longjmp.c +++ b/nptl/pt-longjmp.c @@ -25,21 +25,14 @@ symbol in libpthread, but the historical ABI requires it. For static linking, there is no need to provide anything here--the libc version will be linked in. For shared library ABI compatibility, there must be - longjmp and siglongjmp symbols in libpthread.so; so we define them using - IFUNC to redirect to the libc function. */ + longjmp and siglongjmp symbols in libpthread.so. -#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) - -# if HAVE_IFUNC - -# undef INIT_ARCH -# define INIT_ARCH() -# define DEFINE_LONGJMP(name) libc_ifunc (name, &__libc_longjmp) - -extern __typeof(longjmp) longjmp_ifunc; -extern __typeof(siglongjmp) siglongjmp_ifunc; + With an IFUNC resolver, it would be possible to avoid the indirection, + but the IFUNC resolver might run before the __libc_longjmp symbol has + been relocated, in which case the IFUNC resolver would not be able to + provide the correct address. */ -# else /* !HAVE_IFUNC */ +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) static void __attribute__ ((noreturn, used)) longjmp_compat (jmp_buf env, int val) @@ -47,14 +40,10 @@ longjmp_compat (jmp_buf env, int val) __libc_longjmp (env, val); } -# define DEFINE_LONGJMP(name) strong_alias (longjmp_compat, name) - -# endif /* HAVE_IFUNC */ - -DEFINE_LONGJMP (longjmp_ifunc) -compat_symbol (libpthread, longjmp_ifunc, longjmp, GLIBC_2_0); +strong_alias (longjmp_compat, longjmp_alias) +compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0); -strong_alias (longjmp_ifunc, siglongjmp_ifunc) -compat_symbol (libpthread, siglongjmp_ifunc, siglongjmp, GLIBC_2_0); +strong_alias (longjmp_alias, siglongjmp_alias) +compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0); #endif diff --git a/nptl/pt-system.c b/nptl/pt-system.c index f8ca6ba0d9..b30ddf2b39 100644 --- a/nptl/pt-system.c +++ b/nptl/pt-system.c @@ -25,29 +25,21 @@ libpthread, but the historical ABI requires it. For static linking, there is no need to provide anything here--the libc version will be linked in. For shared library ABI compatibility, there must be a - 'system' symbol in libpthread.so; so we define it using IFUNC to - redirect to the libc function. */ + 'system' symbol in libpthread.so. -#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) - -# if HAVE_IFUNC - -extern __typeof(system) system_ifunc; -# undef INIT_ARCH -# define INIT_ARCH() -libc_ifunc (system_ifunc, &__libc_system) + With an IFUNC resolver, it would be possible to avoid the indirection, + but the IFUNC resolver might run before the __libc_system symbol has + been relocated, in which case the IFUNC resolver would not be able to + provide the correct address. */ -# else /* !HAVE_IFUNC */ +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22) static int __attribute__ ((used)) system_compat (const char *line) { return __libc_system (line); } -strong_alias (system_compat, system_ifunc) - -# endif /* HAVE_IFUNC */ - -compat_symbol (libpthread, system_ifunc, system, GLIBC_2_0); +strong_alias (system_compat, system_alias) +compat_symbol (libpthread, system_alias, system, GLIBC_2_0); #endif diff --git a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c index dc9ca4c476..4425927c30 100644 --- a/nptl/pthread_mutex_lock.c +++ b/nptl/pthread_mutex_lock.c @@ -197,11 +197,14 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex) { /* Try to acquire the lock through a CAS from 0 (not acquired) to our TID | assume_other_futex_waiters. */ - if (__glibc_likely ((oldval == 0) - && (atomic_compare_and_exchange_bool_acq - (&mutex->__data.__lock, - id | assume_other_futex_waiters, 0) == 0))) - break; + if (__glibc_likely (oldval == 0)) + { + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + id | assume_other_futex_waiters, 0); + if (__glibc_likely (oldval == 0)) + break; + } if ((oldval & FUTEX_OWNER_DIED) != 0) { diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c index a4beb7b0dc..dd88cc4ec9 100644 --- a/nptl/pthread_mutex_timedlock.c +++ b/nptl/pthread_mutex_timedlock.c @@ -154,11 +154,14 @@ pthread_mutex_timedlock (pthread_mutex_t *mutex, { /* Try to acquire the lock through a CAS from 0 (not acquired) to our TID | assume_other_futex_waiters. */ - if (__glibc_likely ((oldval == 0) - && (atomic_compare_and_exchange_bool_acq - (&mutex->__data.__lock, - id | assume_other_futex_waiters, 0) == 0))) - break; + if (__glibc_likely (oldval == 0)) + { + oldval + = atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, + id | assume_other_futex_waiters, 0); + if (__glibc_likely (oldval == 0)) + break; + } if ((oldval & FUTEX_OWNER_DIED) != 0) { diff --git a/nptl/pthread_rwlock_common.c b/nptl/pthread_rwlock_common.c index 256508ca2a..846687e1cf 100644 --- a/nptl/pthread_rwlock_common.c +++ b/nptl/pthread_rwlock_common.c @@ -55,7 +55,6 @@ lock acquisition attempts, so that new incoming readers do not prolong a phase in which readers have acquired the lock. - The main components of the rwlock are a writer-only lock that allows only one of the concurrent writers to be the primary writer, and a single-writer-multiple-readers lock that decides between read phases, in @@ -70,15 +69,16 @@ --------------------------- #1 0 0 0 0 Lock is idle (and in a read phase). #2 0 0 >0 0 Readers have acquired the lock. - #3 0 1 0 0 Lock is not acquired; a writer is waiting for a write - phase to start or will try to start one. + #3 0 1 0 0 Lock is not acquired; a writer will try to start a + write phase. #4 0 1 >0 0 Readers have acquired the lock; a writer is waiting and explicit hand-over to the writer is required. #4a 0 1 >0 1 Same as #4 except that there are further readers waiting because the writer is to be preferred. #5 1 0 0 0 Lock is idle (and in a write phase). - #6 1 0 >0 0 Write phase; readers are waiting for a read phase to - start or will try to start one. + #6 1 0 >0 0 Write phase; readers will try to start a read phase + (requires explicit hand-over to all readers that + do not start the read phase). #7 1 1 0 0 Lock is acquired by a writer. #8 1 1 >0 0 Lock acquired by a writer and readers are waiting; explicit hand-over to the readers is required. @@ -375,9 +375,9 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock, complexity. */ if (__glibc_likely ((r & PTHREAD_RWLOCK_WRPHASE) == 0)) return 0; - - /* If there is no primary writer but we are in a write phase, we can try - to install a read phase ourself. */ + /* Otherwise, if we were in a write phase (states #6 or #8), we must wait + for explicit hand-over of the read phase; the only exception is if we + can start a read phase if there is no primary writer currently. */ while (((r & PTHREAD_RWLOCK_WRPHASE) != 0) && ((r & PTHREAD_RWLOCK_WRLOCKED) == 0)) { @@ -390,15 +390,18 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock, { /* 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 us because other readers cannot + distinguish between us and the writer; this includes + explicit hand-over and potentially having to wake other readers + (but we can pretend to do the setting and unsetting of WRLOCKED + atomically, and thus can skip this step). */ + 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; } else @@ -407,102 +410,98 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock, } } - if ((r & PTHREAD_RWLOCK_WRPHASE) != 0) + /* We were in a write phase but did not install the read phase. We cannot + distinguish between a writer and another reader starting the read phase, + so we must wait for explicit hand-over via __wrphase_futex. + However, __wrphase_futex might not have been set to 1 yet (either + because explicit hand-over to the writer is still ongoing, or because + the writer has started the write phase but has not yet updated + __wrphase_futex). The least recent value of __wrphase_futex we can + read from here is the modification of the last read phase (because + we synchronize with the last reader in this read phase through + __readers; see the use of acquire MO on the fetch_add above). + Therefore, if we observe a value of 0 for __wrphase_futex, we need + to subsequently check that __readers now indicates a read phase; we + need to use acquire MO for this so that if we observe a read phase, + we will also see the modification of __wrphase_futex by the previous + writer. We then need to load __wrphase_futex again and continue to + wait if it is not 0, so that we do not skip explicit hand-over. + Relaxed MO is sufficient for the load from __wrphase_futex because + we just use it as an indicator for when we can proceed; we use + __readers and the acquire MO accesses to it to eventually read from + the proper stores to __wrphase_futex. */ + unsigned int wpf; + bool ready = false; + for (;;) { - /* We are in a write phase, and there must be a primary writer because - of the previous loop. Block until the primary writer gives up the - write phase. This case requires explicit hand-over using - __wrphase_futex. - However, __wrphase_futex might not have been set to 1 yet (either - because explicit hand-over to the writer is still ongoing, or because - the writer has started the write phase but does not yet have updated - __wrphase_futex). The least recent value of __wrphase_futex we can - read from here is the modification of the last read phase (because - we synchronize with the last reader in this read phase through - __readers; see the use of acquire MO on the fetch_add above). - Therefore, if we observe a value of 0 for __wrphase_futex, we need - to subsequently check that __readers now indicates a read phase; we - need to use acquire MO for this so that if we observe a read phase, - we will also see the modification of __wrphase_futex by the previous - writer. We then need to load __wrphase_futex again and continue to - wait if it is not 0, so that we do not skip explicit hand-over. - Relaxed MO is sufficient for the load from __wrphase_futex because - we just use it as an indicator for when we can proceed; we use - __readers and the acquire MO accesses to it to eventually read from - the proper stores to __wrphase_futex. */ - unsigned int wpf; - bool ready = false; - for (;;) + while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) + | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) { - while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) - | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED)) + int private = __pthread_rwlock_get_private (rwlock); + if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) + && !atomic_compare_exchange_weak_relaxed + (&rwlock->__data.__wrphase_futex, + &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)) + continue; + int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, + 1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private); + if (err == ETIMEDOUT) { - int private = __pthread_rwlock_get_private (rwlock); - if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) - && !atomic_compare_exchange_weak_relaxed - (&rwlock->__data.__wrphase_futex, - &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)) - continue; - int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, - 1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private); - if (err == ETIMEDOUT) + /* If we timed out, we need to unregister. If no read phase + has been installed while we waited, we can just decrement + the number of readers. Otherwise, we just acquire the + lock, which is allowed because we give no precise timing + guarantees, and because the timeout is only required to + be in effect if we would have had to wait for other + threads (e.g., if futex_wait would time-out immediately + because the given absolute time is in the past). */ + r = atomic_load_relaxed (&rwlock->__data.__readers); + while ((r & PTHREAD_RWLOCK_WRPHASE) != 0) { - /* If we timed out, we need to unregister. If no read phase - has been installed while we waited, we can just decrement - the number of readers. Otherwise, we just acquire the - lock, which is allowed because we give no precise timing - guarantees, and because the timeout is only required to - be in effect if we would have had to wait for other - threads (e.g., if futex_wait would time-out immediately - because the given absolute time is in the past). */ - r = atomic_load_relaxed (&rwlock->__data.__readers); - while ((r & PTHREAD_RWLOCK_WRPHASE) != 0) - { - /* We don't need to make anything else visible to - others besides unregistering, so relaxed MO is - sufficient. */ - if (atomic_compare_exchange_weak_relaxed - (&rwlock->__data.__readers, &r, - r - (1 << PTHREAD_RWLOCK_READER_SHIFT))) - return ETIMEDOUT; - /* TODO Back-off. */ - } - /* Use the acquire MO fence to mirror the steps taken in the - non-timeout case. Note that the read can happen both - in the atomic_load above as well as in the failure case - of the CAS operation. */ - atomic_thread_fence_acquire (); - /* We still need to wait for explicit hand-over, but we must - not use futex_wait anymore because we would just time out - in this case and thus make the spin-waiting we need - unnecessarily expensive. */ - while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex) - | PTHREAD_RWLOCK_FUTEX_USED) - == (1 | PTHREAD_RWLOCK_FUTEX_USED)) - { - /* TODO Back-off? */ - } - ready = true; - break; + /* We don't need to make anything else visible to + others besides unregistering, so relaxed MO is + sufficient. */ + if (atomic_compare_exchange_weak_relaxed + (&rwlock->__data.__readers, &r, + r - (1 << PTHREAD_RWLOCK_READER_SHIFT))) + return ETIMEDOUT; + /* TODO Back-off. */ } - /* If we got interrupted (EINTR) or the futex word does not have the - expected value (EAGAIN), retry. */ + /* Use the acquire MO fence to mirror the steps taken in the + non-timeout case. Note that the read can happen both + in the atomic_load above as well as in the failure case + of the CAS operation. */ + atomic_thread_fence_acquire (); + /* We still need to wait for explicit hand-over, but we must + not use futex_wait anymore because we would just time out + in this case and thus make the spin-waiting we need + unnecessarily expensive. */ + while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex) + | PTHREAD_RWLOCK_FUTEX_USED) + == (1 | PTHREAD_RWLOCK_FUTEX_USED)) + { + /* TODO Back-off? */ + } + ready = true; + break; } - if (ready) - /* See below. */ - break; - /* We need acquire MO here so that we synchronize with the lock - release of the writer, and so that we observe a recent value of - __wrphase_futex (see below). */ - if ((atomic_load_acquire (&rwlock->__data.__readers) - & PTHREAD_RWLOCK_WRPHASE) == 0) - /* We are in a read phase now, so the least recent modification of - __wrphase_futex we can read from is the store by the writer - with value 1. Thus, only now we can assume that if we observe - a value of 0, explicit hand-over is finished. Retry the loop - above one more time. */ - ready = true; + /* If we got interrupted (EINTR) or the futex word does not have the + expected value (EAGAIN), retry. */ } + if (ready) + /* See below. */ + break; + /* We need acquire MO here so that we synchronize with the lock + release of the writer, and so that we observe a recent value of + __wrphase_futex (see below). */ + if ((atomic_load_acquire (&rwlock->__data.__readers) + & PTHREAD_RWLOCK_WRPHASE) == 0) + /* We are in a read phase now, so the least recent modification of + __wrphase_futex we can read from is the store by the writer + with value 1. Thus, only now we can assume that if we observe + a value of 0, explicit hand-over is finished. Retry the loop + above one more time. */ + ready = true; } return 0; @@ -741,10 +740,23 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock, r = atomic_load_relaxed (&rwlock->__data.__readers); } /* Our snapshot of __readers is up-to-date at this point because we - either set WRLOCKED using a CAS or were handed over WRLOCKED from + either set WRLOCKED using a CAS (and update r accordingly below, + which was used as expected value for the CAS) or got WRLOCKED from another writer whose snapshot of __readers we inherit. */ + r |= PTHREAD_RWLOCK_WRLOCKED; } + /* We are the primary writer; enable blocking on __writers_futex. Relaxed + MO is sufficient for futex words; acquire MO on the previous + modifications of __readers ensures that this store happens after the + store of value 0 by the previous primary writer. */ + atomic_store_relaxed (&rwlock->__data.__writers_futex, + 1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0)); + + /* If we are in a write phase, we have acquired the lock. */ + if ((r & PTHREAD_RWLOCK_WRPHASE) != 0) + goto done; + /* If we are in a read phase and there are no readers, try to start a write phase. */ while (((r & PTHREAD_RWLOCK_WRPHASE) == 0) @@ -758,166 +770,156 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock, &r, r | PTHREAD_RWLOCK_WRPHASE)) { /* We have started a write phase, so need to enable readers to wait. - See the similar case in__pthread_rwlock_rdlock_full. */ + See the similar case in __pthread_rwlock_rdlock_full. Unlike in + that similar case, we are the (only) primary writer and so do + not need to wake another writer. */ atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1); - /* Make sure we fall through to the end of the function. */ - r |= PTHREAD_RWLOCK_WRPHASE; - break; + + goto done; } /* TODO Back-off. */ } - /* We are the primary writer; enable blocking on __writers_futex. Relaxed - MO is sufficient for futex words; acquire MO on the previous - modifications of __readers ensures that this store happens after the - store of value 0 by the previous primary writer. */ - atomic_store_relaxed (&rwlock->__data.__writers_futex, - 1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0)); - - if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRPHASE) == 0)) + /* We became the primary writer in a read phase and there were readers when + we did (because of the previous loop). Thus, we have to wait for + explicit hand-over from one of these readers. + We basically do the same steps as for the similar case in + __pthread_rwlock_rdlock_full, except that we additionally might try + to directly hand over to another writer and need to wake up + other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */ + unsigned int wpf; + bool ready = false; + for (;;) { - /* We are not in a read phase and there are readers (because of the - previous loop). Thus, we have to wait for explicit hand-over from - one of these readers. - We basically do the same steps as for the similar case in - __pthread_rwlock_rdlock_full, except that we additionally might try - to directly hand over to another writer and need to wake up - other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */ - unsigned int wpf; - bool ready = false; - for (;;) + while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) + | PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED) { - while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex)) - | PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED) + int private = __pthread_rwlock_get_private (rwlock); + if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) + && !atomic_compare_exchange_weak_relaxed + (&rwlock->__data.__wrphase_futex, &wpf, + PTHREAD_RWLOCK_FUTEX_USED)) + continue; + int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, + PTHREAD_RWLOCK_FUTEX_USED, abstime, private); + if (err == ETIMEDOUT) { - int private = __pthread_rwlock_get_private (rwlock); - if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0) - && !atomic_compare_exchange_weak_relaxed - (&rwlock->__data.__wrphase_futex, &wpf, - PTHREAD_RWLOCK_FUTEX_USED)) - continue; - int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex, - PTHREAD_RWLOCK_FUTEX_USED, abstime, private); - if (err == ETIMEDOUT) + if (rwlock->__data.__flags + != PTHREAD_RWLOCK_PREFER_READER_NP) { - if (rwlock->__data.__flags - != PTHREAD_RWLOCK_PREFER_READER_NP) - { - /* We try writer--writer hand-over. */ - unsigned int w = atomic_load_relaxed - (&rwlock->__data.__writers); - if (w != 0) - { - /* We are about to hand over WRLOCKED, so we must - release __writers_futex too; otherwise, we'd have - a pending store, which could at least prevent - other threads from waiting using the futex - because it could interleave with the stores - by subsequent writers. In turn, this means that - we have to clean up when we do not hand over - WRLOCKED. - Release MO so that another writer that gets - WRLOCKED from us can take over our view of - __readers. */ - unsigned int wf = atomic_exchange_relaxed - (&rwlock->__data.__writers_futex, 0); - while (w != 0) - { - if (atomic_compare_exchange_weak_release - (&rwlock->__data.__writers, &w, - w | PTHREAD_RWLOCK_WRHANDOVER)) - { - /* Wake other writers. */ - if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0) - futex_wake - (&rwlock->__data.__writers_futex, 1, - private); - return ETIMEDOUT; - } - /* TODO Back-off. */ - } - /* We still own WRLOCKED and someone else might set - a write phase concurrently, so enable waiting - again. Make sure we don't loose the flag that - signals whether there are threads waiting on - this futex. */ - atomic_store_relaxed - (&rwlock->__data.__writers_futex, wf); - } - } - /* If we timed out and we are not in a write phase, we can - just stop being a primary writer. Otherwise, we just - acquire the lock. */ - r = atomic_load_relaxed (&rwlock->__data.__readers); - if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) + /* We try writer--writer hand-over. */ + unsigned int w = atomic_load_relaxed + (&rwlock->__data.__writers); + if (w != 0) { - /* We are about to release WRLOCKED, so we must release - __writers_futex too; see the handling of - writer--writer hand-over above. */ + /* We are about to hand over WRLOCKED, so we must + release __writers_futex too; otherwise, we'd have + a pending store, which could at least prevent + other threads from waiting using the futex + because it could interleave with the stores + by subsequent writers. In turn, this means that + we have to clean up when we do not hand over + WRLOCKED. + Release MO so that another writer that gets + WRLOCKED from us can take over our view of + __readers. */ unsigned int wf = atomic_exchange_relaxed (&rwlock->__data.__writers_futex, 0); - while ((r & PTHREAD_RWLOCK_WRPHASE) == 0) + while (w != 0) { - /* While we don't need to make anything from a - caller's critical section visible to other - threads, we need to ensure that our changes to - __writers_futex are properly ordered. - Therefore, use release MO to synchronize with - subsequent primary writers. Also wake up any - waiting readers as they are waiting because of - us. */ if (atomic_compare_exchange_weak_release - (&rwlock->__data.__readers, &r, - (r ^ PTHREAD_RWLOCK_WRLOCKED) - & ~(unsigned int) PTHREAD_RWLOCK_RWAITING)) + (&rwlock->__data.__writers, &w, + w | PTHREAD_RWLOCK_WRHANDOVER)) { /* Wake other writers. */ if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0) futex_wake (&rwlock->__data.__writers_futex, - 1, private); - /* Wake waiting readers. */ - if ((r & PTHREAD_RWLOCK_RWAITING) != 0) - futex_wake (&rwlock->__data.__readers, - INT_MAX, private); + 1, private); return ETIMEDOUT; } + /* TODO Back-off. */ } - /* We still own WRLOCKED and someone else might set a - write phase concurrently, so enable waiting again. - Make sure we don't loose the flag that signals - whether there are threads waiting on this futex. */ - atomic_store_relaxed (&rwlock->__data.__writers_futex, - wf); + /* We still own WRLOCKED and someone else might set + a write phase concurrently, so enable waiting + again. Make sure we don't loose the flag that + signals whether there are threads waiting on + this futex. */ + atomic_store_relaxed + (&rwlock->__data.__writers_futex, wf); } - /* Use the acquire MO fence to mirror the steps taken in the - non-timeout case. Note that the read can happen both - in the atomic_load above as well as in the failure case - of the CAS operation. */ - atomic_thread_fence_acquire (); - /* We still need to wait for explicit hand-over, but we must - not use futex_wait anymore. */ - while ((atomic_load_relaxed - (&rwlock->__data.__wrphase_futex) - | PTHREAD_RWLOCK_FUTEX_USED) - == PTHREAD_RWLOCK_FUTEX_USED) + } + /* If we timed out and we are not in a write phase, we can + just stop being a primary writer. Otherwise, we just + acquire the lock. */ + r = atomic_load_relaxed (&rwlock->__data.__readers); + if ((r & PTHREAD_RWLOCK_WRPHASE) == 0) + { + /* We are about to release WRLOCKED, so we must release + __writers_futex too; see the handling of + writer--writer hand-over above. */ + unsigned int wf = atomic_exchange_relaxed + (&rwlock->__data.__writers_futex, 0); + while ((r & PTHREAD_RWLOCK_WRPHASE) == 0) { - /* TODO Back-off. */ + /* While we don't need to make anything from a + caller's critical section visible to other + threads, we need to ensure that our changes to + __writers_futex are properly ordered. + Therefore, use release MO to synchronize with + subsequent primary writers. Also wake up any + waiting readers as they are waiting because of + us. */ + if (atomic_compare_exchange_weak_release + (&rwlock->__data.__readers, &r, + (r ^ PTHREAD_RWLOCK_WRLOCKED) + & ~(unsigned int) PTHREAD_RWLOCK_RWAITING)) + { + /* Wake other writers. */ + if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0) + futex_wake (&rwlock->__data.__writers_futex, + 1, private); + /* Wake waiting readers. */ + if ((r & PTHREAD_RWLOCK_RWAITING) != 0) + futex_wake (&rwlock->__data.__readers, + INT_MAX, private); + return ETIMEDOUT; + } } - ready = true; - break; + /* We still own WRLOCKED and someone else might set a + write phase concurrently, so enable waiting again. + Make sure we don't loose the flag that signals + whether there are threads waiting on this futex. */ + atomic_store_relaxed (&rwlock->__data.__writers_futex, wf); } - /* If we got interrupted (EINTR) or the futex word does not have - the expected value (EAGAIN), retry. */ + /* Use the acquire MO fence to mirror the steps taken in the + non-timeout case. Note that the read can happen both + in the atomic_load above as well as in the failure case + of the CAS operation. */ + atomic_thread_fence_acquire (); + /* We still need to wait for explicit hand-over, but we must + not use futex_wait anymore. */ + while ((atomic_load_relaxed + (&rwlock->__data.__wrphase_futex) + | PTHREAD_RWLOCK_FUTEX_USED) + == PTHREAD_RWLOCK_FUTEX_USED) + { + /* TODO Back-off. */ + } + ready = true; + break; } - /* See pthread_rwlock_rdlock_full. */ - if (ready) - break; - if ((atomic_load_acquire (&rwlock->__data.__readers) - & PTHREAD_RWLOCK_WRPHASE) != 0) - ready = true; + /* If we got interrupted (EINTR) or the futex word does not have + the expected value (EAGAIN), retry. */ } + /* See pthread_rwlock_rdlock_full. */ + if (ready) + break; + if ((atomic_load_acquire (&rwlock->__data.__readers) + & PTHREAD_RWLOCK_WRPHASE) != 0) + ready = true; } + done: atomic_store_relaxed (&rwlock->__data.__cur_writer, THREAD_GETMEM (THREAD_SELF, tid)); return 0; diff --git a/nptl/tst-compat-forwarder-mod.c b/nptl/tst-compat-forwarder-mod.c new file mode 100644 index 0000000000..823bfa22de --- /dev/null +++ b/nptl/tst-compat-forwarder-mod.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2017 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 + . */ + +/* Call the function system through a statically initialized pointer. */ + +#include + +int (*system_function) (const char *) = system; + +void +call_system (void) +{ + system_function (NULL); +} diff --git a/nptl/tst-compat-forwarder.c b/nptl/tst-compat-forwarder.c new file mode 100644 index 0000000000..f96806b7fe --- /dev/null +++ b/nptl/tst-compat-forwarder.c @@ -0,0 +1,35 @@ +/* Copyright (C) 2017 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 + . */ + +/* Test that the compat forwaders in libpthread work correctly. */ + +#include + +extern void call_system (void); + +int +do_test (void) +{ + /* Calling the system function from a shared library that is not linked + against libpthread, when the main program is linked against + libpthread, should not crash. */ + call_system (); + + return 0; +} + +#include diff --git a/nptl/tst-mutex7.c b/nptl/tst-mutex7.c index a11afdba5e..08fe251eeb 100644 --- a/nptl/tst-mutex7.c +++ b/nptl/tst-mutex7.c @@ -22,25 +22,41 @@ #include #include - +/* This test is a template for other tests to use. Other tests define + the following macros to change the behaviour of the template test. + The test is very simple, it configures N threads given the parameters + below and then proceeds to go through mutex lock and unlock + operations in each thread as described before for the thread + function. */ #ifndef TYPE # define TYPE PTHREAD_MUTEX_DEFAULT #endif - +#ifndef ROBUST +# define ROBUST PTHREAD_MUTEX_STALLED +#endif +#ifndef DELAY_NSEC +# define DELAY_NSEC 11000 +#endif +#ifndef ROUNDS +# define ROUNDS 1000 +#endif +#ifndef N +# define N 100 +#endif static pthread_mutex_t lock; - -#define ROUNDS 1000 -#define N 100 - - +/* Each thread locks and the subsequently unlocks the lock, yielding + the smallest critical section possible. After the unlock the thread + waits DELAY_NSEC nanoseconds before doing the lock and unlock again. + Every thread does this ROUNDS times. The lock and unlock are + checked for errors. */ static void * tf (void *arg) { int nr = (long int) arg; int cnt; - struct timespec ts = { .tv_sec = 0, .tv_nsec = 11000 }; + struct timespec ts = { .tv_sec = 0, .tv_nsec = DELAY_NSEC }; for (cnt = 0; cnt < ROUNDS; ++cnt) { @@ -56,13 +72,16 @@ tf (void *arg) return (void *) 1l; } - nanosleep (&ts, NULL); + if ((ts.tv_sec > 0) || (ts.tv_nsec > 0)) + nanosleep (&ts, NULL); } return NULL; } - +/* Setup and run N threads, where each thread does as described + in the above thread function. The threads are given a minimal 1MiB + stack since they don't do anything between the lock and unlock. */ static int do_test (void) { @@ -80,6 +99,12 @@ do_test (void) exit (1); } + if (pthread_mutexattr_setrobust (&a, ROBUST) != 0) + { + puts ("mutexattr_setrobust failed"); + exit (1); + } + #ifdef ENABLE_PI if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT) != 0) { diff --git a/nptl/tst-mutex7robust.c b/nptl/tst-mutex7robust.c new file mode 100644 index 0000000000..8221a61d29 --- /dev/null +++ b/nptl/tst-mutex7robust.c @@ -0,0 +1,7 @@ +/* Bug 21778: Fix oversight in robust mutex lock acquisition. */ +#define TYPE PTHREAD_MUTEX_NORMAL +#define ROBUST PTHREAD_MUTEX_ROBUST +#define DELAY_NSEC 0 +#define ROUNDS 1000 +#define N 32 +#include "tst-mutex7.c" diff --git a/nptl/tst-rwlock20.c b/nptl/tst-rwlock20.c new file mode 100644 index 0000000000..4aeea2b8f5 --- /dev/null +++ b/nptl/tst-rwlock20.c @@ -0,0 +1,116 @@ +/* Test program for a read-phase / write-phase explicit hand-over. + Copyright (C) 2017 Free Software Foundation, Inc. + + 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; see the file COPYING.LIB. If + not, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We realy want to set threads to 2 to reproduce this issue. The goal + is to have one primary writer and a single reader, and to hit the + bug that happens in the interleaving of those two phase transitions. + However, on most hardware, adding a second writer seems to help the + interleaving happen slightly more often, say 20% of the time. On a + 16 core ppc64 machine this fails 100% of the time with an unpatched + glibc. On a 8 core x86_64 machine this fails ~93% of the time, but + it doesn't fail at all on a 4 core system, so having available + unloaded cores makes a big difference in reproducibility. On an 8 + core qemu/kvm guest the reproducer reliability drops to ~10%. */ +#define THREADS 3 + +#define KIND PTHREAD_RWLOCK_PREFER_READER_NP + +static pthread_rwlock_t lock; +static int done = 0; + +static void* +tf (void* arg) +{ + while (atomic_load_relaxed (&done) == 0) + { + int rcnt = 0; + int wcnt = 100; + if ((uintptr_t) arg == 0) + { + rcnt = 1; + wcnt = 1; + } + + do + { + if (wcnt) + { + xpthread_rwlock_wrlock (&lock); + xpthread_rwlock_unlock (&lock); + wcnt--; + } + if (rcnt) + { + xpthread_rwlock_rdlock (&lock); + xpthread_rwlock_unlock (&lock); + rcnt--; + } + } + while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0)); + + } + return NULL; +} + + + +static int +do_test (void) +{ + pthread_t thr[THREADS]; + int n; + pthread_rwlockattr_t attr; + + xpthread_rwlockattr_init (&attr); + xpthread_rwlockattr_setkind_np (&attr, KIND); + + xpthread_rwlock_init (&lock, &attr); + + /* Make standard error the same as standard output. */ + dup2 (1, 2); + + /* Make sure we see all message, even those on stdout. */ + setvbuf (stdout, NULL, _IONBF, 0); + + for (n = 0; n < THREADS; ++n) + thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n); + + struct timespec delay; + delay.tv_sec = 10; + delay.tv_nsec = 0; + nanosleep (&delay, NULL); + atomic_store_relaxed (&done, 1); + + /* Wait for all the threads. */ + for (n = 0; n < THREADS; ++n) + xpthread_join (thr[n]); + + return 0; +} + +#include diff --git a/posix/Makefile b/posix/Makefile index 8f23d647c8..ccbfcf3c48 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -43,7 +43,7 @@ routines := \ getpgid setpgid getpgrp bsd-getpgrp setpgrp getsid setsid \ getresuid getresgid setresuid setresgid \ pathconf sysconf fpathconf \ - glob glob64 fnmatch regex \ + glob glob64 globfree globfree64 glob_pattern_p fnmatch regex \ confstr \ getopt getopt1 getopt_init \ sched_setp sched_getp sched_sets sched_gets sched_yield sched_primax \ @@ -91,7 +91,8 @@ tests := tstgetopt testfnm runtests runptests \ tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \ tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \ tst-posix_spawn-fd \ - tst-posix_fadvise tst-posix_fadvise64 + tst-posix_fadvise tst-posix_fadvise64 \ + tst-glob-tilde xtests := bug-ga2 ifeq (yes,$(build-shared)) test-srcs := globtest @@ -134,7 +135,8 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \ $(objpfx)tst-rxspencer-no-utf8-mem.out $(objpfx)tst-pcre-mem.out \ $(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \ $(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \ - $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out + $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \ + $(objpfx)tst-glob-tilde-mem.out xtests-special += $(objpfx)bug-ga2-mem.out endif @@ -341,6 +343,12 @@ $(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out $(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \ $(evaluate-test) +tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace + +$(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out + $(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \ + $(evaluate-test) + $(inst_libexecdir)/getconf: $(inst_bindir)/getconf \ $(objpfx)getconf.speclist FORCE $(addprefix $(..)./scripts/mkinstalldirs ,\ diff --git a/posix/flexmember.h b/posix/flexmember.h new file mode 100644 index 0000000000..107c1f09e9 --- /dev/null +++ b/posix/flexmember.h @@ -0,0 +1,45 @@ +/* Sizes of structs with flexible array members. + + Copyright 2016-2017 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 + . + + Written by Paul Eggert. */ + +#include + +/* Nonzero multiple of alignment of TYPE, suitable for FLEXSIZEOF below. + On older platforms without _Alignof, use a pessimistic bound that is + safe in practice even if FLEXIBLE_ARRAY_MEMBER is 1. + On newer platforms, use _Alignof to get a tighter bound. */ + +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 +# define FLEXALIGNOF(type) (sizeof (type) & ~ (sizeof (type) - 1)) +#else +# define FLEXALIGNOF(type) _Alignof (type) +#endif + +/* Upper bound on the size of a struct of type TYPE with a flexible + array member named MEMBER that is followed by N bytes of other data. + This is not simply sizeof (TYPE) + N, since it may require + alignment on unusually picky C11 platforms, and + FLEXIBLE_ARRAY_MEMBER may be 1 on pre-C11 platforms. + Yield a value less than N if and only if arithmetic overflow occurs. */ + +#define FLEXSIZEOF(type, member, n) \ + ((offsetof (type, member) + FLEXALIGNOF (type) - 1 + (n)) \ + & ~ (FLEXALIGNOF (type) - 1)) diff --git a/posix/glob.c b/posix/glob.c index c653809118..b2273ea7bc 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -15,7 +15,7 @@ License along with the GNU C Library; if not, see . */ -#ifdef HAVE_CONFIG_H +#ifndef _LIBC # include #endif @@ -27,29 +27,15 @@ #include #include #include - -/* Outcomment the following line for production quality code. */ -/* #define NDEBUG 1 */ #include +#include -#include /* Needed on stupid SunOS for assert. */ - -#if !defined _LIBC || !defined GLOB_ONLY_P -#if defined HAVE_UNISTD_H || defined _LIBC -# include -# ifndef POSIX -# ifdef _POSIX_VERSION -# define POSIX -# endif -# endif +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WINDOWS32 #endif -#include - -#if defined HAVE_STDINT_H || defined _LIBC -# include -#elif !defined UINTPTR_MAX -# define UINTPTR_MAX (~((size_t) 0)) +#ifndef WINDOWS32 +# include #endif #include @@ -57,24 +43,7 @@ # define __set_errno(val) errno = (val) #endif -#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ -# include -#else -# define dirent direct -# ifdef HAVE_SYS_NDIR_H -# include -# endif -# ifdef HAVE_SYS_DIR_H -# include -# endif -# ifdef HAVE_NDIR_H -# include -# endif -# ifdef HAVE_VMSDIR_H -# include "vmsdir.h" -# endif /* HAVE_VMSDIR_H */ -#endif - +#include #include #include #include @@ -87,27 +56,29 @@ # define opendir(name) __opendir (name) # define readdir(str) __readdir64 (str) # define getpwnam_r(name, bufp, buf, len, res) \ - __getpwnam_r (name, bufp, buf, len, res) + __getpwnam_r (name, bufp, buf, len, res) # ifndef __stat64 # define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf) # endif # define struct_stat64 struct stat64 +# define FLEXIBLE_ARRAY_MEMBER #else /* !_LIBC */ -# include "getlogin_r.h" -# include "mempcpy.h" -# include "stat-macros.h" -# include "strdup.h" -# define __stat64(fname, buf) stat (fname, buf) -# define struct_stat64 struct stat -# define __stat(fname, buf) stat (fname, buf) -# define __alloca alloca -# define __readdir readdir -# define __readdir64 readdir64 -# define __glob_pattern_p glob_pattern_p +# define __getlogin_r(buf, len) getlogin_r (buf, len) +# define __stat64(fname, buf) stat (fname, buf) +# define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag) +# define struct_stat64 struct stat +# ifndef __MVS__ +# define __alloca alloca +# endif +# define __readdir readdir +# define COMPILE_GLOB64 #endif /* _LIBC */ #include +#include +#include + #ifdef _SC_GETPW_R_SIZE_MAX # define GETPW_R_SIZE_MAX() sysconf (_SC_GETPW_R_SIZE_MAX) #else @@ -121,61 +92,59 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; +typedef uint_fast8_t dirent_type; + +#if !defined _LIBC && !defined HAVE_STRUCT_DIRENT_D_TYPE +/* Any distinct values will do here. + Undef any existing macros out of the way. */ +# undef DT_UNKNOWN +# undef DT_DIR +# undef DT_LNK +# define DT_UNKNOWN 0 +# define DT_DIR 1 +# define DT_LNK 2 +#endif + /* A representation of a directory entry which does not depend on the layout of struct dirent, or the size of ino_t. */ struct readdir_result { const char *name; -# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE - uint8_t type; -# endif +#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE + dirent_type type; +#endif +#if defined _LIBC || defined D_INO_IN_DIRENT bool skip_entry; +#endif }; -# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE -/* Initializer based on the d_type member of struct dirent. */ -# define D_TYPE_TO_RESULT(source) (source)->d_type, - -/* True if the directory entry D might be a symbolic link. */ -static bool -readdir_result_might_be_symlink (struct readdir_result d) -{ - return d.type == DT_UNKNOWN || d.type == DT_LNK; -} - -/* True if the directory entry D might be a directory. */ -static bool -readdir_result_might_be_dir (struct readdir_result d) -{ - return d.type == DT_DIR || readdir_result_might_be_symlink (d); -} -# else /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ -# define D_TYPE_TO_RESULT(source) - -/* If we do not have type information, symbolic links and directories - are always a possibility. */ - -static bool -readdir_result_might_be_symlink (struct readdir_result d) +/* Initialize and return type member of struct readdir_result. */ +static dirent_type +readdir_result_type (struct readdir_result d) { - return true; +#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE +# define D_TYPE_TO_RESULT(source) (source)->d_type, + return d.type; +#else +# define D_TYPE_TO_RESULT(source) + return DT_UNKNOWN; +#endif } +/* Initialize and return skip_entry member of struct readdir_result. */ static bool -readdir_result_might_be_dir (struct readdir_result d) +readdir_result_skip_entry (struct readdir_result d) { - return true; -} - -# endif /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ - -# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ /* Initializer for skip_entry. POSIX does not require that the d_ino field be present, and some systems do not provide it. */ -# define D_INO_TO_RESULT(source) false, -# else -# define D_INO_TO_RESULT(source) (source)->d_ino == 0, -# endif +#if defined _LIBC || defined D_INO_IN_DIRENT +# define D_INO_TO_RESULT(source) (source)->d_ino == 0, + return d.skip_entry; +#else +# define D_INO_TO_RESULT(source) + return false; +#endif +} /* Construct an initializer for a struct readdir_result object from a struct dirent *. No copy of the name is made. */ @@ -186,8 +155,6 @@ readdir_result_might_be_dir (struct readdir_result d) D_INO_TO_RESULT (source) \ } -#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ - /* Call gl_readdir on STREAM. This macro can be overridden to reduce type safety if an old interface version needs to be supported. */ #ifndef GL_READDIR @@ -225,18 +192,55 @@ convert_dirent64 (const struct dirent64 *source) } #endif +#ifndef _LIBC +/* The results of opendir() in this file are not used with dirfd and fchdir, + and we do not leak fds to any single-threaded code that could use stdio, + therefore save some unnecessary recursion in fchdir.c and opendir_safer.c. + FIXME - if the kernel ever adds support for multi-thread safety for + avoiding standard fds, then we should use opendir_safer. */ +# ifdef GNULIB_defined_opendir +# undef opendir +# endif +# ifdef GNULIB_defined_closedir +# undef closedir +# endif -#ifndef attribute_hidden -# define attribute_hidden +/* Just use malloc. */ +# define __libc_use_alloca(n) false +# define alloca_account(len, avar) ((void) (len), (void) (avar), (void *) 0) +# define extend_alloca_account(buf, len, newlen, avar) \ + ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0) #endif +/* Set *R = A + B. Return true if the answer is mathematically + incorrect due to overflow; in this case, *R is the low order + bits of the correct answer. */ + +static bool +size_add_wrapv (size_t a, size_t b, size_t *r) +{ +#if 5 <= __GNUC__ && !defined __ICC + return __builtin_add_overflow (a, b, r); +#else + *r = a + b; + return *r < a; +#endif +} + +static bool +glob_use_alloca (size_t alloca_used, size_t len) +{ + size_t size; + return (!size_add_wrapv (alloca_used, len, &size) + && __libc_use_alloca (size)); +} + static int glob_in_dir (const char *pattern, const char *directory, int flags, int (*errfunc) (const char *, int), glob_t *pglob, size_t alloca_used); extern int __glob_pattern_type (const char *pattern, int quote) attribute_hidden; -#if !defined _LIBC || !defined GLOB_ONLY_P static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL; static int collated_compare (const void *, const void *) __THROWNL; @@ -265,16 +269,15 @@ next_brace_sub (const char *cp, int flags) return *cp != '\0' ? cp : NULL; } -#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ /* Do glob searching for PATTERN, placing results in PGLOB. The bits defined above may be set in FLAGS. If a directory cannot be opened or read and ERRFUNC is not nil, it is called with the pathname that caused the error, and the - `errno' value from the failing call; if it returns non-zero - `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored. + 'errno' value from the failing call; if it returns non-zero + 'glob' returns GLOB_ABORTED; if it returns zero, the error is ignored. If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned. - Otherwise, `glob' returns zero. */ + Otherwise, 'glob' returns zero. */ int #ifdef GLOB_ATTRIBUTE GLOB_ATTRIBUTE @@ -292,9 +295,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), int malloc_dirname = 0; glob_t dirs; int retval = 0; -#ifdef _LIBC size_t alloca_used = 0; -#endif if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) { @@ -308,7 +309,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), flags |= GLOB_ONLYDIR; if (!(flags & GLOB_DOOFFS)) - /* Have to do this so `globfree' knows where to start freeing. It + /* Have to do this so 'globfree' knows where to start freeing. It also makes all the code that uses gl_offs simpler. */ pglob->gl_offs = 0; @@ -372,14 +373,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), size_t rest_len; char *onealt; size_t pattern_len = strlen (pattern) - 1; -#ifdef _LIBC - int alloca_onealt = __libc_use_alloca (alloca_used + pattern_len); + int alloca_onealt = glob_use_alloca (alloca_used, pattern_len); if (alloca_onealt) onealt = alloca_account (pattern_len, alloca_used); else -#endif { - onealt = (char *) malloc (pattern_len); + onealt = malloc (pattern_len); if (onealt == NULL) return GLOB_NOSPACE; } @@ -392,11 +391,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), next = next_brace_sub (begin + 1, flags); if (next == NULL) { - /* It is an illegal expression. */ + /* It is an invalid expression. */ illegal_brace: -#ifdef _LIBC if (__glibc_unlikely (!alloca_onealt)) -#endif free (onealt); flags &= ~GLOB_BRACE; goto no_brace; @@ -437,9 +434,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), /* If we got an error, return it. */ if (result && result != GLOB_NOMATCH) { -#ifdef _LIBC if (__glibc_unlikely (!alloca_onealt)) -#endif free (onealt); if (!(flags & GLOB_APPEND)) { @@ -458,9 +453,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), assert (next != NULL); } -#ifdef _LIBC if (__glibc_unlikely (!alloca_onealt)) -#endif free (onealt); if (pglob->gl_pathc != firstc) @@ -476,14 +469,16 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), /* Find the filename. */ filename = strrchr (pattern, '/'); + #if defined __MSDOS__ || defined WINDOWS32 - /* The case of "d:pattern". Since `:' is not allowed in + /* The case of "d:pattern". Since ':' is not allowed in file names, we can safely assume that wherever it happens in pattern, it signals the filename part. This is so we could some day support patterns like "[a-z]:foo". */ if (filename == NULL) filename = strchr (pattern, ':'); #endif /* __MSDOS__ || WINDOWS32 */ + dirname_modified = 0; if (filename == NULL) { @@ -508,11 +503,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } filename = pattern; -#ifdef _AMIGA - dirname = (char *) ""; -#else dirname = (char *) "."; -#endif dirlen = 0; } } @@ -536,22 +527,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), char *drive_spec; ++dirlen; - drive_spec = (char *) __alloca (dirlen + 1); + drive_spec = __alloca (dirlen + 1); *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0'; /* For now, disallow wildcards in the drive spec, to prevent infinite recursion in glob. */ if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE))) return GLOB_NOMATCH; - /* If this is "d:pattern", we need to copy `:' to DIRNAME + /* If this is "d:pattern", we need to copy ':' to DIRNAME as well. If it's "d:/pattern", don't remove the slash from "d:/", since "d:" and "d:/" are not the same.*/ } #endif -#ifdef _LIBC - if (__libc_use_alloca (alloca_used + dirlen + 1)) + + if (glob_use_alloca (alloca_used, dirlen + 1)) newp = alloca_account (dirlen + 1, alloca_used); else -#endif { newp = malloc (dirlen + 1); if (newp == NULL) @@ -562,14 +552,17 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirname = newp; ++filename; - if (filename[0] == '\0' #if defined __MSDOS__ || defined WINDOWS32 - && dirname[dirlen - 1] != ':' - && (dirlen < 3 || dirname[dirlen - 2] != ':' - || dirname[dirlen - 1] != '/') + bool drive_root = (dirlen > 1 + && (dirname[dirlen - 1] == ':' + || (dirlen > 2 && dirname[dirlen - 2] == ':' + && dirname[dirlen - 1] == '/'))); +#else + bool drive_root = false; #endif - && dirlen > 1) - /* "pattern/". Expand "pattern", appending slashes. */ + + if (filename[0] == '\0' && dirlen > 1 && !drive_root) + /* "pattern/". Expand "pattern", appending slashes. */ { int orig_flags = flags; if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') @@ -602,7 +595,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } } -#ifndef VMS if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') { if (dirname[1] == '\0' || dirname[1] == '/' @@ -612,100 +604,127 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), /* Look up home directory. */ char *home_dir = getenv ("HOME"); int malloc_home_dir = 0; -# ifdef _AMIGA - if (home_dir == NULL || home_dir[0] == '\0') - home_dir = "SYS:"; -# else -# ifdef WINDOWS32 - if (home_dir == NULL || home_dir[0] == '\0') - home_dir = "c:/users/default"; /* poor default */ -# else if (home_dir == NULL || home_dir[0] == '\0') { +#ifdef WINDOWS32 + /* Windows NT defines HOMEDRIVE and HOMEPATH. But give + preference to HOME, because the user can change HOME. */ + const char *home_drive = getenv ("HOMEDRIVE"); + const char *home_path = getenv ("HOMEPATH"); + + if (home_drive != NULL && home_path != NULL) + { + size_t home_drive_len = strlen (home_drive); + size_t home_path_len = strlen (home_path); + char *mem = alloca (home_drive_len + home_path_len + 1); + + memcpy (mem, home_drive, home_drive_len); + memcpy (mem + home_drive_len, home_path, home_path_len + 1); + home_dir = mem; + } + else + home_dir = "c:/users/default"; /* poor default */ +#else int success; char *name; + int malloc_name = 0; size_t buflen = GET_LOGIN_NAME_MAX () + 1; if (buflen == 0) - /* `sysconf' does not support _SC_LOGIN_NAME_MAX. Try + /* 'sysconf' does not support _SC_LOGIN_NAME_MAX. Try a moderate value. */ buflen = 20; - name = alloca_account (buflen, alloca_used); + if (glob_use_alloca (alloca_used, buflen)) + name = alloca_account (buflen, alloca_used); + else + { + name = malloc (buflen); + if (name == NULL) + { + retval = GLOB_NOSPACE; + goto out; + } + malloc_name = 1; + } success = __getlogin_r (name, buflen) == 0; if (success) { struct passwd *p; -# if defined HAVE_GETPWNAM_R || defined _LIBC - long int pwbuflen = GETPW_R_SIZE_MAX (); + char *malloc_pwtmpbuf = NULL; char *pwtmpbuf; +# if defined HAVE_GETPWNAM_R || defined _LIBC + long int pwbuflenmax = GETPW_R_SIZE_MAX (); + size_t pwbuflen = pwbuflenmax; struct passwd pwbuf; - int malloc_pwtmpbuf = 0; int save = errno; -# ifndef _LIBC - if (pwbuflen == -1) - /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. +# ifndef _LIBC + if (! (0 < pwbuflenmax && pwbuflenmax <= SIZE_MAX)) + /* 'sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a moderate value. */ pwbuflen = 1024; -# endif - if (__libc_use_alloca (alloca_used + pwbuflen)) +# endif + if (glob_use_alloca (alloca_used, pwbuflen)) pwtmpbuf = alloca_account (pwbuflen, alloca_used); else { pwtmpbuf = malloc (pwbuflen); if (pwtmpbuf == NULL) { + if (__glibc_unlikely (malloc_name)) + free (name); retval = GLOB_NOSPACE; goto out; } - malloc_pwtmpbuf = 1; + malloc_pwtmpbuf = pwtmpbuf; } while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) != 0) { + size_t newlen; + bool v; if (errno != ERANGE) { p = NULL; break; } - - if (!malloc_pwtmpbuf - && __libc_use_alloca (alloca_used - + 2 * pwbuflen)) + v = size_add_wrapv (pwbuflen, pwbuflen, &newlen); + if (!v && malloc_pwtmpbuf == NULL + && glob_use_alloca (alloca_used, newlen)) pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen, - 2 * pwbuflen, - alloca_used); + newlen, alloca_used); else { - char *newp = realloc (malloc_pwtmpbuf - ? pwtmpbuf : NULL, - 2 * pwbuflen); + char *newp = (v ? NULL + : realloc (malloc_pwtmpbuf, newlen)); if (newp == NULL) { - if (__glibc_unlikely (malloc_pwtmpbuf)) - free (pwtmpbuf); + free (malloc_pwtmpbuf); + if (__glibc_unlikely (malloc_name)) + free (name); retval = GLOB_NOSPACE; goto out; } - pwtmpbuf = newp; - pwbuflen = 2 * pwbuflen; - malloc_pwtmpbuf = 1; + malloc_pwtmpbuf = pwtmpbuf = newp; } + pwbuflen = newlen; __set_errno (save); } -# else +# else p = getpwnam (name); -# endif +# endif + if (__glibc_unlikely (malloc_name)) + free (name); if (p != NULL) { - if (!malloc_pwtmpbuf) + if (malloc_pwtmpbuf == NULL) home_dir = p->pw_dir; else { size_t home_dir_len = strlen (p->pw_dir) + 1; - if (__libc_use_alloca (alloca_used + home_dir_len)) + if (glob_use_alloca (alloca_used, home_dir_len)) home_dir = alloca_account (home_dir_len, alloca_used); else @@ -720,26 +739,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), malloc_home_dir = 1; } memcpy (home_dir, p->pw_dir, home_dir_len); - - free (pwtmpbuf); } } + free (malloc_pwtmpbuf); } + else + { + if (__glibc_unlikely (malloc_name)) + free (name); + } +#endif /* WINDOWS32 */ } if (home_dir == NULL || home_dir[0] == '\0') { + if (__glibc_unlikely (malloc_home_dir)) + free (home_dir); if (flags & GLOB_TILDE_CHECK) { - if (__glibc_unlikely (malloc_home_dir)) - free (home_dir); retval = GLOB_NOMATCH; goto out; } else - home_dir = (char *) "~"; /* No luck. */ + { + home_dir = (char *) "~"; /* No luck. */ + malloc_home_dir = 0; + } } -# endif /* WINDOWS32 */ -# endif /* Now construct the full directory. */ if (dirname[1] == '\0') { @@ -754,8 +779,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { char *newp; size_t home_len = strlen (home_dir); - int use_alloca = __libc_use_alloca (alloca_used - + home_len + dirlen); + int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen); if (use_alloca) newp = alloca_account (home_len + dirlen, alloca_used); else @@ -779,12 +803,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirname = newp; dirlen += home_len - 1; malloc_dirname = !use_alloca; + + if (__glibc_unlikely (malloc_home_dir)) + free (home_dir); } dirname_modified = 1; } -# if !defined _AMIGA && !defined WINDOWS32 else { +#ifndef WINDOWS32 char *end_name = strchr (dirname, '/'); char *user_name; int malloc_user_name = 0; @@ -806,7 +833,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), else { char *newp; - if (__libc_use_alloca (alloca_used + (end_name - dirname))) + if (glob_use_alloca (alloca_used, end_name - dirname)) newp = alloca_account (end_name - dirname, alloca_used); else { @@ -823,11 +850,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), char *p = mempcpy (newp, dirname + 1, unescape - dirname - 1); char *q = unescape; - while (*q != '\0') + while (q != end_name) { if (*q == '\\') { - if (q[1] == '\0') + if (q + 1 == end_name) { /* "~fo\\o\\" unescape to user_name "foo\\", but "~fo\\o\\/" unescape to user_name @@ -843,7 +870,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), *p = '\0'; } else - *((char *) mempcpy (newp, dirname + 1, end_name - dirname)) + *((char *) mempcpy (newp, dirname + 1, end_name - dirname - 1)) = '\0'; user_name = newp; } @@ -851,20 +878,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), /* Look up specific user's home directory. */ { struct passwd *p; + char *malloc_pwtmpbuf = NULL; # if defined HAVE_GETPWNAM_R || defined _LIBC - long int buflen = GETPW_R_SIZE_MAX (); + long int buflenmax = GETPW_R_SIZE_MAX (); + size_t buflen = buflenmax; char *pwtmpbuf; - int malloc_pwtmpbuf = 0; struct passwd pwbuf; int save = errno; # ifndef _LIBC - if (buflen == -1) - /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a + if (! (0 <= buflenmax && buflenmax <= SIZE_MAX)) + /* Perhaps 'sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a moderate value. */ buflen = 1024; # endif - if (__libc_use_alloca (alloca_used + buflen)) + if (glob_use_alloca (alloca_used, buflen)) pwtmpbuf = alloca_account (buflen, alloca_used); else { @@ -877,32 +905,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), retval = GLOB_NOSPACE; goto out; } - malloc_pwtmpbuf = 1; + malloc_pwtmpbuf = pwtmpbuf; } while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0) { + size_t newlen; + bool v; if (errno != ERANGE) { p = NULL; break; } - if (!malloc_pwtmpbuf - && __libc_use_alloca (alloca_used + 2 * buflen)) + v = size_add_wrapv (buflen, buflen, &newlen); + if (!v && malloc_pwtmpbuf == NULL + && glob_use_alloca (alloca_used, newlen)) pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen, - 2 * buflen, alloca_used); + newlen, alloca_used); else { - char *newp = realloc (malloc_pwtmpbuf ? pwtmpbuf : NULL, - 2 * buflen); + char *newp = v ? NULL : realloc (malloc_pwtmpbuf, newlen); if (newp == NULL) { - if (__glibc_unlikely (malloc_pwtmpbuf)) - free (pwtmpbuf); + free (malloc_pwtmpbuf); goto nomem_getpw; } - pwtmpbuf = newp; - malloc_pwtmpbuf = 1; + malloc_pwtmpbuf = pwtmpbuf = newp; } __set_errno (save); } @@ -923,7 +951,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), free (dirname); malloc_dirname = 0; - if (__libc_use_alloca (alloca_used + home_len + rest_len + 1)) + if (glob_use_alloca (alloca_used, home_len + rest_len + 1)) dirname = alloca_account (home_len + rest_len + 1, alloca_used); else @@ -931,8 +959,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirname = malloc (home_len + rest_len + 1); if (dirname == NULL) { - if (__glibc_unlikely (malloc_pwtmpbuf)) - free (pwtmpbuf); + free (malloc_pwtmpbuf); retval = GLOB_NOSPACE; goto out; } @@ -944,24 +971,24 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirlen = home_len + rest_len; dirname_modified = 1; - if (__glibc_unlikely (malloc_pwtmpbuf)) - free (pwtmpbuf); + free (malloc_pwtmpbuf); } else { - if (__glibc_unlikely (malloc_pwtmpbuf)) - free (pwtmpbuf); + free (malloc_pwtmpbuf); if (flags & GLOB_TILDE_CHECK) - /* We have to regard it as an error if we cannot find the - home directory. */ - return GLOB_NOMATCH; + { + /* We have to regard it as an error if we cannot find the + home directory. */ + retval = GLOB_NOMATCH; + goto out; + } } } +#endif /* !WINDOWS32 */ } -# endif /* Not Amiga && not WINDOWS32. */ } -#endif /* Not VMS. */ /* Now test whether we looked for "~" or "~NAME". In this case we can give the answer now. */ @@ -980,19 +1007,18 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), size_t newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; - if (newcount > UINTPTR_MAX - (1 + 1) - || newcount + 1 + 1 > ~((size_t) 0) / sizeof (char *)) + if (newcount > SIZE_MAX / sizeof (char *) - 2) { nospace: free (pglob->gl_pathv); pglob->gl_pathv = NULL; pglob->gl_pathc = 0; - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } - new_gl_pathv - = (char **) realloc (pglob->gl_pathv, - (newcount + 1 + 1) * sizeof (char *)); + new_gl_pathv = realloc (pglob->gl_pathv, + (newcount + 2) * sizeof (char *)); if (new_gl_pathv == NULL) goto nospace; pglob->gl_pathv = new_gl_pathv; @@ -1006,12 +1032,19 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen); p[0] = '/'; p[1] = '\0'; + if (__glibc_unlikely (malloc_dirname)) + free (dirname); } else { - pglob->gl_pathv[newcount] = strdup (dirname); - if (pglob->gl_pathv[newcount] == NULL) - goto nospace; + if (__glibc_unlikely (malloc_dirname)) + pglob->gl_pathv[newcount] = dirname; + else + { + pglob->gl_pathv[newcount] = strdup (dirname); + if (pglob->gl_pathv[newcount] == NULL) + goto nospace; + } } pglob->gl_pathv[++newcount] = NULL; ++pglob->gl_pathc; @@ -1021,7 +1054,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), } /* Not found. */ - return GLOB_NOMATCH; + retval = GLOB_NOMATCH; + goto out; } meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE)); @@ -1067,7 +1101,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (status != 0) { if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH) - return status; + { + retval = status; + goto out; + } goto no_matches; } @@ -1078,19 +1115,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { size_t old_pathc; -#ifdef SHELL - { - /* Make globbing interruptible in the bash shell. */ - extern int interrupt_state; - - if (interrupt_state) - { - globfree (&dirs); - return GLOB_ABORTED; - } - } -#endif /* SHELL. */ - old_pathc = pglob->gl_pathc; status = glob_in_dir (filename, dirs.gl_pathv[i], ((flags | GLOB_APPEND) @@ -1105,7 +1129,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; - return status; + retval = status; + goto out; } /* Stick the directory on the front of each name. */ @@ -1116,13 +1141,14 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } } flags |= GLOB_MAGCHAR; - /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls. + /* We have ignored the GLOB_NOCHECK flag in the 'glob_in_dir' calls. But if we have not found any matching entry and the GLOB_NOCHECK flag was set we must return the input pattern itself. */ if (pglob->gl_pathc + pglob->gl_offs == oldcount) @@ -1134,28 +1160,28 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), size_t newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; - if (newcount > UINTPTR_MAX - 2 - || newcount + 2 > ~((size_t) 0) / sizeof (char *)) + if (newcount > SIZE_MAX / sizeof (char *) - 2) { nospace2: globfree (&dirs); - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } - new_gl_pathv = (char **) realloc (pglob->gl_pathv, - (newcount + 2) - * sizeof (char *)); + new_gl_pathv = realloc (pglob->gl_pathv, + (newcount + 2) * sizeof (char *)); if (new_gl_pathv == NULL) goto nospace2; pglob->gl_pathv = new_gl_pathv; - pglob->gl_pathv[newcount] = __strdup (pattern); + pglob->gl_pathv[newcount] = strdup (pattern); if (pglob->gl_pathv[newcount] == NULL) { globfree (&dirs); globfree (pglob); pglob->gl_pathc = 0; - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } ++pglob->gl_pathc; @@ -1167,7 +1193,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), else { globfree (&dirs); - return GLOB_NOMATCH; + retval = GLOB_NOMATCH; + goto out; } } @@ -1213,7 +1240,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), flags = orig_flags; goto no_matches; } - return status; + retval = status; + goto out; } if (dirlen > 0) @@ -1225,7 +1253,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { globfree (pglob); pglob->gl_pathc = 0; - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } } } @@ -1250,7 +1279,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { globfree (pglob); pglob->gl_pathc = 0; - return GLOB_NOSPACE; + retval = GLOB_NOSPACE; + goto out; } strcpy (&new[len - 2], "/"); pglob->gl_pathv[i] = new; @@ -1276,32 +1306,12 @@ libc_hidden_def (glob) #endif -#if !defined _LIBC || !defined GLOB_ONLY_P - -/* Free storage allocated in PGLOB by a previous `glob' call. */ -void -globfree (glob_t *pglob) -{ - if (pglob->gl_pathv != NULL) - { - size_t i; - for (i = 0; i < pglob->gl_pathc; ++i) - free (pglob->gl_pathv[pglob->gl_offs + i]); - free (pglob->gl_pathv); - pglob->gl_pathv = NULL; - } -} -#if defined _LIBC && !defined globfree -libc_hidden_def (globfree) -#endif - - /* Do a collated comparison of A and B. */ static int collated_compare (const void *a, const void *b) { - const char *const s1 = *(const char *const * const) a; - const char *const s2 = *(const char *const * const) b; + char *const *ps1 = a; char *s1 = *ps1; + char *const *ps2 = b; char *s2 = *ps2; if (s1 == s2) return 0; @@ -1322,28 +1332,24 @@ prefix_array (const char *dirname, char **array, size_t n) { size_t i; size_t dirlen = strlen (dirname); -#if defined __MSDOS__ || defined WINDOWS32 - int sep_char = '/'; -# define DIRSEP_CHAR sep_char -#else -# define DIRSEP_CHAR '/' -#endif + char dirsep_char = '/'; if (dirlen == 1 && dirname[0] == '/') /* DIRNAME is just "/", so normal prepending would get us "//foo". We want "/foo" instead, so don't prepend any chars from DIRNAME. */ dirlen = 0; + #if defined __MSDOS__ || defined WINDOWS32 - else if (dirlen > 1) + if (dirlen > 1) { if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':') /* DIRNAME is "d:/". Don't prepend the slash from DIRNAME. */ --dirlen; else if (dirname[dirlen - 1] == ':') { - /* DIRNAME is "d:". Use `:' instead of `/'. */ + /* DIRNAME is "d:". Use ':' instead of '/'. */ --dirlen; - sep_char = ':'; + dirsep_char = ':'; } } #endif @@ -1351,7 +1357,7 @@ prefix_array (const char *dirname, char **array, size_t n) for (i = 0; i < n; ++i) { size_t eltlen = strlen (array[i]) + 1; - char *new = (char *) malloc (dirlen + 1 + eltlen); + char *new = malloc (dirlen + 1 + eltlen); if (new == NULL) { while (i > 0) @@ -1361,7 +1367,7 @@ prefix_array (const char *dirname, char **array, size_t n) { char *endp = mempcpy (new, dirname, dirlen); - *endp++ = DIRSEP_CHAR; + *endp++ = dirsep_char; mempcpy (endp, array[i], eltlen); } free (array[i]); @@ -1371,103 +1377,57 @@ prefix_array (const char *dirname, char **array, size_t n) return 0; } - -/* We must not compile this function twice. */ -#if !defined _LIBC || !defined NO_GLOB_PATTERN_P -int -__glob_pattern_type (const char *pattern, int quote) -{ - const char *p; - int ret = 0; - - for (p = pattern; *p != '\0'; ++p) - switch (*p) - { - case '?': - case '*': - return 1; - - case '\\': - if (quote) - { - if (p[1] != '\0') - ++p; - ret |= 2; - } - break; - - case '[': - ret |= 4; - break; - - case ']': - if (ret & 4) - return 1; - break; - } - - return ret; -} - -/* Return nonzero if PATTERN contains any metacharacters. - Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ -int -__glob_pattern_p (const char *pattern, int quote) -{ - return __glob_pattern_type (pattern, quote) == 1; -} -# ifdef _LIBC -weak_alias (__glob_pattern_p, glob_pattern_p) -# endif -#endif - -#endif /* !GLOB_ONLY_P */ - - /* We put this in a separate function mainly to allow the memory allocated with alloca to be recycled. */ -#if !defined _LIBC || !defined GLOB_ONLY_P static int __attribute_noinline__ -link_exists2_p (const char *dir, size_t dirlen, const char *fname, - glob_t *pglob -# ifndef _LIBC - , int flags +link_stat (const char *dir, size_t dirlen, const char *fname, + glob_t *pglob +# if !defined _LIBC && !HAVE_FSTATAT + , int flags # endif - ) + ) { size_t fnamelen = strlen (fname); - char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1); + char *fullname = __alloca (dirlen + 1 + fnamelen + 1); struct stat st; -# ifndef _LIBC - struct_stat64 st64; -# endif mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1), fname, fnamelen + 1); -# ifdef _LIBC - return (*pglob->gl_stat) (fullname, &st) == 0; -# else - return ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? (*pglob->gl_stat) (fullname, &st) - : __stat64 (fullname, &st64)) == 0); +# if !defined _LIBC && !HAVE_FSTATAT + if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) == 0, 1)) + { + struct_stat64 st64; + return __stat64 (fullname, &st64); + } # endif + return (*pglob->gl_stat) (fullname, &st); } -# ifdef _LIBC -# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ - (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) \ - ? link_exists2_p (dirname, dirnamelen, fname, pglob) \ - : ({ struct stat64 st64; \ - __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; })) + +/* Return true if DIR/FNAME exists. */ +static int +link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname, + glob_t *pglob, int flags) +{ + int status; +# if defined _LIBC || HAVE_FSTATAT + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + status = link_stat (dir, dirlen, fname, pglob); + else + { + /* dfd cannot be -1 here, because dirfd never returns -1 on + glibc, or on hosts that have fstatat. */ + struct_stat64 st64; + status = __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0); + } # else -# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ - link_exists2_p (dirname, dirnamelen, fname, pglob, flags) + status = link_stat (dir, dirlen, fname, pglob, flags); # endif -#endif - + return status == 0 || errno == EOVERFLOW; +} -/* Like `glob', but PATTERN is a final pathname component, +/* Like 'glob', but PATTERN is a final pathname component, and matches are searched for in DIRECTORY. The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done. The GLOB_APPEND flag is assumed to be set (always appends). */ @@ -1478,25 +1438,25 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { size_t dirlen = strlen (directory); void *stream = NULL; - struct globnames - { - struct globnames *next; - size_t count; - char *name[64]; - }; -#define INITIAL_COUNT sizeof (init_names.name) / sizeof (init_names.name[0]) - struct globnames init_names; - struct globnames *names = &init_names; - struct globnames *names_alloca = &init_names; +# define GLOBNAMES_MEMBERS(nnames) \ + struct globnames *next; size_t count; char *name[nnames]; + struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) }; + struct { GLOBNAMES_MEMBERS (64) } init_names_buf; + struct globnames *init_names = (struct globnames *) &init_names_buf; + struct globnames *names = init_names; + struct globnames *names_alloca = init_names; size_t nfound = 0; size_t cur = 0; int meta; int save; + int result; - alloca_used += sizeof (init_names); + alloca_used += sizeof init_names_buf; - init_names.next = NULL; - init_names.count = INITIAL_COUNT; + init_names->next = NULL; + init_names->count = ((sizeof init_names_buf + - offsetof (struct globnames, name)) + / sizeof init_names->name[0]); meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE)); if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) @@ -1516,14 +1476,16 @@ glob_in_dir (const char *pattern, const char *directory, int flags, struct_stat64 st64; } ust; size_t patlen = strlen (pattern); - int alloca_fullname = __libc_use_alloca (alloca_used - + dirlen + 1 + patlen + 1); + size_t fullsize; + bool alloca_fullname + = (! size_add_wrapv (dirlen + 1, patlen + 1, &fullsize) + && glob_use_alloca (alloca_used, fullsize)); char *fullname; if (alloca_fullname) - fullname = alloca_account (dirlen + 1 + patlen + 1, alloca_used); + fullname = alloca_account (fullsize, alloca_used); else { - fullname = malloc (dirlen + 1 + patlen + 1); + fullname = malloc (fullsize); if (fullname == NULL) return GLOB_NOSPACE; } @@ -1531,9 +1493,11 @@ glob_in_dir (const char *pattern, const char *directory, int flags, mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), "/", 1), pattern, patlen + 1); - if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) ? (*pglob->gl_stat) (fullname, &ust.st) - : __stat64 (fullname, &ust.st64)) == 0) + : __stat64 (fullname, &ust.st64)) + == 0) + || errno == EOVERFLOW) /* We found this file to be existing. Now tell the rest of the function to copy this name into the result. */ flags |= GLOB_NOCHECK; @@ -1555,16 +1519,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags, } else { -#ifdef _LIBC int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) ? -1 : dirfd ((DIR *) stream)); -#endif int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) - | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) -#if defined _AMIGA || defined VMS - | FNM_CASEFOLD -#endif - ); + | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)); flags |= GLOB_MAGCHAR; while (1) @@ -1584,19 +1542,24 @@ glob_in_dir (const char *pattern, const char *directory, int flags, } if (d.name == NULL) break; - if (d.skip_entry) + if (readdir_result_skip_entry (d)) continue; /* If we shall match only directories use the information provided by the dirent call if possible. */ - if ((flags & GLOB_ONLYDIR) && !readdir_result_might_be_dir (d)) - continue; + if (flags & GLOB_ONLYDIR) + switch (readdir_result_type (d)) + { + case DT_DIR: case DT_LNK: case DT_UNKNOWN: break; + default: continue; + } if (fnmatch (pattern, d.name, fnm_flags) == 0) { /* If the file we found is a symlink we have to make sure the target file exists. */ - if (!readdir_result_might_be_symlink (d) + dirent_type type = readdir_result_type (d); + if (! (type == DT_LNK || type == DT_UNKNOWN) || link_exists_p (dfd, directory, dirlen, d.name, pglob, flags)) { @@ -1604,10 +1567,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { struct globnames *newnames; size_t count = names->count * 2; - size_t size = (sizeof (struct globnames) - + ((count - INITIAL_COUNT) - * sizeof (char *))); - if (__libc_use_alloca (alloca_used + size)) + size_t nameoff = offsetof (struct globnames, name); + size_t size = FLEXSIZEOF (struct globnames, name, + count * sizeof (char *)); + if ((SIZE_MAX - nameoff) / 2 / sizeof (char *) + < names->count) + goto memory_error; + if (glob_use_alloca (alloca_used, size)) newnames = names_alloca = alloca_account (size, alloca_used); else if ((newnames = malloc (size)) @@ -1623,6 +1589,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags, goto memory_error; ++cur; ++nfound; + if (SIZE_MAX - pglob->gl_offs <= nfound) + goto memory_error; } } } @@ -1633,29 +1601,27 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { size_t len = strlen (pattern); nfound = 1; - names->name[cur] = (char *) malloc (len + 1); + names->name[cur] = malloc (len + 1); if (names->name[cur] == NULL) goto memory_error; *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0'; } - int result = GLOB_NOMATCH; + result = GLOB_NOMATCH; if (nfound != 0) { + char **new_gl_pathv; result = 0; - if (pglob->gl_pathc > UINTPTR_MAX - pglob->gl_offs - || pglob->gl_pathc + pglob->gl_offs > UINTPTR_MAX - nfound - || pglob->gl_pathc + pglob->gl_offs + nfound > UINTPTR_MAX - 1 - || (pglob->gl_pathc + pglob->gl_offs + nfound + 1 - > UINTPTR_MAX / sizeof (char *))) + if (SIZE_MAX / sizeof (char *) - pglob->gl_pathc + < pglob->gl_offs + nfound + 1) goto memory_error; - char **new_gl_pathv; new_gl_pathv - = (char **) realloc (pglob->gl_pathv, - (pglob->gl_pathc + pglob->gl_offs + nfound + 1) - * sizeof (char *)); + = realloc (pglob->gl_pathv, + (pglob->gl_pathc + pglob->gl_offs + nfound + 1) + * sizeof (char *)); + if (new_gl_pathv == NULL) { memory_error: @@ -1671,7 +1637,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, and this is the block assigned to OLD here. */ if (names == NULL) { - assert (old == &init_names); + assert (old == init_names); break; } cur = names->count; @@ -1697,7 +1663,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, and this is the block assigned to OLD here. */ if (names == NULL) { - assert (old == &init_names); + assert (old == init_names); break; } cur = names->count; diff --git a/posix/glob64.c b/posix/glob64.c index 6cb3d654a8..a515a1c12f 100644 --- a/posix/glob64.c +++ b/posix/glob64.c @@ -43,10 +43,4 @@ glob64 (const char *pattern, int flags, } libc_hidden_def (glob64) -void -globfree64 (glob64_t *pglob) -{ -} -libc_hidden_def (globfree64) - stub_warning (glob64) diff --git a/posix/glob_internal.h b/posix/glob_internal.h new file mode 100644 index 0000000000..12c93660b7 --- /dev/null +++ b/posix/glob_internal.h @@ -0,0 +1,57 @@ +/* Shared definition for glob and glob_pattern_p. + Copyright (C) 2017 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 GLOB_INTERNAL_H +# define GLOB_INTERNAL_H + +static inline int +__glob_pattern_type (const char *pattern, int quote) +{ + const char *p; + int ret = 0; + + for (p = pattern; *p != '\0'; ++p) + switch (*p) + { + case '?': + case '*': + return 1; + + case '\\': + if (quote) + { + if (p[1] != '\0') + ++p; + ret |= 2; + } + break; + + case '[': + ret |= 4; + break; + + case ']': + if (ret & 4) + return 1; + break; + } + + return ret; +} + +#endif /* GLOB_INTERNAL_H */ diff --git a/posix/glob_pattern_p.c b/posix/glob_pattern_p.c new file mode 100644 index 0000000000..a17d337182 --- /dev/null +++ b/posix/glob_pattern_p.c @@ -0,0 +1,33 @@ +/* Return nonzero if PATTERN contains any metacharacters. + Copyright (C) 2017 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 _LIBC +# include +#endif + +#include +#include "glob_internal.h" + +/* Return nonzero if PATTERN contains any metacharacters. + Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ +int +__glob_pattern_p (const char *pattern, int quote) +{ + return __glob_pattern_type (pattern, quote) == 1; +} +weak_alias (__glob_pattern_p, glob_pattern_p) diff --git a/posix/globfree.c b/posix/globfree.c new file mode 100644 index 0000000000..042e29d9b0 --- /dev/null +++ b/posix/globfree.c @@ -0,0 +1,41 @@ +/* Frees the dynamically allocated storage from an earlier call to glob. + Copyright (C) 2017 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 _LIBC +# include +#endif + +#include +#include + +/* Free storage allocated in PGLOB by a previous `glob' call. */ +void +globfree (glob_t *pglob) +{ + if (pglob->gl_pathv != NULL) + { + size_t i; + for (i = 0; i < pglob->gl_pathc; ++i) + free (pglob->gl_pathv[pglob->gl_offs + i]); + free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } +} +#ifndef globfree +libc_hidden_def (globfree) +#endif diff --git a/posix/globfree64.c b/posix/globfree64.c new file mode 100644 index 0000000000..c9f8908a4e --- /dev/null +++ b/posix/globfree64.c @@ -0,0 +1,31 @@ +/* Frees the dynamically allocated storage from an earlier call to glob. + Copyright (C) 2017 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 _LIBC +# include +#endif + +#include +#include + +/* Free storage allocated in PGLOB by a previous `glob' call. */ +void +globfree64 (glob64_t *pglob) +{ +} +libc_hidden_def (globfree64) diff --git a/posix/globtest.sh b/posix/globtest.sh index f9cc80b4b5..73f7ae31cc 100755 --- a/posix/globtest.sh +++ b/posix/globtest.sh @@ -47,7 +47,12 @@ testout=${common_objpfx}posix/globtest-out rm -rf $testdir $testout mkdir $testdir -trap 'chmod 777 $testdir/noread; rm -fr $testdir $testout' 1 2 3 15 +cleanup() { + chmod 777 $testdir/noread + rm -fr $testdir $testout +} + +trap cleanup 0 HUP INT QUIT TERM echo 1 > $testdir/file1 echo 2 > $testdir/file2 @@ -811,8 +816,6 @@ if test $failed -ne 0; then fi if test $result -eq 0; then - chmod 777 $testdir/noread - rm -fr $testdir $testout echo "All OK." > $logfile fi diff --git a/posix/tst-glob-tilde.c b/posix/tst-glob-tilde.c new file mode 100644 index 0000000000..6886f4371f --- /dev/null +++ b/posix/tst-glob-tilde.c @@ -0,0 +1,143 @@ +/* Check for GLOB_TIDLE heap allocation issues (bugs 22320, 22325, 22332). + Copyright (C) 2017 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 + +/* Flag which indicates whether to pass the GLOB_ONLYDIR flag. */ +static int do_onlydir; + +/* Flag which indicates whether to pass the GLOB_NOCHECK flag. */ +static int do_nocheck; + +/* Flag which indicates whether to pass the GLOB_MARK flag. */ +static int do_mark; + +/* Flag which indicates whether to pass the GLOB_NOESCAPE flag. */ +static int do_noescape; + +static void +one_test (const char *prefix, const char *middle, const char *suffix) +{ + char *pattern = xasprintf ("%s%s%s", prefix, middle, suffix); + int flags = GLOB_TILDE; + if (do_onlydir) + flags |= GLOB_ONLYDIR; + if (do_nocheck) + flags |= GLOB_NOCHECK; + if (do_mark) + flags |= GLOB_MARK; + if (do_noescape) + flags |= GLOB_NOESCAPE; + glob_t gl; + /* This glob call might result in crashes or memory leaks. */ + if (glob (pattern, flags, NULL, &gl) == 0) + globfree (&gl); + free (pattern); +} + +enum + { + /* The largest base being tested. */ + largest_base_size = 500000, + + /* The actual size is the base size plus a variable whose absolute + value is not greater than this. This helps malloc to trigger + overflows. */ + max_size_skew = 16, + + /* The maximum string length supported by repeating_string + below. */ + repeat_size = largest_base_size + max_size_skew, + }; + +/* Used to construct strings which repeat a single character 'x'. */ +static char *repeat; + +/* Return a string of SIZE characters. */ +const char * +repeating_string (int size) +{ + TEST_VERIFY (size >= 0); + TEST_VERIFY (size <= repeat_size); + const char *repeated_shifted = repeat + repeat_size - size; + TEST_VERIFY (strlen (repeated_shifted) == size); + return repeated_shifted; +} + +static int +do_test (void) +{ + /* Avoid network-based NSS modules and initialize nss_files with a + dummy lookup. This has to come before mtrace because NSS does + not free all memory. */ + __nss_configure_lookup ("passwd", "files"); + (void) getpwnam ("root"); + + mtrace (); + + repeat = xmalloc (repeat_size + 1); + memset (repeat, 'x', repeat_size); + repeat[repeat_size] = '\0'; + + /* These numbers control the size of the user name. The values + cover the minimum (0), a typical size (8), a large + stack-allocated size (100000), and a somewhat large + heap-allocated size (largest_base_size). */ + static const int base_sizes[] = { 0, 8, 100, 100000, largest_base_size, -1 }; + + for (do_onlydir = 0; do_onlydir < 2; ++do_onlydir) + for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck) + for (do_mark = 0; do_mark < 2; ++do_mark) + for (do_noescape = 0; do_noescape < 2; ++do_noescape) + for (int base_idx = 0; base_sizes[base_idx] >= 0; ++base_idx) + { + for (int size_skew = -max_size_skew; size_skew <= max_size_skew; + ++size_skew) + { + int size = base_sizes[base_idx] + size_skew; + if (size < 0) + continue; + + const char *user_name = repeating_string (size); + one_test ("~", user_name, "/a/b"); + one_test ("~", user_name, "x\\x\\x////x\\a"); + } + + const char *user_name = repeating_string (base_sizes[base_idx]); + one_test ("~", user_name, ""); + one_test ("~", user_name, "/"); + one_test ("~", user_name, "/a"); + one_test ("~", user_name, "/*/*"); + one_test ("~", user_name, "\\/"); + one_test ("/~", user_name, ""); + one_test ("*/~", user_name, "/a/b"); + } + + free (repeat); + + return 0; +} + +#include diff --git a/resolv/Makefile b/resolv/Makefile index fdc37edff1..01086d569f 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -46,6 +46,7 @@ tests += \ tst-res_hconf_reorder \ tst-res_use_inet6 \ tst-resolv-basic \ + tst-resolv-edns \ tst-resolv-network \ tst-resolv-search \ @@ -124,6 +125,7 @@ $(objpfx)tst-bug18665-tcp: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-bug18665: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-res_use_inet6: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library) $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library) diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c index d80b5318e5..5a0bb1044b 100644 --- a/resolv/res_mkquery.c +++ b/resolv/res_mkquery.c @@ -69,7 +69,7 @@ #include #include #include -#include +#include #include #include #include @@ -243,7 +243,30 @@ __res_nopt(res_state statp, *cp++ = 0; /* "." */ NS_PUT16(T_OPT, cp); /* TYPE */ - NS_PUT16(MIN(anslen, 0xffff), cp); /* CLASS = UDP payload size */ + + /* Lowering the advertised buffer size based on the actual + answer buffer size is desirable because the server will + minimize the reply to fit into the UDP packet (and A + non-minimal response might not fit the buffer). + + The RESOLV_EDNS_BUFFER_SIZE limit could still result in TCP + fallback and a non-minimal response which has to be + hard-truncated in the stub resolver, but this is price to + pay for avoiding fragmentation. (This issue does not + affect the nss_dns functions because they use the stub + resolver in such a way that it allocates a properly sized + response buffer.) */ + { + uint16_t buffer_size; + if (anslen < 512) + buffer_size = 512; + else if (anslen > RESOLV_EDNS_BUFFER_SIZE) + buffer_size = RESOLV_EDNS_BUFFER_SIZE; + else + buffer_size = anslen; + NS_PUT16 (buffer_size, cp); + } + *cp++ = NOERROR; /* extended RCODE */ *cp++ = 0; /* EDNS version */ @@ -261,4 +284,3 @@ __res_nopt(res_state statp, return cp - buf; } -libresolv_hidden_def (__res_nopt) diff --git a/resolv/res_query.c b/resolv/res_query.c index 07dc6f6583..57156d01ec 100644 --- a/resolv/res_query.c +++ b/resolv/res_query.c @@ -77,6 +77,7 @@ #include #include #include +#include /* Options. Leave them on. */ /* #undef DEBUG */ @@ -146,7 +147,10 @@ __libc_res_nquery(res_state statp, if ((oflags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) { - n = __res_nopt(statp, n, query1, bufsize, anslen / 2); + /* Use RESOLV_EDNS_BUFFER_SIZE because the receive + buffer can be reallocated. */ + n = __res_nopt (statp, n, query1, bufsize, + RESOLV_EDNS_BUFFER_SIZE); if (n < 0) goto unspec_nomem; } @@ -167,8 +171,10 @@ __libc_res_nquery(res_state statp, if (n > 0 && (oflags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) - n = __res_nopt(statp, n, query2, bufsize - nused - n, - anslen / 2); + /* Use RESOLV_EDNS_BUFFER_SIZE because the receive + buffer can be reallocated. */ + n = __res_nopt (statp, n, query2, bufsize, + RESOLV_EDNS_BUFFER_SIZE); nquery2 = n; } @@ -182,7 +188,16 @@ __libc_res_nquery(res_state statp, if (n > 0 && (oflags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) - n = __res_nopt(statp, n, query1, bufsize, anslen); + { + /* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer + can be reallocated. */ + size_t advertise; + if (answerp == NULL) + advertise = anslen; + else + advertise = RESOLV_EDNS_BUFFER_SIZE; + n = __res_nopt (statp, n, query1, bufsize, advertise); + } nquery1 = n; } diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h index 99fc17c609..76fbe2f1a6 100644 --- a/resolv/resolv-internal.h +++ b/resolv/resolv-internal.h @@ -32,4 +32,22 @@ res_use_inet6 (void) return _res.options & DEPRECATED_RES_USE_INET6; } +enum + { + /* The advertized EDNS buffer size. The value 1200 is derived + from the IPv6 minimum MTU (1280 bytes) minus some arbitrary + space for tunneling overhead. If the DNS server does not react + to ICMP Fragmentation Needed But DF Set messages, this should + avoid all UDP fragments on current networks. Avoiding UDP + fragments is desirable because it prevents fragmentation-based + spoofing attacks because the randomness in a DNS packet is + concentrated in the first fragment (with the headers) and does + not protect subsequent fragments. */ + RESOLV_EDNS_BUFFER_SIZE = 1200, + }; + +/* Add an OPT record to a DNS query. */ +int __res_nopt (res_state, int n0, unsigned char *buf, int buflen, + int anslen) attribute_hidden; + #endif /* _RESOLV_INTERNAL_H */ diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c new file mode 100644 index 0000000000..f17dbc3450 --- /dev/null +++ b/resolv/tst-resolv-edns.c @@ -0,0 +1,501 @@ +/* Test EDNS handling in the stub resolver. + Copyright (C) 2016-2017 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 + +/* Data produced by a test query. */ +struct response_data +{ + char *qname; + uint16_t qtype; + struct resolv_edns_info edns; +}; + +/* Global array used by put_response and get_response to record + response data. The test DNS server returns the index of the array + element which contains the actual response data. This enables the + test case to return arbitrary amounts of data with the limited + number of bits which fit into an IP addres. + + The volatile specifier is needed because the test case accesses + these variables from a callback function called from a function + which is marked as __THROW (i.e., a leaf function which actually is + not). */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static struct response_data ** volatile response_data_array; +volatile static size_t response_data_count; + +/* Extract information from the query, store it in a struct + response_data object, and return its index in the + response_data_array. */ +static unsigned int +put_response (const struct resolv_response_context *ctx, + const char *qname, uint16_t qtype) +{ + xpthread_mutex_lock (&mutex); + ++response_data_count; + /* We only can represent 2**24 indexes in 10.0.0.0/8. */ + TEST_VERIFY (response_data_count < (1 << 24)); + response_data_array = xrealloc + (response_data_array, sizeof (*response_data_array) * response_data_count); + unsigned int index = response_data_count - 1; + struct response_data *data = xmalloc (sizeof (*data)); + *data = (struct response_data) + { + .qname = xstrdup (qname), + .qtype = qtype, + .edns = ctx->edns, + }; + response_data_array[index] = data; + xpthread_mutex_unlock (&mutex); + return index; +} + +/* Verify the index into the response_data array and return the data + at it. */ +static struct response_data * +get_response (unsigned int index) +{ + xpthread_mutex_lock (&mutex); + TEST_VERIFY_EXIT (index < response_data_count); + struct response_data *result = response_data_array[index]; + xpthread_mutex_unlock (&mutex); + return result; +} + +/* Deallocate all response data. */ +static void +free_response_data (void) +{ + xpthread_mutex_lock (&mutex); + size_t count = response_data_count; + struct response_data **array = response_data_array; + for (unsigned int i = 0; i < count; ++i) + { + struct response_data *data = array[i]; + free (data->qname); + free (data); + } + free (array); + response_data_array = NULL; + response_data_count = 0; + xpthread_mutex_unlock (&mutex); +} + +#define EDNS_PROBE_EXAMPLE "edns-probe.example" + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_VERIFY_EXIT (qname != NULL); + + /* The "tcp." prefix can be used to request TCP fallback. */ + const char *qname_compare = qname; + bool force_tcp; + if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0) + { + force_tcp = true; + qname_compare += strlen ("tcp."); + } + else + force_tcp = false; + + enum {edns_probe} requested_qname; + if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0) + requested_qname = edns_probe; + else + { + support_record_failure (); + printf ("error: unexpected QNAME: %s\n", qname); + return; + } + TEST_VERIFY_EXIT (qclass == C_IN); + struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + if (flags.tc) + return; + + if (test_verbose) + printf ("info: edns=%d payload_size=%d\n", + ctx->edns.active, ctx->edns.payload_size); + + /* Encode the response_data object in multiple address records. + Each record carries two bytes of payload data, and an index. */ + resolv_response_section (b, ns_s_an); + switch (requested_qname) + { + case edns_probe: + { + unsigned int index = put_response (ctx, qname, qtype); + switch (qtype) + { + case T_A: + { + uint32_t addr = htonl (0x0a000000 | index); + resolv_response_open_record (b, qname, qclass, qtype, 0); + resolv_response_add_data (b, &addr, sizeof (addr)); + resolv_response_close_record (b); + } + break; + case T_AAAA: + { + char addr[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + index >> 16, index >> 8, index}; + resolv_response_open_record (b, qname, qclass, qtype, 0); + resolv_response_add_data (b, &addr, sizeof (addr)); + resolv_response_close_record (b); + } + } + } + break; + } +} + +/* Update *DATA with data from ADDRESS of SIZE. Set the corresponding + flag in SHADOW for each byte written. */ +static struct response_data * +decode_address (const void *address, size_t size) +{ + switch (size) + { + case 4: + TEST_VERIFY (memcmp (address, "\x0a", 1) == 0); + break; + case 16: + TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0); + break; + default: + FAIL_EXIT1 ("unexpected address size %zu", size); + } + const unsigned char *addr = address; + unsigned int index = addr[size - 3] * 256 * 256 + + addr[size - 2] * 256 + + addr[size - 1]; + return get_response (index); +} + +static struct response_data * +decode_hostent (struct hostent *e) +{ + TEST_VERIFY_EXIT (e != NULL); + TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL); + TEST_VERIFY (e->h_addr_list[1] == NULL); + return decode_address (e->h_addr_list[0], e->h_length); +} + +static struct response_data * +decode_addrinfo (struct addrinfo *ai, int family) +{ + struct response_data *data = NULL; + while (ai != NULL) + { + if (ai->ai_family == family) + { + struct response_data *new_data; + switch (family) + { + case AF_INET: + { + struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr; + new_data = decode_address (&pin->sin_addr.s_addr, 4); + } + break; + case AF_INET6: + { + struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr; + new_data = decode_address (&pin->sin6_addr.s6_addr, 16); + } + break; + default: + FAIL_EXIT1 ("invalid address family %d", ai->ai_family); + } + if (data == NULL) + data = new_data; + else + /* Check pointer equality because this should be the same + response (same index). */ + TEST_VERIFY (data == new_data); + } + ai = ai->ai_next; + } + TEST_VERIFY_EXIT (data != NULL); + return data; +} + +/* Updated by the main test loop in accordance with what is set in + _res.options. */ +static bool use_edns; +static bool use_dnssec; + +/* Verify the decoded response data against the flags above. */ +static void +verify_response_data_payload (struct response_data *data, + size_t expected_payload) +{ + bool edns = use_edns || use_dnssec; + TEST_VERIFY (data->edns.active == edns); + if (!edns) + expected_payload = 0; + if (data->edns.payload_size != expected_payload) + { + support_record_failure (); + printf ("error: unexpected payload size %d (edns=%d)\n", + (int) data->edns.payload_size, edns); + } + uint16_t expected_flags = 0; + if (use_dnssec) + expected_flags |= 0x8000; /* DO flag. */ + if (data->edns.flags != expected_flags) + { + support_record_failure (); + printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n", + (int) data->edns.flags, edns); + } +} + +/* Same as verify_response_data_payload, but use the default + payload. */ +static void +verify_response_data (struct response_data *data) +{ + verify_response_data_payload (data, 1200); +} + +static void +check_hostent (struct hostent *e) +{ + TEST_VERIFY_EXIT (e != NULL); + verify_response_data (decode_hostent (e)); +} + +static void +do_ai (int family) +{ + struct addrinfo hints = { .ai_family = family }; + struct addrinfo *ai; + int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai); + TEST_VERIFY_EXIT (ret == 0); + switch (family) + { + case AF_INET: + case AF_INET6: + verify_response_data (decode_addrinfo (ai, family)); + break; + case AF_UNSPEC: + verify_response_data (decode_addrinfo (ai, AF_INET)); + verify_response_data (decode_addrinfo (ai, AF_INET6)); + break; + default: + FAIL_EXIT1 ("invalid address family %d", family); + } + freeaddrinfo (ai); +} + +enum res_op +{ + res_op_search, + res_op_query, + res_op_querydomain, + res_op_nsearch, + res_op_nquery, + res_op_nquerydomain, + + res_op_last = res_op_nquerydomain, +}; + +static const char * +res_op_string (enum res_op op) +{ + switch (op) + { + case res_op_search: + return "res_search"; + case res_op_query: + return "res_query"; + case res_op_querydomain: + return "res_querydomain"; + case res_op_nsearch: + return "res_nsearch"; + case res_op_nquery: + return "res_nquery"; + case res_op_nquerydomain: + return "res_nquerydomain"; + } + FAIL_EXIT1 ("invalid res_op value %d", (int) op); +} + +/* Call libresolv function OP to look up PROBE_NAME, with an answer + buffer of SIZE bytes. Check that the advertised UDP buffer size is + in fact EXPECTED_BUFFER_SIZE. */ +static void +do_res_search (const char *probe_name, enum res_op op, size_t size, + size_t expected_buffer_size) +{ + if (test_verbose) + printf ("info: testing %s with buffer size %zu\n", + res_op_string (op), size); + unsigned char *buffer = xmalloc (size); + int ret = -1; + switch (op) + { + case res_op_search: + ret = res_search (probe_name, C_IN, T_A, buffer, size); + break; + case res_op_query: + ret = res_query (probe_name, C_IN, T_A, buffer, size); + break; + case res_op_nsearch: + ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size); + break; + case res_op_nquery: + ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size); + break; + case res_op_querydomain: + case res_op_nquerydomain: + { + char *example_stripped = xstrdup (probe_name); + char *dot_example = strstr (example_stripped, ".example"); + if (dot_example != NULL && strcmp (dot_example, ".example") == 0) + { + /* Truncate the domain name. */ + *dot_example = '\0'; + if (op == res_op_querydomain) + ret = res_querydomain + (example_stripped, "example", C_IN, T_A, buffer, size); + else + ret = res_nquerydomain + (&_res, example_stripped, "example", C_IN, T_A, buffer, size); + } + else + FAIL_EXIT1 ("invalid probe name: %s", probe_name); + free (example_stripped); + } + break; + } + TEST_VERIFY_EXIT (ret > 12); + unsigned char *end = buffer + ret; + + HEADER *hd = (HEADER *) buffer; + TEST_VERIFY (ntohs (hd->qdcount) == 1); + TEST_VERIFY (ntohs (hd->ancount) == 1); + /* Skip over the header. */ + unsigned char *p = buffer + sizeof (*hd); + /* Skip over the question. */ + ret = dn_skipname (p, end); + TEST_VERIFY_EXIT (ret > 0); + p += ret; + TEST_VERIFY_EXIT (end - p >= 4); + p += 4; + /* Skip over the RNAME and the RR header, but stop at the RDATA + length. */ + ret = dn_skipname (p, end); + TEST_VERIFY_EXIT (ret > 0); + p += ret; + TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4); + p += 2 + 2 + 4; + /* The IP address should be 4 bytes long. */ + TEST_VERIFY_EXIT (p[0] == 0); + TEST_VERIFY_EXIT (p[1] == 4); + /* Extract the address information. */ + p += 2; + struct response_data *data = decode_address (p, 4); + + verify_response_data_payload (data, expected_buffer_size); + + free (buffer); +} + +static void +run_test (const char *probe_name) +{ + if (test_verbose) + printf ("\ninfo: * use_edns=%d use_dnssec=%d\n", + use_edns, use_dnssec); + check_hostent (gethostbyname (probe_name)); + check_hostent (gethostbyname2 (probe_name, AF_INET)); + check_hostent (gethostbyname2 (probe_name, AF_INET6)); + do_ai (AF_UNSPEC); + do_ai (AF_INET); + do_ai (AF_INET6); + + for (int op = 0; op <= res_op_last; ++op) + { + do_res_search (probe_name, op, 301, 512); + do_res_search (probe_name, op, 511, 512); + do_res_search (probe_name, op, 512, 512); + do_res_search (probe_name, op, 513, 513); + do_res_search (probe_name, op, 657, 657); + do_res_search (probe_name, op, 1199, 1199); + do_res_search (probe_name, op, 1200, 1200); + do_res_search (probe_name, op, 1201, 1200); + do_res_search (probe_name, op, 65535, 1200); + } +} + +static int +do_test (void) +{ + for (int do_edns = 0; do_edns < 2; ++do_edns) + for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec) + for (int do_tcp = 0; do_tcp < 2; ++do_tcp) + { + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + use_edns = do_edns; + if (do_edns) + _res.options |= RES_USE_EDNS0; + use_dnssec = do_dnssec; + if (do_dnssec) + _res.options |= RES_USE_DNSSEC; + + char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE); + if (do_tcp) + { + char *n = xasprintf ("tcp.%s", probe_name); + free (probe_name); + probe_name = n; + } + + run_test (probe_name); + + free (probe_name); + resolv_test_end (aux); + } + + free_response_data (); + return 0; +} + +#include diff --git a/resolv/tst-resolv-qtypes.c b/resolv/tst-resolv-qtypes.c index dcb39e505e..da3325f80c 100644 --- a/resolv/tst-resolv-qtypes.c +++ b/resolv/tst-resolv-qtypes.c @@ -50,7 +50,7 @@ response (const struct resolv_response_context *ctx, resolv_response_close_record (b); } -static const const char *domain = "www.example.com"; +static const char domain[] = "www.example.com"; static int wrap_res_query (int type, unsigned char *answer, int answer_length) diff --git a/scripts/backport-support.sh b/scripts/backport-support.sh new file mode 100644 index 0000000000..2ece7ce575 --- /dev/null +++ b/scripts/backport-support.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Create a patch which backports the support/ subdirectory. +# Copyright (C) 2017 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 +# . + +# This script does not backport the Makefile tweaks outside the +# support/ directory (which need to be backported separately), or the +# changes to test-skeleton.c (which should not be backported). + +set -e + +export LC_ALL=C +export GIT_CONFIG=/dev/null +export GTT_CONFIG_NOSYSTEM=0 +export GIT_PAGER= + +usage () { + cat >&2 <&2 + echo "# rm -rf $patch_targets" >&2 +} + +command_commit () { + git status --porcelain | while read line ; do + echo "error: working copy is not clean, cannot commit" >&2 + exit 1 + done + for path in $patch_targets; do + echo "# Processing $path" >&2 + case "$path" in + [a-zA-Z0-9]*/) + # Directory. + git rm --cached --ignore-unmatch -r "$path" + rm -rf "$path" + git read-tree --prefix="$path" "$latest_commit":"$path" + git checkout "$path" + ;; + *) + # File. + git show "$latest_commit":"$path" > "$path" + git add "$path" + esac + done + git commit -m "Synchronize support/ infrastructure with $branch_name + +This commit updates the support/ subdirectory to +commit $latest_commit +on the $branch_name branch. +" +} + +command_$command diff --git a/stdlib/getentropy.c b/stdlib/getentropy.c index a71d4cd8f5..a88bbf8de3 100644 --- a/stdlib/getentropy.c +++ b/stdlib/getentropy.c @@ -21,7 +21,7 @@ /* Write LENGTH bytes of randomness starting at BUFFER. Return 0 on success and -1 on failure. */ -ssize_t +int getentropy (void *buffer, size_t length) { __set_errno (ENOSYS); diff --git a/string/stratcliff.c b/string/stratcliff.c index e672644888..2cd8686082 100644 --- a/string/stratcliff.c +++ b/string/stratcliff.c @@ -58,8 +58,8 @@ static int do_test (void) { - int size = sysconf (_SC_PAGESIZE); - int nchars = size / sizeof (CHAR); + size_t size = sysconf (_SC_PAGESIZE); + size_t nchars = size / sizeof (CHAR); CHAR *adr; CHAR *dest; int result = 0; @@ -80,7 +80,17 @@ do_test (void) } else { - int inner, middle, outer; + size_t inner, middle, outer, nchars64, max128; + + if (nchars > 64) + nchars64 = nchars - 64; + else + nchars64 = 0; + + if (nchars > 128) + max128 = nchars - 128; + else + max128 = 0; mprotect (adr, size, PROT_NONE); mprotect (adr + 2 * nchars, size, PROT_NONE); @@ -93,59 +103,65 @@ do_test (void) MEMSET (adr, L('T'), nchars); /* strlen/wcslen test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner < nchars; ++inner) + for (inner = MAX (outer, nchars64); inner < nchars; ++inner) { adr[inner] = L('\0'); if (STRLEN (&adr[outer]) != (size_t) (inner - outer)) { - printf ("%s flunked for outer = %d, inner = %d\n", + printf ("%s flunked for outer = %zu, inner = %zu\n", STRINGIFY (STRLEN), outer, inner); result = 1; } adr[inner] = L('T'); } + if (outer == 0) + break; } /* strnlen/wcsnlen test */ - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner < nchars; ++inner) + for (inner = MAX (outer, nchars64); inner < nchars; ++inner) { adr[inner] = L('\0'); if (STRNLEN (&adr[outer], inner - outer + 1) != (size_t) (inner - outer)) { - printf ("%s flunked for outer = %d, inner = %d\n", + printf ("%s flunked for outer = %zu, inner = %zu\n", STRINGIFY (STRNLEN), outer, inner); result = 1; } adr[inner] = L('T'); } + if (outer == 0) + break; } - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner <= nchars; ++inner) + for (inner = MAX (outer, nchars64); inner <= nchars; ++inner) { if (STRNLEN (&adr[outer], inner - outer) != (size_t) (inner - outer)) { - printf ("%s flunked bounded for outer = %d, inner = %d\n", + printf ("%s flunked bounded for outer = %zu, inner = %zu\n", STRINGIFY (STRNLEN), outer, inner); result = 1; } } + if (outer == 0) + break; } /* strchr/wcschr test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { for (inner = middle; inner < nchars; ++inner) { @@ -158,8 +174,8 @@ do_test (void) || (inner != middle && (cp - &adr[outer]) != middle - outer)) { - printf ("%s flunked for outer = %d, middle = %d, " - "inner = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu, " + "inner = %zu\n", STRINGIFY (STRCHR), outer, middle, inner); result = 1; } @@ -168,6 +184,8 @@ do_test (void) adr[middle] = L('T'); } } + if (outer == 0) + break; } /* Special test. */ @@ -180,9 +198,9 @@ do_test (void) } /* strrchr/wcsrchr test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { for (inner = middle; inner < nchars; ++inner) { @@ -195,8 +213,8 @@ do_test (void) || (inner != middle && (cp - &adr[outer]) != middle - outer)) { - printf ("%s flunked for outer = %d, middle = %d, " - "inner = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu, " + "inner = %zu\n", STRINGIFY (STRRCHR), outer, middle, inner); result = 1; } @@ -205,12 +223,14 @@ do_test (void) adr[middle] = L('T'); } } + if (outer == 0) + break; } /* memchr test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { adr[middle] = L('V'); @@ -218,32 +238,36 @@ do_test (void) if (cp - &adr[outer] != middle - outer) { - printf ("%s flunked for outer = %d, middle = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu\n", STRINGIFY (MEMCHR), outer, middle); result = 1; } adr[middle] = L('T'); } + if (outer == 0) + break; } - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { CHAR *cp = MEMCHR (&adr[outer], L('V'), nchars - outer); if (cp != NULL) { - printf ("%s flunked for outer = %d\n", + printf ("%s flunked for outer = %zu\n", STRINGIFY (MEMCHR), outer); result = 1; } + if (outer == 0) + break; } /* These functions only exist for single-byte characters. */ #ifndef WCSTEST /* rawmemchr test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { adr[middle] = L('V'); @@ -251,19 +275,21 @@ do_test (void) if (cp - &adr[outer] != middle - outer) { - printf ("%s flunked for outer = %d, middle = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu\n", STRINGIFY (rawmemchr), outer, middle); result = 1; } adr[middle] = L('T'); } + if (outer == 0) + break; } /* memrchr test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { adr[middle] = L('V'); @@ -271,44 +297,50 @@ do_test (void) if (cp - &adr[outer] != middle - outer) { - printf ("%s flunked for outer = %d, middle = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu\n", STRINGIFY (memrchr), outer, middle); result = 1; } adr[middle] = L('T'); } + if (outer == 0) + break; } - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { CHAR *cp = memrchr (&adr[outer], L('V'), nchars - outer); if (cp != NULL) { - printf ("%s flunked for outer = %d\n", + printf ("%s flunked for outer = %zu\n", STRINGIFY (memrchr), outer); result = 1; } + if (outer == 0) + break; } #endif /* strcpy/wcscpy test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner < nchars; ++inner) + for (inner = MAX (outer, nchars64); inner < nchars; ++inner) { adr[inner] = L('\0'); if (STRCPY (dest, &adr[outer]) != dest || STRLEN (dest) != (size_t) (inner - outer)) { - printf ("%s flunked for outer = %d, inner = %d\n", + printf ("%s flunked for outer = %zu, inner = %zu\n", STRINGIFY (STRCPY), outer, inner); result = 1; } adr[inner] = L('T'); } + if (outer == 0) + break; } /* strcmp/wcscmp tests */ @@ -322,14 +354,14 @@ do_test (void) if (STRCMP (adr + middle, dest + nchars - outer) <= 0) { - printf ("%s 1 flunked for outer = %d, middle = %d\n", + printf ("%s 1 flunked for outer = %zu, middle = %zu\n", STRINGIFY (STRCMP), outer, middle); result = 1; } if (STRCMP (dest + nchars - outer, adr + middle) >= 0) { - printf ("%s 2 flunked for outer = %d, middle = %d\n", + printf ("%s 2 flunked for outer = %zu, middle = %zu\n", STRINGIFY (STRCMP), outer, middle); result = 1; } @@ -348,16 +380,16 @@ do_test (void) { if (STRNCMP (adr + middle, dest + nchars - outer, inner) != 0) { - printf ("%s 1 flunked for outer = %d, middle = %d, " - "inner = %d\n", + printf ("%s 1 flunked for outer = %zu, middle = %zu, " + "inner = %zu\n", STRINGIFY (STRNCMP), outer, middle, inner); result = 1; } if (STRNCMP (dest + nchars - outer, adr + middle, inner) != 0) { - printf ("%s 2 flunked for outer = %d, middle = %d, " - "inner = %d\n", + printf ("%s 2 flunked for outer = %zu, middle = %zu, " + "inner = %zu\n", STRINGIFY (STRNCMP), outer, middle, inner); result = 1; } @@ -365,14 +397,14 @@ do_test (void) if (STRNCMP (adr + middle, dest + nchars - outer, outer) >= 0) { - printf ("%s 1 flunked for outer = %d, middle = %d, full\n", + printf ("%s 1 flunked for outer = %zu, middle = %zu, full\n", STRINGIFY (STRNCMP), outer, middle); result = 1; } if (STRNCMP (dest + nchars - outer, adr + middle, outer) <= 0) { - printf ("%s 2 flunked for outer = %d, middle = %d, full\n", + printf ("%s 2 flunked for outer = %zu, middle = %zu, full\n", STRINGIFY (STRNCMP), outer, middle); result = 1; } @@ -380,7 +412,7 @@ do_test (void) /* strncpy/wcsncpy tests */ adr[nchars - 1] = L('T'); - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { size_t len; @@ -389,17 +421,19 @@ do_test (void) if (STRNCPY (dest, &adr[outer], len) != dest || MEMCMP (dest, &adr[outer], len) != 0) { - printf ("outer %s flunked for outer = %d, len = %Zd\n", + printf ("outer %s flunked for outer = %zu, len = %zu\n", STRINGIFY (STRNCPY), outer, len); result = 1; } } + if (outer == 0) + break; } adr[nchars - 1] = L('\0'); - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner < nchars; ++inner) + for (inner = MAX (outer, nchars64); inner < nchars; ++inner) { size_t len; @@ -413,8 +447,8 @@ do_test (void) || (inner - outer < len && STRLEN (dest) != (inner - outer))) { - printf ("%s flunked for outer = %d, inner = %d, " - "len = %Zd\n", + printf ("%s flunked for outer = %zu, inner = %zu, " + "len = %zu\n", STRINGIFY (STRNCPY), outer, inner, len); result = 1; } @@ -424,8 +458,8 @@ do_test (void) || (inner - outer < len && STRLEN (dest + 1) != (inner - outer))) { - printf ("%s+1 flunked for outer = %d, inner = %d, " - "len = %Zd\n", + printf ("%s+1 flunked for outer = %zu, inner = %zu, " + "len = %zu\n", STRINGIFY (STRNCPY), outer, inner, len); result = 1; } @@ -433,29 +467,33 @@ do_test (void) adr[inner] = L('T'); } + if (outer == 0) + break; } /* stpcpy/wcpcpy test */ - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (inner = MAX (outer, nchars - 64); inner < nchars; ++inner) + for (inner = MAX (outer, nchars64); inner < nchars; ++inner) { adr[inner] = L('\0'); if ((STPCPY (dest, &adr[outer]) - dest) != inner - outer) { - printf ("%s flunked for outer = %d, inner = %d\n", + printf ("%s flunked for outer = %zu, inner = %zu\n", STRINGIFY (STPCPY), outer, inner); result = 1; } adr[inner] = L('T'); } + if (outer == 0) + break; } /* stpncpy/wcpncpy test */ adr[nchars - 1] = L('T'); - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars; outer >= max128; --outer) { size_t len; @@ -464,17 +502,19 @@ do_test (void) if (STPNCPY (dest, &adr[outer], len) != dest + len || MEMCMP (dest, &adr[outer], len) != 0) { - printf ("outer %s flunked for outer = %d, len = %Zd\n", + printf ("outer %s flunked for outer = %zu, len = %zu\n", STRINGIFY (STPNCPY), outer, len); result = 1; } } + if (outer == 0) + break; } adr[nchars - 1] = L('\0'); - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) + for (outer = nchars - 1; outer >= max128; --outer) { - for (middle = MAX (outer, nchars - 64); middle < nchars; ++middle) + for (middle = MAX (outer, nchars64); middle < nchars; ++middle) { adr[middle] = L('\0'); @@ -483,8 +523,8 @@ do_test (void) if ((STPNCPY (dest, &adr[outer], inner) - dest) != MIN (inner, middle - outer)) { - printf ("%s flunked for outer = %d, middle = %d, " - "inner = %d\n", + printf ("%s flunked for outer = %zu, middle = %zu, " + "inner = %zu\n", STRINGIFY (STPNCPY), outer, middle, inner); result = 1; } @@ -492,66 +532,84 @@ do_test (void) adr[middle] = L('T'); } + if (outer == 0) + break; } /* memcpy/wmemcpy test */ - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) - for (inner = 0; inner < nchars - outer; ++inner) - if (MEMCPY (dest, &adr[outer], inner) != dest) - { - printf ("%s flunked for outer = %d, inner = %d\n", - STRINGIFY (MEMCPY), outer, inner); - result = 1; - } + for (outer = nchars; outer >= max128; --outer) + { + for (inner = 0; inner < nchars - outer; ++inner) + if (MEMCPY (dest, &adr[outer], inner) != dest) + { + printf ("%s flunked for outer = %zu, inner = %zu\n", + STRINGIFY (MEMCPY), outer, inner); + result = 1; + } + if (outer == 0) + break; + } /* mempcpy/wmempcpy test */ - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) - for (inner = 0; inner < nchars - outer; ++inner) - if (MEMPCPY (dest, &adr[outer], inner) != dest + inner) - { - printf ("%s flunked for outer = %d, inner = %d\n", - STRINGIFY (MEMPCPY), outer, inner); - result = 1; - } + for (outer = nchars; outer >= max128; --outer) + { + for (inner = 0; inner < nchars - outer; ++inner) + if (MEMPCPY (dest, &adr[outer], inner) != dest + inner) + { + printf ("%s flunked for outer = %zu, inner = %zu\n", + STRINGIFY (MEMPCPY), outer, inner); + result = 1; + } + if (outer == 0) + break; + } /* This function only exists for single-byte characters. */ #ifndef WCSTEST /* memccpy test */ memset (adr, '\0', nchars); - for (outer = nchars; outer >= MAX (0, nchars - 128); --outer) - for (inner = 0; inner < nchars - outer; ++inner) - if (memccpy (dest, &adr[outer], L('\1'), inner) != NULL) - { - printf ("memccpy flunked full copy for outer = %d, inner = %d\n", - outer, inner); - result = 1; - } - for (outer = nchars - 1; outer >= MAX (0, nchars - 128); --outer) - for (middle = 0; middle < nchars - outer; ++middle) - { - memset (dest, L('\2'), middle + 1); - for (inner = 0; inner < middle; ++inner) + for (outer = nchars; outer >= max128; --outer) + { + for (inner = 0; inner < nchars - outer; ++inner) + if (memccpy (dest, &adr[outer], L('\1'), inner) != NULL) { - adr[outer + inner] = L('\1'); - - if (memccpy (dest, &adr[outer], '\1', middle + 128) - != dest + inner + 1) - { - printf ("\ -memccpy flunked partial copy for outer = %d, middle = %d, inner = %d\n", - outer, middle, inner); - result = 1; - } - else if (dest[inner + 1] != L('\2')) - { - printf ("\ -memccpy copied too much for outer = %d, middle = %d, inner = %d\n", - outer, middle, inner); - result = 1; - } - adr[outer + inner] = L('\0'); + printf ("memccpy flunked full copy for outer = %zu, inner = %zu\n", + outer, inner); + result = 1; } - } + if (outer == 0) + break; + } + for (outer = nchars - 1; outer >= max128; --outer) + { + for (middle = 0; middle < nchars - outer; ++middle) + { + memset (dest, L('\2'), middle + 1); + for (inner = 0; inner < middle; ++inner) + { + adr[outer + inner] = L('\1'); + + if (memccpy (dest, &adr[outer], '\1', middle + 128) + != dest + inner + 1) + { + printf ("\ + memccpy flunked partial copy for outer = %zu, middle = %zu, inner = %zu\n", + outer, middle, inner); + result = 1; + } + else if (dest[inner + 1] != L('\2')) + { + printf ("\ + memccpy copied too much for outer = %zu, middle = %zu, inner = %zu\n", + outer, middle, inner); + result = 1; + } + adr[outer + inner] = L('\0'); + } + } + if (outer == 0) + break; + } #endif } diff --git a/string/test-memchr.c b/string/test-memchr.c index d62889ff8f..6431605c7e 100644 --- a/string/test-memchr.c +++ b/string/test-memchr.c @@ -208,6 +208,12 @@ test_main (void) do_test (0, i, i + 1, i + 1, 0); } + /* BZ#21182 - wrong overflow calculation for i686 implementation + with address near end of the page. */ + for (i = 2; i < 16; ++i) + /* page_size is in fact getpagesize() * 2. */ + do_test (page_size / 2 - i, i, i, 1, 0x9B); + do_random_tests (); return ret; } diff --git a/sunrpc/Makefile b/sunrpc/Makefile index 0c1e6124ff..7e5d2955a0 100644 --- a/sunrpc/Makefile +++ b/sunrpc/Makefile @@ -93,11 +93,12 @@ rpcgen-objs = rpc_main.o rpc_hout.o rpc_cout.o rpc_parse.o \ extra-objs = $(rpcgen-objs) $(addprefix cross-,$(rpcgen-objs)) others += rpcgen -tests = tst-xdrmem tst-xdrmem2 test-rpcent +tests = tst-xdrmem tst-xdrmem2 test-rpcent tst-udp-error tst-udp-timeout \ + tst-udp-nonblocking xtests := tst-getmyaddr ifeq ($(have-thread-library),yes) -xtests += thrsvc +xtests += thrsvc tst-udp-garbage endif ifeq ($(run-built-tests),yes) @@ -155,6 +156,7 @@ BUILD_CPPFLAGS += $(sunrpc-CPPFLAGS) $(objpfx)tst-getmyaddr: $(common-objpfx)linkobj/libc.so $(objpfx)tst-xdrmem: $(common-objpfx)linkobj/libc.so $(objpfx)tst-xdrmem2: $(common-objpfx)linkobj/libc.so +$(objpfx)tst-udp-error: $(common-objpfx)linkobj/libc.so $(objpfx)rpcgen: $(addprefix $(objpfx),$(rpcgen-objs)) @@ -234,3 +236,8 @@ $(rpcgen-tests): $(objpfx)%.out: %.x $(objpfx)rpcgen $(built-program-cmd) -c $< -o $@; \ $(evaluate-test) endif + +$(objpfx)tst-udp-timeout: $(common-objpfx)linkobj/libc.so +$(objpfx)tst-udp-nonblocking: $(common-objpfx)linkobj/libc.so +$(objpfx)tst-udp-garbage: \ + $(common-objpfx)linkobj/libc.so $(shared-thread-library) diff --git a/sunrpc/clnt_udp.c b/sunrpc/clnt_udp.c index 4d9acb1e6a..6ce16eb298 100644 --- a/sunrpc/clnt_udp.c +++ b/sunrpc/clnt_udp.c @@ -55,6 +55,7 @@ #endif #include +#include extern u_long _create_xid (void); @@ -80,7 +81,9 @@ static const struct clnt_ops udp_ops = }; /* - * Private data kept per client handle + * Private data kept per client handle. This private struct is + * unfortunately part of the ABI; ypbind contains a copy of it and + * accesses it through CLIENT::cl_private field. */ struct cu_data { @@ -278,28 +281,38 @@ clntudp_call (/* client handle */ int inlen; socklen_t fromlen; struct pollfd fd; - int milliseconds = (cu->cu_wait.tv_sec * 1000) + - (cu->cu_wait.tv_usec / 1000); struct sockaddr_in from; struct rpc_msg reply_msg; XDR reply_xdrs; - struct timeval time_waited; bool_t ok; int nrefreshes = 2; /* number of times to refresh cred */ - struct timeval timeout; int anyup; /* any network interface up */ - if (cu->cu_total.tv_usec == -1) - { - timeout = utimeout; /* use supplied timeout */ - } - else + struct deadline_current_time current_time = __deadline_current_time (); + struct deadline total_deadline; /* Determined once by overall timeout. */ + struct deadline response_deadline; /* Determined anew for each query. */ + + /* Choose the timeout value. For non-sending usage (xargs == NULL), + the total deadline does not matter, only cu->cu_wait is used + below. */ + if (xargs != NULL) { - timeout = cu->cu_total; /* use default timeout */ + struct timeval tv; + if (cu->cu_total.tv_usec == -1) + /* Use supplied timeout. */ + tv = utimeout; + else + /* Use default timeout. */ + tv = cu->cu_total; + if (!__is_timeval_valid_timeout (tv)) + return (cu->cu_error.re_status = RPC_TIMEDOUT); + total_deadline = __deadline_from_timeval (current_time, tv); } - time_waited.tv_sec = 0; - time_waited.tv_usec = 0; + /* Guard against bad timeout specification. */ + if (!__is_timeval_valid_timeout (cu->cu_wait)) + return (cu->cu_error.re_status = RPC_TIMEDOUT); + call_again: xdrs = &(cu->cu_outxdrs); if (xargs == NULL) @@ -325,27 +338,46 @@ send_again: return (cu->cu_error.re_status = RPC_CANTSEND); } - /* - * Hack to provide rpc-based message passing - */ - if (timeout.tv_sec == 0 && timeout.tv_usec == 0) - { - return (cu->cu_error.re_status = RPC_TIMEDOUT); - } + /* sendto may have blocked, so recompute the current time. */ + current_time = __deadline_current_time (); get_reply: - /* - * sub-optimal code appears here because we have - * some clock time to spare while the packets are in flight. - * (We assume that this is actually only executed once.) - */ + response_deadline = __deadline_from_timeval (current_time, cu->cu_wait); + reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = resultsp; reply_msg.acpted_rply.ar_results.proc = xresults; fd.fd = cu->cu_sock; fd.events = POLLIN; anyup = 0; + + /* Per-response retry loop. current_time must be up-to-date at the + top of the loop. */ for (;;) { + int milliseconds; + if (xargs != NULL) + { + if (__deadline_elapsed (current_time, total_deadline)) + /* Overall timeout expired. */ + return (cu->cu_error.re_status = RPC_TIMEDOUT); + milliseconds = __deadline_to_ms + (current_time, __deadline_first (total_deadline, + response_deadline)); + if (milliseconds == 0) + /* Per-query timeout expired. */ + goto send_again; + } + else + { + /* xatgs == NULL. Collect a response without sending a + query. In this mode, we need to ignore the total + deadline. */ + milliseconds = __deadline_to_ms (current_time, response_deadline); + if (milliseconds == 0) + /* Cannot send again, so bail out. */ + return (cu->cu_error.re_status = RPC_CANTSEND); + } + switch (__poll (&fd, 1, milliseconds)) { @@ -356,27 +388,10 @@ send_again: if (!anyup) return (cu->cu_error.re_status = RPC_CANTRECV); } - - time_waited.tv_sec += cu->cu_wait.tv_sec; - time_waited.tv_usec += cu->cu_wait.tv_usec; - while (time_waited.tv_usec >= 1000000) - { - time_waited.tv_sec++; - time_waited.tv_usec -= 1000000; - } - if ((time_waited.tv_sec < timeout.tv_sec) || - ((time_waited.tv_sec == timeout.tv_sec) && - (time_waited.tv_usec < timeout.tv_usec))) - goto send_again; - return (cu->cu_error.re_status = RPC_TIMEDOUT); - - /* - * buggy in other cases because time_waited is not being - * updated. - */ + goto next_response; case -1: if (errno == EINTR) - continue; + goto next_response; cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTRECV); } @@ -421,9 +436,9 @@ send_again: cmsg = CMSG_NXTHDR (&msg, cmsg)) if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { - free (cbuf); e = (struct sock_extended_err *) CMSG_DATA(cmsg); cu->cu_error.re_errno = e->ee_errno; + free (cbuf); return (cu->cu_error.re_status = RPC_CANTRECV); } free (cbuf); @@ -440,20 +455,22 @@ send_again: if (inlen < 0) { if (errno == EWOULDBLOCK) - continue; + goto next_response; cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTRECV); } - if (inlen < 4) - continue; - - /* see if reply transaction id matches sent id. - Don't do this if we only wait for a replay */ - if (xargs != NULL - && memcmp (cu->cu_inbuf, cu->cu_outbuf, sizeof (u_int32_t)) != 0) - continue; - /* we now assume we have the proper reply */ - break; + /* Accept the response if the packet is sufficiently long and + the transaction ID matches the query (if available). */ + if (inlen >= 4 + && (xargs == NULL + || memcmp (cu->cu_inbuf, cu->cu_outbuf, + sizeof (u_int32_t)) == 0)) + break; + + next_response: + /* Update the current time because poll and recvmsg waited for + an unknown time. */ + current_time = __deadline_current_time (); } /* diff --git a/sunrpc/tst-udp-error.c b/sunrpc/tst-udp-error.c new file mode 100644 index 0000000000..1efc02f5c6 --- /dev/null +++ b/sunrpc/tst-udp-error.c @@ -0,0 +1,62 @@ +/* Check for use-after-free in clntudp_call (bug 21115). + Copyright (C) 2017 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 + +static int +do_test (void) +{ + support_become_root (); + support_enter_network_namespace (); + + /* Obtain a likely-unused port number. */ + struct sockaddr_in sin = + { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (INADDR_LOOPBACK), + }; + { + int fd = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + xbind (fd, (struct sockaddr *) &sin, sizeof (sin)); + socklen_t sinlen = sizeof (sin); + xgetsockname (fd, (struct sockaddr *) &sin, &sinlen); + /* Close the socket, so that we will receive an error below. */ + close (fd); + } + + int sock = RPC_ANYSOCK; + CLIENT *clnt = clntudp_create + (&sin, 1, 2, (struct timeval) { 1, 0 }, &sock); + TEST_VERIFY_EXIT (clnt != NULL); + TEST_VERIFY (clnt_call (clnt, 3, + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_void, NULL, + ((struct timeval) { 3, 0 })) + == RPC_CANTRECV); + clnt_destroy (clnt); + + return 0; +} + +#include diff --git a/sunrpc/tst-udp-garbage.c b/sunrpc/tst-udp-garbage.c new file mode 100644 index 0000000000..4abda93f08 --- /dev/null +++ b/sunrpc/tst-udp-garbage.c @@ -0,0 +1,104 @@ +/* Test that garbage packets do not affect timeout handling. + Copyright (C) 2017 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 + +/* Descriptor for the server UDP socket. */ +static int server_fd; + +static void * +garbage_sender_thread (void *unused) +{ + while (true) + { + struct sockaddr_storage sa; + socklen_t salen = sizeof (sa); + char buf[1]; + if (recvfrom (server_fd, buf, sizeof (buf), 0, + (struct sockaddr *) &sa, &salen) < 0) + FAIL_EXIT1 ("recvfrom: %m"); + + /* Send garbage packets indefinitely. */ + buf[0] = 0; + while (true) + { + /* sendto can fail if the client closed the socket. */ + if (sendto (server_fd, buf, sizeof (buf), 0, + (struct sockaddr *) &sa, salen) < 0) + break; + + /* Wait a bit, to avoid burning too many CPU cycles in a + tight loop. The wait period must be much shorter than + the client timeouts configured below. */ + usleep (50 * 1000); + } + } +} + +static int +do_test (void) +{ + support_become_root (); + support_enter_network_namespace (); + + server_fd = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); + struct sockaddr_in server_address = + { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (INADDR_LOOPBACK), + }; + xbind (server_fd, + (struct sockaddr *) &server_address, sizeof (server_address)); + { + socklen_t sinlen = sizeof (server_address); + xgetsockname (server_fd, (struct sockaddr *) &server_address, &sinlen); + TEST_VERIFY (sizeof (server_address) == sinlen); + } + + /* Garbage packet source. */ + xpthread_detach (xpthread_create (NULL, garbage_sender_thread, NULL)); + + /* Test client. Use an arbitrary timeout of one second, which is + much longer than the garbage packet interval, but still + reasonably short, so that the test completes quickly. */ + int client_fd = RPC_ANYSOCK; + CLIENT *clnt = clntudp_create (&server_address, + 1, 2, /* Arbitrary RPC endpoint numbers. */ + (struct timeval) { 1, 0 }, + &client_fd); + if (clnt == NULL) + FAIL_EXIT1 ("clntudp_create: %m"); + + TEST_VERIFY (clnt_call (clnt, 3, /* Arbitrary RPC procedure number. */ + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_void, NULL, + ((struct timeval) { 1, 0 }))); + + return 0; +} + +#include diff --git a/sunrpc/tst-udp-nonblocking.c b/sunrpc/tst-udp-nonblocking.c new file mode 100644 index 0000000000..1d6a7f4b56 --- /dev/null +++ b/sunrpc/tst-udp-nonblocking.c @@ -0,0 +1,333 @@ +/* Test non-blocking use of the UDP client. + Copyright (C) 2017 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 +#include +#include + +/* Test data serialization and deserialization. */ + +struct test_query +{ + uint32_t a; + uint32_t b; + uint32_t timeout_ms; +}; + +static bool_t +xdr_test_query (XDR *xdrs, void *data, ...) +{ + struct test_query *p = data; + return xdr_uint32_t (xdrs, &p->a) + && xdr_uint32_t (xdrs, &p->b) + && xdr_uint32_t (xdrs, &p->timeout_ms); +} + +struct test_response +{ + uint32_t server_id; + uint32_t seq; + uint32_t sum; +}; + +static bool_t +xdr_test_response (XDR *xdrs, void *data, ...) +{ + struct test_response *p = data; + return xdr_uint32_t (xdrs, &p->server_id) + && xdr_uint32_t (xdrs, &p->seq) + && xdr_uint32_t (xdrs, &p->sum); +} + +/* Implementation of the test server. */ + +enum + { + /* Number of test servers to run. */ + SERVER_COUNT = 3, + + /* RPC parameters, chosen at random. */ + PROGNUM = 8242, + VERSNUM = 19654, + + /* Main RPC operation. */ + PROC_ADD = 1, + + /* Request process termination. */ + PROC_EXIT, + + /* Special exit status to mark successful processing. */ + EXIT_MARKER = 55, + }; + +/* Set by the parent process to tell test servers apart. */ +static int server_id; + +/* Implementation of the test server. */ +static void +server_dispatch (struct svc_req *request, SVCXPRT *transport) +{ + /* Query sequence number. */ + static uint32_t seq = 0; + ++seq; + static bool proc_add_seen; + + if (test_verbose) + printf ("info: server_dispatch server_id=%d seq=%u rq_proc=%lu\n", + server_id, seq, request->rq_proc); + + switch (request->rq_proc) + { + case PROC_ADD: + { + struct test_query query; + memset (&query, 0xc0, sizeof (query)); + TEST_VERIFY_EXIT + (svc_getargs (transport, xdr_test_query, + (void *) &query)); + + if (test_verbose) + printf (" a=%u b=%u timeout_ms=%u\n", + query.a, query.b, query.timeout_ms); + + usleep (query.timeout_ms * 1000); + + struct test_response response = + { + .server_id = server_id, + .seq = seq, + .sum = query.a + query.b, + }; + TEST_VERIFY (svc_sendreply (transport, xdr_test_response, + (void *) &response)); + if (test_verbose) + printf (" server id %d response seq=%u sent\n", server_id, seq); + proc_add_seen = true; + } + break; + + case PROC_EXIT: + TEST_VERIFY (proc_add_seen); + TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL)); + _exit (EXIT_MARKER); + break; + + default: + FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc); + break; + } +} + +/* Return the number seconds since an arbitrary point in time. */ +static double +get_ticks (void) +{ + { + struct timespec ts; + if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec + ts.tv_nsec * 1e-9; + } + { + struct timeval tv; + TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0); + return tv.tv_sec + tv.tv_usec * 1e-6; + } +} + +static int +do_test (void) +{ + support_become_root (); + support_enter_network_namespace (); + + /* Information about the test servers. */ + struct + { + SVCXPRT *transport; + struct sockaddr_in address; + pid_t pid; + uint32_t xid; + } servers[SERVER_COUNT]; + + /* Spawn the test servers. */ + for (int i = 0; i < SERVER_COUNT; ++i) + { + servers[i].transport = svcudp_create (RPC_ANYSOCK); + TEST_VERIFY_EXIT (servers[i].transport != NULL); + servers[i].address = (struct sockaddr_in) + { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (INADDR_LOOPBACK), + .sin_port = htons (servers[i].transport->xp_port), + }; + servers[i].xid = 0xabcd0101 + i; + if (test_verbose) + printf ("info: setting up server %d xid=%x on port %d\n", + i, servers[i].xid, servers[i].transport->xp_port); + + server_id = i; + servers[i].pid = xfork (); + if (servers[i].pid == 0) + { + TEST_VERIFY (svc_register (servers[i].transport, + PROGNUM, VERSNUM, server_dispatch, 0)); + svc_run (); + FAIL_EXIT1 ("supposed to be unreachable"); + } + /* We need to close the socket so that we do not accidentally + consume the request. */ + TEST_VERIFY (close (servers[i].transport->xp_sock) == 0); + } + + + /* The following code mirrors what ypbind does. */ + + /* Copied from clnt_udp.c (like ypbind). */ + struct cu_data + { + int cu_sock; + bool_t cu_closeit; + struct sockaddr_in cu_raddr; + int cu_rlen; + struct timeval cu_wait; + struct timeval cu_total; + struct rpc_err cu_error; + XDR cu_outxdrs; + u_int cu_xdrpos; + u_int cu_sendsz; + char *cu_outbuf; + u_int cu_recvsz; + char cu_inbuf[1]; + }; + + int client_socket = xsocket (AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + CLIENT *clnt = clntudp_create (&servers[0].address, PROGNUM, VERSNUM, + /* 5 seconds per-response timeout. */ + ((struct timeval) { 5, 0 }), + &client_socket); + TEST_VERIFY (clnt != NULL); + clnt->cl_auth = authunix_create_default (); + { + struct timeval zero = { 0, 0 }; + TEST_VERIFY (clnt_control (clnt, CLSET_TIMEOUT, (void *) &zero)); + } + + /* Poke at internal data structures (like ypbind). */ + struct cu_data *cu = (struct cu_data *) clnt->cl_private; + + /* Send a ping to each server. */ + double before_pings = get_ticks (); + for (int i = 0; i < SERVER_COUNT; ++i) + { + if (test_verbose) + printf ("info: sending server %d ping\n", i); + /* Reset the xid because it is changed by each invocation of + clnt_call. Subtract one to compensate for the xid update + during the call. */ + *((u_int32_t *) (cu->cu_outbuf)) = servers[i].xid - 1; + cu->cu_raddr = servers[i].address; + + struct test_query query = { .a = 100, .b = i + 1 }; + if (i == 1) + /* Shorter timeout to prefer this server. These timeouts must + be much shorter than the 5-second per-response timeout + configured with clntudp_create. */ + query.timeout_ms = 700; + else + query.timeout_ms = 1400; + struct test_response response = { 0 }; + /* NB: Do not check the return value. The server reply will + prove that the call worked. */ + double before_one_ping = get_ticks (); + clnt_call (clnt, PROC_ADD, + xdr_test_query, (void *) &query, + xdr_test_response, (void *) &response, + ((struct timeval) { 0, 0 })); + double after_one_ping = get_ticks (); + if (test_verbose) + printf ("info: non-blocking send took %f seconds\n", + after_one_ping - before_one_ping); + /* clnt_call should return immediately. Accept some delay in + case the process is descheduled. */ + TEST_VERIFY (after_one_ping - before_one_ping < 0.3); + } + + /* Collect the non-blocking response. */ + if (test_verbose) + printf ("info: collecting response\n"); + struct test_response response = { 0 }; + TEST_VERIFY + (clnt_call (clnt, PROC_ADD, NULL, NULL, + xdr_test_response, (void *) &response, + ((struct timeval) { 0, 0 })) == RPC_SUCCESS); + double after_pings = get_ticks (); + if (test_verbose) + printf ("info: send/receive took %f seconds\n", + after_pings - before_pings); + /* Expected timeout is 0.7 seconds. */ + TEST_VERIFY (0.7 <= after_pings - before_pings); + TEST_VERIFY (after_pings - before_pings < 1.2); + + uint32_t xid; + memcpy (&xid, &cu->cu_inbuf, sizeof (xid)); + if (test_verbose) + printf ("info: non-blocking response: xid=%x server_id=%u seq=%u sum=%u\n", + xid, response.server_id, response.seq, response.sum); + /* Check that the reply from the preferred server was used. */ + TEST_VERIFY (servers[1].xid == xid); + TEST_VERIFY (response.server_id == 1); + TEST_VERIFY (response.seq == 1); + TEST_VERIFY (response.sum == 102); + + auth_destroy (clnt->cl_auth); + clnt_destroy (clnt); + + for (int i = 0; i < SERVER_COUNT; ++i) + { + if (test_verbose) + printf ("info: requesting server %d termination\n", i); + client_socket = RPC_ANYSOCK; + clnt = clntudp_create (&servers[i].address, PROGNUM, VERSNUM, + ((struct timeval) { 5, 0 }), + &client_socket); + TEST_VERIFY_EXIT (clnt != NULL); + TEST_VERIFY (clnt_call (clnt, PROC_EXIT, + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_void, NULL, + ((struct timeval) { 3, 0 })) == RPC_SUCCESS); + clnt_destroy (clnt); + + int status; + xwaitpid (servers[i].pid, &status, 0); + TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER); + } + + return 0; +} + +#include diff --git a/sunrpc/tst-udp-timeout.c b/sunrpc/tst-udp-timeout.c new file mode 100644 index 0000000000..db9943a03e --- /dev/null +++ b/sunrpc/tst-udp-timeout.c @@ -0,0 +1,402 @@ +/* Test timeout handling in the UDP client. + Copyright (C) 2017 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 +#include +#include + +/* Test data serialization and deserialization. */ + +struct test_query +{ + uint32_t a; + uint32_t b; + uint32_t timeout_ms; + uint32_t wait_for_seq; + uint32_t garbage_packets; +}; + +static bool_t +xdr_test_query (XDR *xdrs, void *data, ...) +{ + struct test_query *p = data; + return xdr_uint32_t (xdrs, &p->a) + && xdr_uint32_t (xdrs, &p->b) + && xdr_uint32_t (xdrs, &p->timeout_ms) + && xdr_uint32_t (xdrs, &p->wait_for_seq) + && xdr_uint32_t (xdrs, &p->garbage_packets); +} + +struct test_response +{ + uint32_t seq; + uint32_t sum; +}; + +static bool_t +xdr_test_response (XDR *xdrs, void *data, ...) +{ + struct test_response *p = data; + return xdr_uint32_t (xdrs, &p->seq) + && xdr_uint32_t (xdrs, &p->sum); +} + +/* Implementation of the test server. */ + +enum + { + /* RPC parameters, chosen at random. */ + PROGNUM = 15717, + VERSNUM = 13689, + + /* Main RPC operation. */ + PROC_ADD = 1, + + /* Reset the sequence number. */ + PROC_RESET_SEQ, + + /* Request process termination. */ + PROC_EXIT, + + /* Special exit status to mark successful processing. */ + EXIT_MARKER = 55, + }; + +static void +server_dispatch (struct svc_req *request, SVCXPRT *transport) +{ + /* Query sequence number. */ + static uint32_t seq = 0; + ++seq; + + if (test_verbose) + printf ("info: server_dispatch seq=%u rq_proc=%lu\n", + seq, request->rq_proc); + + switch (request->rq_proc) + { + case PROC_ADD: + { + struct test_query query; + memset (&query, 0xc0, sizeof (query)); + TEST_VERIFY_EXIT + (svc_getargs (transport, xdr_test_query, + (void *) &query)); + + if (test_verbose) + printf (" a=%u b=%u timeout_ms=%u wait_for_seq=%u" + " garbage_packets=%u\n", + query.a, query.b, query.timeout_ms, query.wait_for_seq, + query.garbage_packets); + + if (seq < query.wait_for_seq) + { + /* No response at this point. */ + if (test_verbose) + printf (" skipped response\n"); + break; + } + + if (query.garbage_packets > 0) + { + int per_packet_timeout; + if (query.timeout_ms > 0) + per_packet_timeout + = query.timeout_ms * 1000 / query.garbage_packets; + else + per_packet_timeout = 0; + + char buf[20]; + memset (&buf, 0xc0, sizeof (buf)); + for (int i = 0; i < query.garbage_packets; ++i) + { + /* 13 is relatively prime to 20 = sizeof (buf) + 1, so + the len variable will cover the entire interval + [0, 20] if query.garbage_packets is sufficiently + large. */ + size_t len = (i * 13 + 1) % (sizeof (buf) + 1); + TEST_VERIFY (sendto (transport->xp_sock, + buf, len, MSG_NOSIGNAL, + (struct sockaddr *) &transport->xp_raddr, + transport->xp_addrlen) == len); + if (per_packet_timeout > 0) + usleep (per_packet_timeout); + } + } + else if (query.timeout_ms > 0) + usleep (query.timeout_ms * 1000); + + struct test_response response = + { + .seq = seq, + .sum = query.a + query.b, + }; + TEST_VERIFY (svc_sendreply (transport, xdr_test_response, + (void *) &response)); + } + break; + + case PROC_RESET_SEQ: + seq = 0; + TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL)); + break; + + case PROC_EXIT: + TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL)); + _exit (EXIT_MARKER); + break; + + default: + FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc); + break; + } +} + +/* Implementation of the test client. */ + +static struct test_response +test_call (CLIENT *clnt, int proc, struct test_query query, + struct timeval timeout) +{ + if (test_verbose) + printf ("info: test_call proc=%d timeout=%lu.%06lu\n", + proc, (unsigned long) timeout.tv_sec, + (unsigned long) timeout.tv_usec); + struct test_response response; + TEST_VERIFY_EXIT (clnt_call (clnt, proc, + xdr_test_query, (void *) &query, + xdr_test_response, (void *) &response, + timeout) + == RPC_SUCCESS); + return response; +} + +static void +test_call_timeout (CLIENT *clnt, int proc, struct test_query query, + struct timeval timeout) +{ + struct test_response response; + TEST_VERIFY (clnt_call (clnt, proc, + xdr_test_query, (void *) &query, + xdr_test_response, (void *) &response, + timeout) + == RPC_TIMEDOUT); +} + +/* Complete one regular RPC call to drain the server socket + buffer. Resets the sequence number. */ +static void +test_call_flush (CLIENT *clnt) +{ + /* This needs a longer timeout to flush out all pending requests. + The choice of 5 seconds is larger than the per-response timeouts + requested via the timeout_ms field. */ + if (test_verbose) + printf ("info: flushing pending queries\n"); + TEST_VERIFY_EXIT (clnt_call (clnt, PROC_RESET_SEQ, + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_void, NULL, + ((struct timeval) { 5, 0 })) + == RPC_SUCCESS); +} + +/* Return the number seconds since an arbitrary point in time. */ +static double +get_ticks (void) +{ + { + struct timespec ts; + if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec + ts.tv_nsec * 1e-9; + } + { + struct timeval tv; + TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0); + return tv.tv_sec + tv.tv_usec * 1e-6; + } +} + +static void +test_udp_server (int port) +{ + struct sockaddr_in sin = + { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl (INADDR_LOOPBACK), + .sin_port = htons (port) + }; + int sock = RPC_ANYSOCK; + + /* The client uses a 1.5 second timeout for retries. The timeouts + are arbitrary, but chosen so that there is a substantial gap + between them, but the total time spent waiting is not too + large. */ + CLIENT *clnt = clntudp_create (&sin, PROGNUM, VERSNUM, + (struct timeval) { 1, 500 * 1000 }, + &sock); + TEST_VERIFY_EXIT (clnt != NULL); + + /* Basic call/response test. */ + struct test_response response = test_call + (clnt, PROC_ADD, + (struct test_query) { .a = 17, .b = 4 }, + (struct timeval) { 3, 0 }); + TEST_VERIFY (response.sum == 21); + TEST_VERIFY (response.seq == 1); + + /* Check that garbage packets do not interfere with timeout + processing. */ + double before = get_ticks (); + response = test_call + (clnt, PROC_ADD, + (struct test_query) { + .a = 19, .b = 4, .timeout_ms = 500, .garbage_packets = 21, + }, + (struct timeval) { 3, 0 }); + TEST_VERIFY (response.sum == 23); + TEST_VERIFY (response.seq == 2); + double after = get_ticks (); + if (test_verbose) + printf ("info: 21 garbage packets took %f seconds\n", after - before); + /* Expected timeout is 0.5 seconds. Add some slack in case process + scheduling delays processing the query or response, but do not + accept a retry (which would happen at 1.5 seconds). */ + TEST_VERIFY (0.5 <= after - before); + TEST_VERIFY (after - before < 1.2); + test_call_flush (clnt); + + /* Check that missing a response introduces a 1.5 second timeout, as + requested when calling clntudp_create. */ + before = get_ticks (); + response = test_call + (clnt, PROC_ADD, + (struct test_query) { .a = 170, .b = 40, .wait_for_seq = 2 }, + (struct timeval) { 3, 0 }); + TEST_VERIFY (response.sum == 210); + TEST_VERIFY (response.seq == 2); + after = get_ticks (); + if (test_verbose) + printf ("info: skipping one response took %f seconds\n", + after - before); + /* Expected timeout is 1.5 seconds. Do not accept a second retry + (which would happen at 3 seconds). */ + TEST_VERIFY (1.5 <= after - before); + TEST_VERIFY (after - before < 2.9); + test_call_flush (clnt); + + /* Check that the overall timeout wins against the per-query + timeout. */ + before = get_ticks (); + test_call_timeout + (clnt, PROC_ADD, + (struct test_query) { .a = 170, .b = 41, .wait_for_seq = 2 }, + (struct timeval) { 0, 750 * 1000 }); + after = get_ticks (); + if (test_verbose) + printf ("info: 0.75 second timeout took %f seconds\n", + after - before); + TEST_VERIFY (0.75 <= after - before); + TEST_VERIFY (after - before < 1.4); + test_call_flush (clnt); + + for (int with_garbage = 0; with_garbage < 2; ++with_garbage) + { + /* Check that no response at all causes the client to bail out. */ + before = get_ticks (); + test_call_timeout + (clnt, PROC_ADD, + (struct test_query) { + .a = 170, .b = 40, .timeout_ms = 1200, + .garbage_packets = with_garbage * 21 + }, + (struct timeval) { 0, 750 * 1000 }); + after = get_ticks (); + if (test_verbose) + printf ("info: test_udp_server: 0.75 second timeout took %f seconds" + " (garbage %d)\n", + after - before, with_garbage); + TEST_VERIFY (0.75 <= after - before); + TEST_VERIFY (after - before < 1.4); + test_call_flush (clnt); + + /* As above, but check the total timeout. */ + before = get_ticks (); + test_call_timeout + (clnt, PROC_ADD, + (struct test_query) { + .a = 170, .b = 40, .timeout_ms = 3000, + .garbage_packets = with_garbage * 30 + }, + (struct timeval) { 2, 300 * 1000 }); + after = get_ticks (); + if (test_verbose) + printf ("info: test_udp_server: 2.3 second timeout took %f seconds" + " (garbage %d)\n", + after - before, with_garbage); + TEST_VERIFY (2.3 <= after - before); + TEST_VERIFY (after - before < 3.0); + test_call_flush (clnt); + } + + TEST_VERIFY_EXIT (clnt_call (clnt, PROC_EXIT, + (xdrproc_t) xdr_void, NULL, + (xdrproc_t) xdr_void, NULL, + ((struct timeval) { 5, 0 })) + == RPC_SUCCESS); + clnt_destroy (clnt); +} + +static int +do_test (void) +{ + support_become_root (); + support_enter_network_namespace (); + + SVCXPRT *transport = svcudp_create (RPC_ANYSOCK); + TEST_VERIFY_EXIT (transport != NULL); + TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM, server_dispatch, 0)); + + pid_t pid = xfork (); + if (pid == 0) + { + svc_run (); + FAIL_EXIT1 ("supposed to be unreachable"); + } + test_udp_server (transport->xp_port); + + int status; + xwaitpid (pid, &status, 0); + TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER); + + SVC_DESTROY (transport); + return 0; +} + +/* The minimum run time is around 17 seconds. */ +#define TIMEOUT 25 +#include diff --git a/support/Makefile b/support/Makefile index 2ace559ae0..027a663000 100644 --- a/support/Makefile +++ b/support/Makefile @@ -35,7 +35,12 @@ libsupport-routines = \ oom_error \ resolv_test \ set_fortify_handler \ + support-xstat \ support_become_root \ + support_can_chroot \ + support_capture_subprocess \ + support_capture_subprocess_check \ + support_chroot \ support_enter_network_namespace \ support_format_address_family \ support_format_addrinfo \ @@ -43,17 +48,25 @@ libsupport-routines = \ support_format_herrno \ support_format_hostent \ support_format_netent \ + support_isolate_in_subprocess \ support_record_failure \ support_run_diff \ + support_shared_allocate \ + support_write_file_string \ support_test_main \ support_test_verify_impl \ temp_file \ write_message \ xaccept \ + xaccept4 \ xasprintf \ xbind \ xcalloc \ + xchroot \ + xclose \ xconnect \ + xdlfcn \ + xdup2 \ xfclose \ xfopen \ xfork \ @@ -61,13 +74,18 @@ libsupport-routines = \ xlisten \ xmalloc \ xmemstream \ + xmkdir \ xmmap \ + xmprotect \ xmunmap \ + xopen \ + xpipe \ xpoll \ xpthread_attr_destroy \ xpthread_attr_init \ xpthread_attr_setdetachstate \ xpthread_attr_setstacksize \ + xpthread_attr_setguardsize \ xpthread_barrier_destroy \ xpthread_barrier_init \ xpthread_barrier_wait \ @@ -89,6 +107,12 @@ libsupport-routines = \ xpthread_mutexattr_setrobust \ xpthread_mutexattr_settype \ xpthread_once \ + xpthread_rwlock_init \ + xpthread_rwlock_rdlock \ + xpthread_rwlock_wrlock \ + xpthread_rwlock_unlock \ + xpthread_rwlockattr_init \ + xpthread_rwlockattr_setkind_np \ xpthread_sigmask \ xpthread_spin_lock \ xpthread_spin_unlock \ @@ -111,6 +135,8 @@ endif tests = \ README-testing \ tst-support-namespace \ + tst-support_capture_subprocess \ + tst-support_format_dns_packet \ tst-support_record_failure \ ifeq ($(run-built-tests),yes) @@ -125,4 +151,6 @@ $(objpfx)tst-support_record_failure-2.out: tst-support_record_failure-2.sh \ $(evaluate-test) endif +$(objpfx)tst-support_format_dns_packet: $(common-objpfx)resolv/libresolv.so + include ../Rules diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h new file mode 100644 index 0000000000..43caf9bce4 --- /dev/null +++ b/support/capture_subprocess.h @@ -0,0 +1,61 @@ +/* Capture output from a subprocess. + Copyright (C) 2017 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_CAPTURE_SUBPROCESS_H +#define SUPPORT_CAPTURE_SUBPROCESS_H + +#include + +struct support_capture_subprocess +{ + struct xmemstream out; + struct xmemstream err; + int status; +}; + +/* Invoke CALLBACK (CLOSURE) in a subprocess and capture standard + output, standard error, and the exit status. The out.buffer and + err.buffer members in the result are null-terminated strings which + can be examined by the caller (out.out and err.out are NULL). */ +struct support_capture_subprocess support_capture_subprocess + (void (*callback) (void *), void *closure); + +/* Deallocate the subprocess data captured by + support_capture_subprocess. */ +void support_capture_subprocess_free (struct support_capture_subprocess *); + +enum support_capture_allow +{ + /* No output is allowed. */ + sc_allow_none = 0x01, + /* Output to stdout is permitted. */ + sc_allow_stdout = 0x02, + /* Output to standard error is permitted. */ + sc_allow_stderr = 0x04, +}; + +/* Check that the subprocess exited with STATUS and that only the + allowed outputs happened. ALLOWED is a combination of + support_capture_allow flags. Report errors under the CONTEXT + message. */ +void support_capture_subprocess_check (struct support_capture_subprocess *, + const char *context, int status, + int allowed) + __attribute__ ((nonnull (1, 2))); + +#endif /* SUPPORT_CAPTURE_SUBPROCESS_H */ diff --git a/support/check.h b/support/check.h index 1d244a3557..bdcd12952a 100644 --- a/support/check.h +++ b/support/check.h @@ -51,7 +51,7 @@ __BEGIN_DECLS if (expr) \ ; \ else \ - support_test_verify_impl (-1, __FILE__, __LINE__, #expr); \ + support_test_verify_impl (__FILE__, __LINE__, #expr); \ }) /* Record a test failure and exit if EXPR evaluates to false. */ @@ -60,7 +60,8 @@ __BEGIN_DECLS if (expr) \ ; \ else \ - support_test_verify_impl (1, __FILE__, __LINE__, #expr); \ + support_test_verify_exit_impl \ + (1, __FILE__, __LINE__, #expr); \ }) int support_print_failure_impl (const char *file, int line, @@ -70,8 +71,11 @@ void support_exit_failure_impl (int exit_status, const char *file, int line, const char *format, ...) __attribute__ ((noreturn, nonnull (2), format (printf, 4, 5))); -void support_test_verify_impl (int status, const char *file, int line, +void support_test_verify_impl (const char *file, int line, const char *expr); +void support_test_verify_exit_impl (int status, const char *file, int line, + const char *expr) + __attribute__ ((noreturn)); /* Record a test failure. This function returns and does not terminate the process. The failure counter is stored in a shared diff --git a/support/namespace.h b/support/namespace.h index 6bc82d619b..9eddb1a0e9 100644 --- a/support/namespace.h +++ b/support/namespace.h @@ -35,6 +35,13 @@ __BEGIN_DECLS single-threaded processes. */ bool support_become_root (void); +/* Return true if this process can perform a chroot operation. In + general, this is only possible if support_become_root has been + called. Note that the actual test is performed in a subprocess, + after fork, so that the file system root of the original process is + not changed. */ +bool support_can_chroot (void); + /* Enter a network namespace (and a UTS namespace if possible) and configure the loopback interface. Return true if a network namespace could be created. Print diagnostics to standard output. @@ -48,6 +55,48 @@ bool support_enter_network_namespace (void); UTS namespace. */ bool support_in_uts_namespace (void); +/* Invoke CALLBACK (CLOSURE) in a subprocess created using fork. + Terminate the calling process if the subprocess exits with a + non-zero exit status. */ +void support_isolate_in_subprocess (void (*callback) (void *), void *closure); + +/* Describe the setup of a chroot environment, for + support_chroot_create below. */ +struct support_chroot_configuration +{ + /* File contents. The files are not created if the field is + NULL. */ + const char *resolv_conf; /* /etc/resolv.conf. */ + const char *hosts; /* /etc/hosts. */ + const char *host_conf; /* /etc/host.conf. */ +}; + +/* The result of the creation of a chroot. */ +struct support_chroot +{ + /* Path information. All these paths are relative to the parent + chroot. */ + + /* Path to the chroot directory. */ + char *path_chroot; + + /* Paths to files in the chroot. These are absolute and outside of + the chroot. */ + char *path_resolv_conf; /* /etc/resolv.conf. */ + char *path_hosts; /* /etc/hosts. */ + char *path_host_conf; /* /etc/host.conf. */ +}; + +/* Create a chroot environment. The returned data should be freed + using support_chroot_free below. The files will be deleted when + the process exits. This function does not enter the chroot. */ +struct support_chroot *support_chroot_create + (struct support_chroot_configuration); + +/* Deallocate the chroot information created by + support_chroot_create. */ +void support_chroot_free (struct support_chroot *); + __END_DECLS #endif diff --git a/support/resolv_test.c b/support/resolv_test.c index 2d0ea3c17c..1625dcf43a 100644 --- a/support/resolv_test.c +++ b/support/resolv_test.c @@ -32,9 +32,11 @@ #include #include #include +#include +#include #include -/* Response builder. */ +/* Response builder. */ enum { @@ -428,6 +430,7 @@ struct query_info char qname[MAXDNAME]; uint16_t qclass; uint16_t qtype; + struct resolv_edns_info edns; }; /* Update *INFO from the specified DNS packet. */ @@ -435,10 +438,26 @@ static void parse_query (struct query_info *info, const unsigned char *buffer, size_t length) { - if (length < 12) + HEADER hd; + _Static_assert (sizeof (hd) == 12, "DNS header size"); + if (length < sizeof (hd)) FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length); - - int ret = dn_expand (buffer, buffer + length, buffer + 12, + memcpy (&hd, buffer, sizeof (hd)); + + if (ntohs (hd.qdcount) != 1) + FAIL_EXIT1 ("malformed DNS query: wrong question count: %d", + (int) ntohs (hd.qdcount)); + if (ntohs (hd.ancount) != 0) + FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d", + (int) ntohs (hd.ancount)); + if (ntohs (hd.nscount) != 0) + FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d", + (int) ntohs (hd.nscount)); + if (ntohs (hd.arcount) > 1) + FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d", + (int) ntohs (hd.arcount)); + + int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd), info->qname, sizeof (info->qname)); if (ret < 0) FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME"); @@ -456,6 +475,37 @@ parse_query (struct query_info *info, memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass)); info->qclass = ntohs (qtype_qclass.qclass); info->qtype = ntohs (qtype_qclass.qtype); + + memset (&info->edns, 0, sizeof (info->edns)); + if (ntohs (hd.arcount) > 0) + { + /* Parse EDNS record. */ + struct __attribute__ ((packed, aligned (1))) + { + uint8_t root; + uint16_t rtype; + uint16_t payload; + uint8_t edns_extended_rcode; + uint8_t edns_version; + uint16_t flags; + uint16_t rdatalen; + } rr; + _Static_assert (sizeof (rr) == 11, "EDNS record size"); + + if (remaining < 4 + sizeof (rr)) + FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record"); + memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr)); + if (rr.root != 0) + FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root); + if (rr.rtype != htons (41)) + FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n", + ntohs (rr.rtype)); + info->edns.active = true; + info->edns.extended_rcode = rr.edns_extended_rcode; + info->edns.version = rr.edns_version; + info->edns.flags = ntohs (rr.flags); + info->edns.payload_size = ntohs (rr.payload); + } } @@ -585,6 +635,7 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index) .query_length = length, .server_index = server_index, .tcp = false, + .edns = qinfo.edns, }; struct resolv_response_builder *b = response_builder_allocate (query, length); obj->config.response_callback @@ -820,6 +871,7 @@ server_thread_tcp_client (void *arg) .query_length = query_length, .server_index = closure->server_index, .tcp = true, + .edns = qinfo.edns, }; struct resolv_response_builder *b = response_builder_allocate (query_buffer, query_length); @@ -860,7 +912,7 @@ server_thread_tcp_client (void *arg) break; } - close (closure->client_socket); + xclose (closure->client_socket); free (closure); return NULL; } @@ -881,7 +933,7 @@ server_thread_tcp (struct resolv_test *obj, int server_index) if (obj->termination_requested) { xpthread_mutex_unlock (&obj->lock); - close (client_socket); + xclose (client_socket); break; } xpthread_mutex_unlock (&obj->lock); @@ -941,8 +993,8 @@ make_server_sockets (struct resolv_test_server *server) next local UDP address randomly. */ if (errno == EADDRINUSE) { - close (server->socket_udp); - close (server->socket_tcp); + xclose (server->socket_udp); + xclose (server->socket_tcp); continue; } FAIL_EXIT1 ("TCP bind: %m"); @@ -952,6 +1004,29 @@ make_server_sockets (struct resolv_test_server *server) } } +/* Like make_server_sockets, but the caller supplies the address to + use. */ +static void +make_server_sockets_for_address (struct resolv_test_server *server, + const struct sockaddr *addr) +{ + server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (addr->sa_family == AF_INET) + server->address = *(const struct sockaddr_in *) addr; + else + /* We cannot store the server address in the socket. This should + not matter if disable_redirect is used. */ + server->address = (struct sockaddr_in) { .sin_family = 0, }; + + xbind (server->socket_udp, + (struct sockaddr *)&server->address, sizeof (server->address)); + xbind (server->socket_tcp, + (struct sockaddr *)&server->address, sizeof (server->address)); + xlisten (server->socket_tcp, 5); +} + /* One-time initialization of NSS. */ static void resolv_redirect_once (void) @@ -1012,11 +1087,17 @@ resolv_test_start (struct resolv_redirect_config config) .lock = PTHREAD_MUTEX_INITIALIZER, }; - resolv_test_init (); + if (!config.disable_redirect) + resolv_test_init (); /* Create all the servers, to reserve the necessary ports. */ for (int server_index = 0; server_index < config.nscount; ++server_index) - make_server_sockets (obj->servers + server_index); + if (config.disable_redirect && config.server_address_overrides != NULL) + make_server_sockets_for_address + (obj->servers + server_index, + config.server_address_overrides[server_index]); + else + make_server_sockets (obj->servers + server_index); /* Start server threads. Disable the server ports, as requested. */ @@ -1025,7 +1106,7 @@ resolv_test_start (struct resolv_redirect_config config) struct resolv_test_server *server = obj->servers + server_index; if (config.servers[server_index].disable_udp) { - close (server->socket_udp); + xclose (server->socket_udp); server->socket_udp = -1; } else if (!config.single_thread_udp) @@ -1033,7 +1114,7 @@ resolv_test_start (struct resolv_redirect_config config) server_thread_udp); if (config.servers[server_index].disable_tcp) { - close (server->socket_tcp); + xclose (server->socket_tcp); server->socket_tcp = -1; } else @@ -1043,6 +1124,9 @@ resolv_test_start (struct resolv_redirect_config config) if (config.single_thread_udp) start_server_thread_udp_single (obj); + if (config.disable_redirect) + return obj; + int timeout = 1; /* Initialize libresolv. */ @@ -1077,6 +1161,7 @@ resolv_test_start (struct resolv_redirect_config config) } for (int server_index = 0; server_index < config.nscount; ++server_index) { + TEST_VERIFY_EXIT (obj->servers[server_index].address.sin_port != 0); _res.nsaddr_list[server_index] = obj->servers[server_index].address; if (test_verbose) { @@ -1114,7 +1199,7 @@ resolv_test_end (struct resolv_test *obj) xsendto (sock, "", 1, 0, (struct sockaddr *) &obj->servers[server_index].address, sizeof (obj->servers[server_index].address)); - close (sock); + xclose (sock); } if (!obj->config.servers[server_index].disable_tcp) { @@ -1122,7 +1207,7 @@ resolv_test_end (struct resolv_test *obj) xconnect (sock, (struct sockaddr *) &obj->servers[server_index].address, sizeof (obj->servers[server_index].address)); - close (sock); + xclose (sock); } } @@ -1137,12 +1222,12 @@ resolv_test_end (struct resolv_test *obj) { if (!obj->config.single_thread_udp) xpthread_join (obj->servers[server_index].thread_udp); - close (obj->servers[server_index].socket_udp); + xclose (obj->servers[server_index].socket_udp); } if (!obj->config.servers[server_index].disable_tcp) { xpthread_join (obj->servers[server_index].thread_tcp); - close (obj->servers[server_index].socket_tcp); + xclose (obj->servers[server_index].socket_tcp); } } diff --git a/support/resolv_test.h b/support/resolv_test.h index 7a9f1f7ae8..b953dc1200 100644 --- a/support/resolv_test.h +++ b/support/resolv_test.h @@ -25,6 +25,16 @@ __BEGIN_DECLS +/* Information about EDNS properties of a DNS query. */ +struct resolv_edns_info +{ + bool active; + uint8_t extended_rcode; + uint8_t version; + uint16_t flags; + uint16_t payload_size; +}; + /* This struct provides context information when the response callback specified in struct resolv_redirect_config is invoked. */ struct resolv_response_context @@ -33,6 +43,7 @@ struct resolv_response_context size_t query_length; int server_index; bool tcp; + struct resolv_edns_info edns; }; /* This opaque struct is used to construct responses from within the @@ -82,6 +93,16 @@ struct resolv_redirect_config may results in more predictable ordering of queries and responses. */ bool single_thread_udp; + + /* Do not rewrite the _res variable or change NSS defaults. Use + server_address_overrides below to tell the testing framework on + which addresses to create the servers. */ + bool disable_redirect; + + /* Use these addresses for creating the DNS servers. The array must + have ns_count (or resolv_max_test_servers) sockaddr * elements if + not NULL. */ + const struct sockaddr *const *server_address_overrides; }; /* Configure NSS to use, nss_dns only for aplicable databases, and try diff --git a/support/support-xstat.c b/support/support-xstat.c new file mode 100644 index 0000000000..86a81ec601 --- /dev/null +++ b/support/support-xstat.c @@ -0,0 +1,30 @@ +/* stat64 with error checking. + Copyright (C) 2017 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 + . */ + +/* NB: Non-standard file name to avoid sysdeps override for xstat. */ + +#include +#include +#include + +void +xstat (const char *path, struct stat64 *result) +{ + if (stat64 (path, result) != 0) + FAIL_EXIT1 ("stat64 (\"%s\"): %m", path); +} diff --git a/support/support.h b/support/support.h index 7292e2a564..4b5f04c2cc 100644 --- a/support/support.h +++ b/support/support.h @@ -44,6 +44,21 @@ void set_fortify_handler (void (*handler) (int sig)); void oom_error (const char *function, size_t size) __attribute__ ((nonnull (1))); +/* Return a pointer to a memory region of SIZE bytes. The memory is + initialized to zero and will be shared with subprocesses (across + fork). The returned pointer must be freed using + support_shared_free; it is not compatible with the malloc + functions. */ +void *support_shared_allocate (size_t size); + +/* Deallocate a pointer returned by support_shared_allocate. */ +void support_shared_free (void *); + +/* Write CONTENTS to the file PATH. Create or truncate the file as + needed. The file mode is 0666 masked by the umask. Terminate the + process on error. */ +void support_write_file_string (const char *path, const char *contents); + /* Error-checking wrapper functions which terminate the process on error. */ diff --git a/support/support_can_chroot.c b/support/support_can_chroot.c new file mode 100644 index 0000000000..0dfd2deb54 --- /dev/null +++ b/support/support_can_chroot.c @@ -0,0 +1,65 @@ +/* Return true if the process can perform a chroot operation. + Copyright (C) 2017 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 + +static void +callback (void *closure) +{ + int *result = closure; + struct stat64 before; + xstat ("/dev", &before); + if (chroot ("/dev") != 0) + { + *result = errno; + return; + } + struct stat64 after; + xstat ("/", &after); + TEST_VERIFY (before.st_dev == after.st_dev); + TEST_VERIFY (before.st_ino == after.st_ino); + *result = 0; +} + +bool +support_can_chroot (void) +{ + int *result = support_shared_allocate (sizeof (*result)); + *result = 0; + support_isolate_in_subprocess (callback, result); + bool ok = *result == 0; + if (!ok) + { + static bool already_warned; + if (!already_warned) + { + already_warned = true; + errno = *result; + printf ("warning: this process does not support chroot: %m\n"); + } + } + support_shared_free (result); + return ok; +} diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c new file mode 100644 index 0000000000..030f124252 --- /dev/null +++ b/support/support_capture_subprocess.c @@ -0,0 +1,108 @@ +/* Capture output from a subprocess. + Copyright (C) 2017 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 + +static void +transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream) +{ + if (pfd->revents != 0) + { + char buf[1024]; + ssize_t ret = TEMP_FAILURE_RETRY (read (pfd->fd, buf, sizeof (buf))); + if (ret < 0) + { + support_record_failure (); + printf ("error: reading from subprocess %s: %m", what); + pfd->events = 0; + pfd->revents = 0; + } + else if (ret == 0) + { + /* EOF reached. Stop listening. */ + pfd->events = 0; + pfd->revents = 0; + } + else + /* Store the data just read. */ + TEST_VERIFY (fwrite (buf, ret, 1, stream->out) == 1); + } +} + +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); + + int stdout_pipe[2]; + xpipe (stdout_pipe); + int stderr_pipe[2]; + xpipe (stderr_pipe); + + 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); + 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 }, + }; + + do + { + xpoll (fds, 2, -1); + 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); + return result; +} + +void +support_capture_subprocess_free (struct support_capture_subprocess *p) +{ + free (p->out.buffer); + free (p->err.buffer); +} diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c new file mode 100644 index 0000000000..e1cf73b6a5 --- /dev/null +++ b/support/support_capture_subprocess_check.c @@ -0,0 +1,67 @@ +/* Verify capture output from a subprocess. + Copyright (C) 2017 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 + +static void +print_context (const char *context, bool *failed) +{ + if (*failed) + /* Do not duplicate message. */ + return; + support_record_failure (); + printf ("error: subprocess failed: %s\n", context); +} + +void +support_capture_subprocess_check (struct support_capture_subprocess *proc, + const char *context, int status, + int allowed) +{ + TEST_VERIFY ((allowed & sc_allow_none) + || (allowed & sc_allow_stdout) + || (allowed & sc_allow_stderr)); + TEST_VERIFY (!((allowed & sc_allow_none) + && ((allowed & sc_allow_stdout) + || (allowed & sc_allow_stderr)))); + + bool failed = false; + if (proc->status != status) + { + print_context (context, &failed); + printf ("error: expected exit status: %d\n", status); + printf ("error: actual exit status: %d\n", proc->status); + } + if (!(allowed & sc_allow_stdout) && proc->out.length != 0) + { + print_context (context, &failed); + printf ("error: unexpected output from subprocess\n"); + fwrite (proc->out.buffer, proc->out.length, 1, stdout); + puts ("\n"); + } + if (!(allowed & sc_allow_stderr) && proc->err.length != 0) + { + print_context (context, &failed); + printf ("error: unexpected error output from subprocess\n"); + fwrite (proc->err.buffer, proc->err.length, 1, stdout); + puts ("\n"); + } +} diff --git a/support/support_chroot.c b/support/support_chroot.c new file mode 100644 index 0000000000..f3ef551b05 --- /dev/null +++ b/support/support_chroot.c @@ -0,0 +1,85 @@ +/* Setup a chroot environment for use within tests. + Copyright (C) 2017 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 + +/* If CONTENTS is not NULL, write it to the file at DIRECTORY/RELPATH, + and store the name in *ABSPATH. If CONTENTS is NULL, store NULL in + *ABSPATH. */ +static void +write_file (const char *directory, const char *relpath, const char *contents, + char **abspath) +{ + if (contents != NULL) + { + *abspath = xasprintf ("%s/%s", directory, relpath); + add_temp_file (*abspath); + support_write_file_string (*abspath, contents); + } + else + *abspath = NULL; +} + +struct support_chroot * +support_chroot_create (struct support_chroot_configuration conf) +{ + struct support_chroot *chroot = xmalloc (sizeof (*chroot)); + + chroot->path_chroot = xasprintf ("%s/tst-resolv-res_init-XXXXXX", test_dir); + if (mkdtemp (chroot->path_chroot) == NULL) + FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chroot->path_chroot); + add_temp_file (chroot->path_chroot); + + /* Create the /etc directory in the chroot environment. */ + char *path_etc = xasprintf ("%s/etc", chroot->path_chroot); + xmkdir (path_etc, 0777); + add_temp_file (path_etc); + + write_file (path_etc, "resolv.conf", conf.resolv_conf, + &chroot->path_resolv_conf); + write_file (path_etc, "hosts", conf.hosts, &chroot->path_hosts); + write_file (path_etc, "host.conf", conf.host_conf, &chroot->path_host_conf); + + free (path_etc); + + /* valgrind needs a temporary directory in the chroot. */ + { + char *path_tmp = xasprintf ("%s/tmp", chroot->path_chroot); + xmkdir (path_tmp, 0777); + add_temp_file (path_tmp); + free (path_tmp); + } + + return chroot; +} + +void +support_chroot_free (struct support_chroot *chroot) +{ + free (chroot->path_chroot); + free (chroot->path_resolv_conf); + free (chroot->path_hosts); + free (chroot->path_host_conf); + free (chroot); +} diff --git a/support/support_enter_network_namespace.c b/support/support_enter_network_namespace.c index d2e78fe560..28b0ee29cf 100644 --- a/support/support_enter_network_namespace.c +++ b/support/support_enter_network_namespace.c @@ -23,9 +23,10 @@ #include #include #include +#include +#include #include #include -#include static bool in_uts_namespace; @@ -58,7 +59,7 @@ support_enter_network_namespace (void) req.ifr_flags |= IFF_UP | IFF_RUNNING; TEST_VERIFY_EXIT (ioctl (fd, SIOCSIFFLAGS, &req) == 0); } - close (fd); + xclose (fd); return !already_up; } diff --git a/support/support_format_addrinfo.c b/support/support_format_addrinfo.c index 262e0df737..eedb030591 100644 --- a/support/support_format_addrinfo.c +++ b/support/support_format_addrinfo.c @@ -39,8 +39,8 @@ socket_address_length (int family) } static void -format_ai_flags (FILE *out, struct addrinfo *ai, int flag, const char *name, - int * flags_printed) +format_ai_flags_1 (FILE *out, struct addrinfo *ai, int flag, const char *name, + int * flags_printed) { if ((ai->ai_flags & flag) != 0) fprintf (out, " %s", name); @@ -48,14 +48,16 @@ format_ai_flags (FILE *out, struct addrinfo *ai, int flag, const char *name, } static void -format_ai_one (FILE *out, struct addrinfo *ai, int *flags) +format_ai_flags (FILE *out, struct addrinfo *ai) { - /* ai_flags */ - if (ai->ai_flags != *flags) + if (ai == NULL) + return; + + if (ai->ai_flags != 0) { fprintf (out, "flags:"); int flags_printed = 0; -#define FLAG(flag) format_ai_flags (out, ai, flag, #flag, &flags_printed) +#define FLAG(flag) format_ai_flags_1 (out, ai, flag, #flag, &flags_printed) FLAG (AI_PASSIVE); FLAG (AI_CANONNAME); FLAG (AI_NUMERICHOST); @@ -72,9 +74,47 @@ format_ai_one (FILE *out, struct addrinfo *ai, int *flags) if (remaining != 0) fprintf (out, " %08x", remaining); fprintf (out, "\n"); - *flags = ai->ai_flags; } + /* Report flag mismatches within the list. */ + int flags = ai->ai_flags; + int index = 1; + ai = ai->ai_next; + while (ai != NULL) + { + if (ai->ai_flags != flags) + fprintf (out, "error: flags at %d: 0x%x expected, 0x%x actual\n", + index, flags, ai->ai_flags); + ai = ai->ai_next; + ++index; + } +} + +static void +format_ai_canonname (FILE *out, struct addrinfo *ai) +{ + if (ai == NULL) + return; + if (ai->ai_canonname != NULL) + fprintf (out, "canonname: %s\n", ai->ai_canonname); + + /* Report incorrectly set ai_canonname fields on subsequent list + entries. */ + int index = 1; + ai = ai->ai_next; + while (ai != NULL) + { + if (ai->ai_canonname != NULL) + fprintf (out, "error: canonname set at %d: %s\n", + index, ai->ai_canonname); + ai = ai->ai_next; + ++index; + } +} + +static void +format_ai_one (FILE *out, struct addrinfo *ai) +{ { char type_buf[32]; const char *type_str; @@ -156,20 +196,16 @@ format_ai_one (FILE *out, struct addrinfo *ai, int *flags) else fprintf (out, " %s %u\n", buf, ntohs (port)); } - - /* ai_canonname */ - if (ai->ai_canonname != NULL) - fprintf (out, "canonname: %s\n", ai->ai_canonname); } /* Format all the addresses in one address family. */ static void -format_ai_family (FILE *out, struct addrinfo *ai, int family, int *flags) +format_ai_family (FILE *out, struct addrinfo *ai, int family) { while (ai) { if (ai->ai_family == family) - format_ai_one (out, ai, flags); + format_ai_one (out, ai); ai = ai->ai_next; } } @@ -192,9 +228,10 @@ support_format_addrinfo (struct addrinfo *ai, int ret) } else { - int flags = 0; - format_ai_family (mem.out, ai, AF_INET, &flags); - format_ai_family (mem.out, ai, AF_INET6, &flags); + format_ai_flags (mem.out, ai); + format_ai_canonname (mem.out, ai); + format_ai_family (mem.out, ai, AF_INET); + format_ai_family (mem.out, ai, AF_INET6); } xfclose_memstream (&mem); diff --git a/support/support_format_dns_packet.c b/support/support_format_dns_packet.c index 21fe7e5c8d..2992c57971 100644 --- a/support/support_format_dns_packet.c +++ b/support/support_format_dns_packet.c @@ -174,7 +174,7 @@ support_format_dns_packet (const unsigned char *buffer, size_t length) goto out; } /* Skip non-matching record types. */ - if (rtype != qtype || rclass != qclass) + if ((rtype != qtype && rtype != T_CNAME) || rclass != qclass) continue; switch (rtype) { @@ -186,22 +186,29 @@ support_format_dns_packet (const unsigned char *buffer, size_t length) rdata.data[2], rdata.data[3]); else - fprintf (mem.out, "error: A record of size %d: %s\n", rdlen, rname.name); + fprintf (mem.out, "error: A record of size %d: %s\n", + rdlen, rname.name); break; case T_AAAA: { - char buf[100]; - if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL) - fprintf (mem.out, "error: AAAA record decoding failed: %m\n"); + if (rdlen == 16) + { + char buf[100]; + if (inet_ntop (AF_INET6, rdata.data, buf, sizeof (buf)) == NULL) + fprintf (mem.out, "error: AAAA record decoding failed: %m\n"); + else + fprintf (mem.out, "address: %s\n", buf); + } else - fprintf (mem.out, "address: %s\n", buf); + fprintf (mem.out, "error: AAAA record of size %d: %s\n", + rdlen, rname.name); } break; case T_CNAME: case T_PTR: { struct dname name; - if (extract_name (full, &in, &name)) + if (extract_name (full, &rdata, &name)) fprintf (mem.out, "name: %s\n", name.name); else fprintf (mem.out, "error: malformed CNAME/PTR record\n"); diff --git a/support/support_format_hostent.c b/support/support_format_hostent.c index 5b5f26082e..88c85ec1f1 100644 --- a/support/support_format_hostent.c +++ b/support/support_format_hostent.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -41,10 +42,15 @@ support_format_hostent (struct hostent *h) { if (h == NULL) { - char *value = support_format_herrno (h_errno); - char *result = xasprintf ("error: %s\n", value); - free (value); - return result; + if (h_errno == NETDB_INTERNAL) + return xasprintf ("error: NETDB_INTERNAL (errno %d, %m)\n", errno); + else + { + char *value = support_format_herrno (h_errno); + char *result = xasprintf ("error: %s\n", value); + free (value); + return result; + } } struct xmemstream mem; diff --git a/support/support_isolate_in_subprocess.c b/support/support_isolate_in_subprocess.c new file mode 100644 index 0000000000..cf48614383 --- /dev/null +++ b/support/support_isolate_in_subprocess.c @@ -0,0 +1,38 @@ +/* Run a function in a subprocess. + Copyright (C) 2017 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 + +void +support_isolate_in_subprocess (void (*callback) (void *), void *closure) +{ + pid_t pid = xfork (); + if (pid == 0) + { + /* Child process. */ + callback (closure); + _exit (0); + } + + /* Parent process. */ + int status; + xwaitpid (pid, &status, 0); + if (status != 0) + FAIL_EXIT1 ("child process exited with status %d", status); +} diff --git a/support/support_run_diff.c b/support/support_run_diff.c index 3085037a69..f5155de727 100644 --- a/support/support_run_diff.c +++ b/support/support_run_diff.c @@ -24,8 +24,8 @@ #include #include #include +#include #include -#include static char * write_to_temp_file (const char *prefix, const char *str) @@ -36,7 +36,7 @@ write_to_temp_file (const char *prefix, const char *str) TEST_VERIFY_EXIT (fd >= 0); free (template); xwrite (fd, str, strlen (str)); - TEST_VERIFY_EXIT (close (fd) == 0); + xclose (fd); return name; } diff --git a/support/support_shared_allocate.c b/support/support_shared_allocate.c new file mode 100644 index 0000000000..61d088e8cf --- /dev/null +++ b/support/support_shared_allocate.c @@ -0,0 +1,57 @@ +/* Allocate a memory region shared across processes. + Copyright (C) 2017 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 + +/* Header for the allocation. It contains the size of the allocation + for subsequent unmapping. */ +struct header +{ + size_t total_size; + char data[] __attribute__ ((aligned (__alignof__ (max_align_t)))); +}; + +void * +support_shared_allocate (size_t size) +{ + size_t total_size = size + offsetof (struct header, data); + if (total_size < size) + { + errno = ENOMEM; + oom_error (__func__, size); + return NULL; + } + else + { + struct header *result = xmmap (NULL, total_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1); + result->total_size = total_size; + return &result->data; + } +} + +void +support_shared_free (void *data) +{ + struct header *header = data - offsetof (struct header, data); + xmunmap (header, header->total_size); +} diff --git a/support/support_test_main.c b/support/support_test_main.c index 914d64f603..3c411a467b 100644 --- a/support/support_test_main.c +++ b/support/support_test_main.c @@ -211,7 +211,8 @@ support_test_main (int argc, char **argv, const struct test_config *config) mallopt (M_PERTURB, 42); } - while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, config->optstring, options, NULL)) + != -1) switch (opt) { case '?': diff --git a/support/support_test_verify_impl.c b/support/support_test_verify_impl.c index 5bae38f8b1..55ab2111b3 100644 --- a/support/support_test_verify_impl.c +++ b/support/support_test_verify_impl.c @@ -22,12 +22,16 @@ #include void -support_test_verify_impl (int status, const char *file, int line, - const char *expr) +support_test_verify_impl (const char *file, int line, const char *expr) { support_record_failure (); printf ("error: %s:%d: not true: %s\n", file, line, expr); - if (status >= 0) - exit (status); +} +void +support_test_verify_exit_impl (int status, const char *file, int line, + const char *expr) +{ + support_test_verify_impl (file, line, expr); + exit (status); } diff --git a/support/support_write_file_string.c b/support/support_write_file_string.c new file mode 100644 index 0000000000..48e89597f3 --- /dev/null +++ b/support/support_write_file_string.c @@ -0,0 +1,39 @@ +/* Write a string to a file. + Copyright (C) 2017 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 + +void +support_write_file_string (const char *path, const char *contents) +{ + int fd = xopen (path, O_CREAT | O_TRUNC | O_WRONLY, 0666); + const char *end = contents + strlen (contents); + for (const char *p = contents; p < end; ) + { + ssize_t ret = write (fd, p, end - p); + if (ret < 0) + FAIL_EXIT1 ("cannot write to \"%s\": %m", path); + if (ret == 0) + FAIL_EXIT1 ("zero-length write to \"%s\"", path); + p += ret; + } + xclose (fd); +} diff --git a/support/temp_file.c b/support/temp_file.c index f06647a467..fdb2477ab9 100644 --- a/support/temp_file.c +++ b/support/temp_file.c @@ -25,16 +25,17 @@ #include #include -#include #include #include #include +#include /* List of temporary files. */ static struct temp_name_list { - struct qelem q; + struct temp_name_list *next; char *name; + pid_t owner; } *temp_name_list; /* Location of the temporary files. Set by the test skeleton via @@ -50,10 +51,9 @@ add_temp_file (const char *name) if (newname != NULL) { newp->name = newname; - if (temp_name_list == NULL) - temp_name_list = (struct temp_name_list *) &newp->q; - else - insque (newp, temp_name_list); + newp->next = temp_name_list; + newp->owner = getpid (); + temp_name_list = newp; } else free (newp); @@ -97,13 +97,22 @@ support_set_test_dir (const char *path) void support_delete_temp_files (void) { + pid_t pid = getpid (); while (temp_name_list != NULL) { - remove (temp_name_list->name); + /* Only perform the removal if the path was registed in the same + process, as identified by the PID. (This assumes that the + parent process which registered the temporary file sticks + around, to prevent PID reuse.) */ + if (temp_name_list->owner == pid) + { + if (remove (temp_name_list->name) != 0) + printf ("warning: could not remove temporary file: %s: %m\n", + temp_name_list->name); + } free (temp_name_list->name); - struct temp_name_list *next - = (struct temp_name_list *) temp_name_list->q.q_forw; + struct temp_name_list *next = temp_name_list->next; free (temp_name_list); temp_name_list = next; } @@ -116,9 +125,7 @@ support_print_temp_files (FILE *f) { struct temp_name_list *n; fprintf (f, "temp_files=(\n"); - for (n = temp_name_list; - n != NULL; - n = (struct temp_name_list *) n->q.q_forw) + for (n = temp_name_list; n != NULL; n = n->next) fprintf (f, " '%s'\n", n->name); fprintf (f, ")\n"); } diff --git a/support/test-driver.c b/support/test-driver.c index 482066dbeb..47c387c2b4 100644 --- a/support/test-driver.c +++ b/support/test-driver.c @@ -93,6 +93,10 @@ has this type: void CMDLINE_PROCESS (int); + + If the program also to process custom default short command line + argument (similar to getopt) it must define CMDLINE_OPTSTRING + with the expected options (for instance "vb"). */ #include @@ -151,6 +155,11 @@ main (int argc, char **argv) #ifdef CMDLINE_PROCESS test_config.cmdline_function = CMDLINE_PROCESS; #endif +#ifdef CMDLINE_OPTSTRING + test_config.optstring = "+" CMDLINE_OPTSTRING; +#else + test_config.optstring = "+"; +#endif return support_test_main (argc, argv, &test_config); } diff --git a/support/test-driver.h b/support/test-driver.h index af1971a9ca..a8fe9c3565 100644 --- a/support/test-driver.h +++ b/support/test-driver.h @@ -35,6 +35,7 @@ struct test_config int expected_status; /* Expected exit status. */ int expected_signal; /* If non-zero, expect termination by signal. */ char no_mallopt; /* Boolean flag to disable mallopt. */ + const char *optstring; /* Short command line options. */ }; enum diff --git a/support/tst-support-namespace.c b/support/tst-support-namespace.c index a50b074f5e..dbe7cc07c8 100644 --- a/support/tst-support-namespace.c +++ b/support/tst-support-namespace.c @@ -16,18 +16,98 @@ License along with the GNU C Library; if not, see . */ +#include +#include #include +#include #include +#include +#include + +/* Check that the loopback interface provides multiple addresses which + can be used to run independent servers. */ +static void +test_localhost_bind (void) +{ + printf ("info: testing loopback interface with multiple addresses\n"); + + /* Create the two server addresses. */ + static const struct addrinfo hints = + { + .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP, + }; + struct addrinfo *ai[3]; + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.1", "53", &hints, ai + 0) == 0); + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.2", "53", &hints, ai + 1) == 0); + TEST_VERIFY_EXIT (getaddrinfo ("127.0.0.3", "53", &hints, ai + 2) == 0); + + /* Create the server scokets and bind them to these addresses. */ + int sockets[3]; + for (int i = 0; i < 3; ++i) + { + sockets[i] = xsocket + (ai[i]->ai_family, ai[i]->ai_socktype, ai[i]->ai_protocol); + xbind (sockets[i], ai[i]->ai_addr, ai[i]->ai_addrlen); + } + + /* Send two packets to each server. */ + int client = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + for (int i = 0; i < 3; ++i) + { + TEST_VERIFY (sendto (client, &i, sizeof (i), 0, + ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (i)); + int j = i + 256; + TEST_VERIFY (sendto (client, &j, sizeof (j), 0, + ai[i]->ai_addr, ai[i]->ai_addrlen) == sizeof (j)); + } + + /* Check that the packets can be received with the expected + contents. Note that the receive calls interleave differently, + which hopefully proves that the sockets are, indeed, + independent. */ + for (int i = 0; i < 3; ++i) + { + int buf; + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf)); + TEST_VERIFY (buf == i); + } + for (int i = 0; i < 3; ++i) + { + int buf; + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), 0) == sizeof (buf)); + TEST_VERIFY (buf == i + 256); + /* Check that there is no more data to receive. */ + TEST_VERIFY (recv (sockets[i], &buf, sizeof (buf), MSG_DONTWAIT) == -1); + TEST_VERIFY (errno == EWOULDBLOCK || errno == EAGAIN); + } + + /* Close all sockets and free the addresses. */ + for (int i = 0; i < 3; ++i) + { + freeaddrinfo (ai[i]); + xclose (sockets[i]); + } + xclose (client); +} + static int do_test (void) { - if (support_become_root ()) + bool root = support_become_root (); + if (root) printf ("info: acquired root-like privileges\n"); - if (support_enter_network_namespace ()) + bool netns = support_enter_network_namespace (); + if (netns) printf ("info: entered network namespace\n"); if (support_in_uts_namespace ()) printf ("info: also entered UTS namespace\n"); + + if (root && netns) + test_localhost_bind (); + return 0; } diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c new file mode 100644 index 0000000000..5672fba0f7 --- /dev/null +++ b/support/tst-support_capture_subprocess.c @@ -0,0 +1,188 @@ +/* Test capturing output from a subprocess. + Copyright (C) 2017 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 + +/* Write one byte at *P to FD and advance *P. Do nothing if *P is + '\0'. */ +static void +transfer (const unsigned char **p, int fd) +{ + if (**p != '\0') + { + TEST_VERIFY (write (fd, *p, 1) == 1); + ++*p; + } +} + +/* Determine the order in which stdout and stderr are written. */ +enum write_mode { out_first, err_first, interleave, + write_mode_last = interleave }; + +/* Describe what to write in the subprocess. */ +struct test +{ + char *out; + char *err; + enum write_mode write_mode; + int signal; + int status; +}; + +/* For use with support_capture_subprocess. */ +static void +callback (void *closure) +{ + const struct test *test = closure; + bool mode_ok = false; + switch (test->write_mode) + { + case out_first: + TEST_VERIFY (fputs (test->out, stdout) >= 0); + TEST_VERIFY (fflush (stdout) == 0); + TEST_VERIFY (fputs (test->err, stderr) >= 0); + TEST_VERIFY (fflush (stderr) == 0); + mode_ok = true; + break; + case err_first: + TEST_VERIFY (fputs (test->err, stderr) >= 0); + TEST_VERIFY (fflush (stderr) == 0); + TEST_VERIFY (fputs (test->out, stdout) >= 0); + TEST_VERIFY (fflush (stdout) == 0); + mode_ok = true; + break; + case interleave: + { + const unsigned char *pout = (const unsigned char *) test->out; + const unsigned char *perr = (const unsigned char *) test->err; + do + { + transfer (&pout, STDOUT_FILENO); + transfer (&perr, STDERR_FILENO); + } + while (*pout != '\0' || *perr != '\0'); + } + mode_ok = true; + break; + } + TEST_VERIFY (mode_ok); + + if (test->signal != 0) + raise (test->signal); + exit (test->status); +} + +/* Create a heap-allocated random string of letters. */ +static char * +random_string (size_t length) +{ + char *result = xmalloc (length + 1); + for (size_t i = 0; i < length; ++i) + result[i] = 'a' + (rand () % 26); + result[length] = '\0'; + return result; +} + +/* Check that the specific stream from the captured subprocess matches + expectations. */ +static void +check_stream (const char *what, const struct xmemstream *stream, + const char *expected) +{ + if (strcmp (stream->buffer, expected) != 0) + { + support_record_failure (); + printf ("error: captured %s data incorrect\n" + " expected: %s\n" + " actual: %s\n", + what, expected, stream->buffer); + } + if (stream->length != strlen (expected)) + { + support_record_failure (); + printf ("error: captured %s data length incorrect\n" + " expected: %zu\n" + " actual: %zu\n", + what, strlen (expected), stream->length); + } +} + +static int +do_test (void) +{ + const int lengths[] = {0, 1, 17, 512, 20000, -1}; + + /* Test multiple combinations of support_capture_subprocess. + + length_idx_stdout: Index into the lengths array above, + controls how many bytes are written by the subprocess to + standard output. + length_idx_stderr: Same for standard error. + write_mode: How standard output and standard error writes are + ordered. + signal: Exit with no signal if zero, with SIGTERM if one. + status: Process exit status: 0 if zero, 3 if one. */ + for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0; + ++length_idx_stdout) + for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0; + ++length_idx_stderr) + for (int write_mode = 0; write_mode < write_mode_last; ++write_mode) + for (int signal = 0; signal < 2; ++signal) + for (int status = 0; status < 2; ++status) + { + struct test test = + { + .out = random_string (lengths[length_idx_stdout]), + .err = random_string (lengths[length_idx_stderr]), + .write_mode = write_mode, + .signal = signal * SIGTERM, /* 0 or SIGTERM. */ + .status = status * 3, /* 0 or 3. */ + }; + 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); + check_stream ("stdout", &result.out, test.out); + check_stream ("stderr", &result.err, test.err); + if (test.signal != 0) + { + TEST_VERIFY (WIFSIGNALED (result.status)); + TEST_VERIFY (WTERMSIG (result.status) == test.signal); + } + else + { + TEST_VERIFY (WIFEXITED (result.status)); + TEST_VERIFY (WEXITSTATUS (result.status) == test.status); + } + support_capture_subprocess_free (&result); + free (test.out); + free (test.err); + } + return 0; +} + +#include diff --git a/support/tst-support_format_dns_packet.c b/support/tst-support_format_dns_packet.c new file mode 100644 index 0000000000..9c8589c09c --- /dev/null +++ b/support/tst-support_format_dns_packet.c @@ -0,0 +1,101 @@ +/* Tests for the support_format_dns_packet function. + Copyright (C) 2016-2017 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 + +static void +check_packet (const void *buffer, size_t length, + const char *name, const char *expected) +{ + char *actual = support_format_dns_packet (buffer, length); + if (strcmp (actual, expected) != 0) + { + support_record_failure (); + printf ("error: formatted packet does not match: %s\n", name); + support_run_diff ("expected", expected, + "actual", actual); + } + free (actual); +} + +static void +test_aaaa_length (void) +{ + static const char packet[] = + /* Header: Response with two records. */ + "\x12\x34\x80\x00\x00\x01\x00\x02\x00\x00\x00\x00" + /* Question section. www.example/IN/AAAA. */ + "\x03www\x07""example\x00\x00\x1c\x00\x01" + /* Answer section. www.example AAAA [corrupted]. */ + "\xc0\x0c" + "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x10" + "\x20\x01\x0d\xb8\x05\x06\x07\x08" + "\x11\x12\x13\x14\x15\x16\x17\x18" + /* www.example AAAA [corrupted]. */ + "\xc0\x0c" + "\x00\x1c\x00\x01\x00\x00\x00\x00\x00\x11" + "\x01\x02\x03\x04\x05\x06\x07\x08" + "\x11\x12\x13\x14\x15\x16\x17\x18" "\xff"; + check_packet (packet, sizeof (packet) - 1, __func__, + "name: www.example\n" + "address: 2001:db8:506:708:1112:1314:1516:1718\n" + "error: AAAA record of size 17: www.example\n"); +} + +static void +test_multiple_cnames (void) +{ + static const char packet[] = + /* Header: Response with three records. */ + "\x12\x34\x80\x00\x00\x01\x00\x03\x00\x00\x00\x00" + /* Question section. www.example/IN/A. */ + "\x03www\x07""example\x00\x00\x01\x00\x01" + /* Answer section. www.example CNAME www1.example. */ + "\xc0\x0c" + "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07" + "\x04www1\xc0\x10" + /* www1 CNAME www2. */ + "\x04www1\xc0\x10" + "\x00\x05\x00\x01\x00\x00\x00\x00\x00\x07" + "\x04www2\xc0\x10" + /* www2 A 192.0.2.1. */ + "\x04www2\xc0\x10" + "\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04" + "\xc0\x00\x02\x01"; + check_packet (packet, sizeof (packet) - 1, __func__, + "name: www.example\n" + "name: www1.example\n" + "name: www2.example\n" + "address: 192.0.2.1\n"); +} + +static int +do_test (void) +{ + test_aaaa_length (); + test_multiple_cnames (); + return 0; +} + +#include diff --git a/support/tst-support_record_failure-2.sh b/support/tst-support_record_failure-2.sh index 175137780a..2c9372cc29 100644 --- a/support/tst-support_record_failure-2.sh +++ b/support/tst-support_record_failure-2.sh @@ -37,7 +37,7 @@ run_test () { set -e echo " exit status: $status" if test "$output" != "$expected_output" ; then - echo "error: unexpected ouput: $output" + echo "error: unexpected output: $output" exit 1 fi if test "$status" -ne "$expected_status" ; then @@ -52,9 +52,9 @@ different_status () { run_test 1 "error: 1 test failures" $direct --status=1 run_test 2 "error: 1 test failures" $direct --status=2 run_test 1 "error: 1 test failures" $direct --status=77 - run_test 2 "error: tst-support_record_failure.c:108: not true: false + run_test 2 "error: tst-support_record_failure.c:109: not true: false error: 1 test failures" $direct --test-verify - run_test 2 "error: tst-support_record_failure.c:108: not true: false + run_test 2 "error: tst-support_record_failure.c:109: not true: false info: execution passed failed TEST_VERIFY error: 1 test failures" $direct --test-verify --verbose } @@ -62,8 +62,8 @@ error: 1 test failures" $direct --test-verify --verbose different_status different_status --direct -run_test 1 "error: tst-support_record_failure.c:115: not true: false +run_test 1 "error: tst-support_record_failure.c:116: not true: false error: 1 test failures" --test-verify-exit # --direct does not print the summary error message if exit is called. -run_test 1 "error: tst-support_record_failure.c:115: not true: false" \ +run_test 1 "error: tst-support_record_failure.c:116: not true: false" \ --direct --test-verify-exit diff --git a/support/tst-support_record_failure.c b/support/tst-support_record_failure.c index 62d8e1f057..e739e739c3 100644 --- a/support/tst-support_record_failure.c +++ b/support/tst-support_record_failure.c @@ -25,6 +25,7 @@ #include #include #include +#include static int exit_status_with_failure = -1; static bool test_verify; diff --git a/support/xaccept4.c b/support/xaccept4.c new file mode 100644 index 0000000000..67dd95e9fb --- /dev/null +++ b/support/xaccept4.c @@ -0,0 +1,32 @@ +/* accept4 with error checking. + Copyright (C) 2017 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 + +int +xaccept4 (int fd, struct sockaddr *sa, socklen_t *salen, int flags) +{ + int clientfd = accept4 (fd, sa, salen, flags); + if (clientfd < 0) + FAIL_EXIT1 ("accept4 (%d, 0x%x): %m", fd, flags); + return clientfd; +} diff --git a/support/xchroot.c b/support/xchroot.c new file mode 100644 index 0000000000..abcc299e00 --- /dev/null +++ b/support/xchroot.c @@ -0,0 +1,28 @@ +/* chroot with error checking. + Copyright (C) 2017 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 + +void +xchroot (const char *path) +{ + if (chroot (path) != 0) + FAIL_EXIT1 ("chroot (\"%s\"): %m", path); +} diff --git a/support/xclose.c b/support/xclose.c new file mode 100644 index 0000000000..c931e08421 --- /dev/null +++ b/support/xclose.c @@ -0,0 +1,28 @@ +/* close with error checking. + Copyright (C) 2017 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 + +void +xclose (int fd) +{ + if (close (fd) < 0 && errno != EINTR) + FAIL_EXIT1 ("close of descriptor %d failed: %m", fd); +} diff --git a/support/xdlfcn.c b/support/xdlfcn.c new file mode 100644 index 0000000000..6e3979983d --- /dev/null +++ b/support/xdlfcn.c @@ -0,0 +1,58 @@ +/* Support functionality for using dlopen/dlclose/dlsym. + Copyright (C) 2017 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 + +void * +xdlopen (const char *filename, int flags) +{ + void *dso = dlopen (filename, flags); + + if (dso == NULL) + FAIL_EXIT1 ("error: dlopen: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); + + return dso; +} + +void * +xdlsym (void *handle, const char *symbol) +{ + void *sym = dlsym (handle, symbol); + + if (sym == NULL) + FAIL_EXIT1 ("error: dlsym: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); + + return sym; +} + +void +xdlclose (void *handle) +{ + if (dlclose (handle) != 0) + FAIL_EXIT1 ("error: dlclose: %s\n", dlerror ()); + + /* Clear any errors. */ + dlerror (); +} diff --git a/support/xdlfcn.h b/support/xdlfcn.h new file mode 100644 index 0000000000..9bdcb38d3e --- /dev/null +++ b/support/xdlfcn.h @@ -0,0 +1,34 @@ +/* Support functionality for using dlopen/dlclose/dlsym. + Copyright (C) 2017 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_DLOPEN_H +#define SUPPORT_DLOPEN_H + +#include + +__BEGIN_DECLS + +/* Each of these terminates process on failure with relevant error message. */ +void *xdlopen (const char *filename, int flags); +void *xdlsym (void *handle, const char *symbol); +void xdlclose (void *handle); + + +__END_DECLS + +#endif /* SUPPORT_DLOPEN_H */ diff --git a/support/xdup2.c b/support/xdup2.c new file mode 100644 index 0000000000..dc08c94518 --- /dev/null +++ b/support/xdup2.c @@ -0,0 +1,28 @@ +/* dup2 with error checking. + Copyright (C) 2017 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 + +void +xdup2 (int from, int to) +{ + if (dup2 (from, to) < 0) + FAIL_EXIT1 ("dup2 (%d, %d): %m", from, to); +} diff --git a/support/xmkdir.c b/support/xmkdir.c new file mode 100644 index 0000000000..ea17d49391 --- /dev/null +++ b/support/xmkdir.c @@ -0,0 +1,28 @@ +/* mkdir with error checking. + Copyright (C) 2017 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 + +void +xmkdir (const char *path, mode_t mode) +{ + if (mkdir (path, mode) != 0) + FAIL_EXIT1 ("mkdir (\"%s\", 0%o): %m", path, mode); +} diff --git a/support/xmprotect.c b/support/xmprotect.c new file mode 100644 index 0000000000..9410251c32 --- /dev/null +++ b/support/xmprotect.c @@ -0,0 +1,28 @@ +/* mprotect with error checking. + Copyright (C) 2017 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 + +void +xmprotect (void *addr, size_t length, int prot) +{ + if (mprotect (addr, length, prot) != 0) + FAIL_EXIT1 ("mprotect (%p, %zu, 0x%x): %m", addr, length, prot); +} diff --git a/support/xopen.c b/support/xopen.c new file mode 100644 index 0000000000..7f033a03a7 --- /dev/null +++ b/support/xopen.c @@ -0,0 +1,30 @@ +/* open64 with error checking. + Copyright (C) 2017 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 + +int +xopen (const char *path, int flags, mode_t mode) +{ + int ret = open64 (path, flags, mode); + if (ret < 0) + FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0%o): %m", path, flags, mode); + return ret; +} diff --git a/support/xpipe.c b/support/xpipe.c new file mode 100644 index 0000000000..89a64a55c1 --- /dev/null +++ b/support/xpipe.c @@ -0,0 +1,28 @@ +/* pipe with error checking. + Copyright (C) 2017 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 + +void +xpipe (int fds[2]) +{ + if (pipe (fds) < 0) + FAIL_EXIT1 ("pipe: %m"); +} diff --git a/support/xpthread_attr_setguardsize.c b/support/xpthread_attr_setguardsize.c new file mode 100644 index 0000000000..35fed5d9ec --- /dev/null +++ b/support/xpthread_attr_setguardsize.c @@ -0,0 +1,26 @@ +/* pthread_attr_setguardsize with error checking. + Copyright (C) 2017 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_attr_setguardsize (pthread_attr_t *attr, size_t guardsize) +{ + xpthread_check_return ("pthread_attr_setguardize", + pthread_attr_setguardsize (attr, guardsize)); +} diff --git a/support/xpthread_rwlock_init.c b/support/xpthread_rwlock_init.c new file mode 100644 index 0000000000..824288c90e --- /dev/null +++ b/support/xpthread_rwlock_init.c @@ -0,0 +1,27 @@ +/* pthread_rwlock_init with error checking. + Copyright (C) 2017 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_init (pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr) +{ + xpthread_check_return ("pthread_rwlock_init", + pthread_rwlock_init (rwlock, attr)); +} diff --git a/support/xpthread_rwlock_rdlock.c b/support/xpthread_rwlock_rdlock.c new file mode 100644 index 0000000000..96330a5637 --- /dev/null +++ b/support/xpthread_rwlock_rdlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_rdlock with error checking. + Copyright (C) 2017 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_rdlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_rdlock", + pthread_rwlock_rdlock (rwlock)); +} diff --git a/support/xpthread_rwlock_unlock.c b/support/xpthread_rwlock_unlock.c new file mode 100644 index 0000000000..eaa136b3ec --- /dev/null +++ b/support/xpthread_rwlock_unlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_unlock with error checking. + Copyright (C) 2017 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_unlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_unlock", + pthread_rwlock_unlock (rwlock)); +} diff --git a/support/xpthread_rwlock_wrlock.c b/support/xpthread_rwlock_wrlock.c new file mode 100644 index 0000000000..8d25d5b818 --- /dev/null +++ b/support/xpthread_rwlock_wrlock.c @@ -0,0 +1,26 @@ +/* pthread_rwlock_wrlock with error checking. + Copyright (C) 2017 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_wrlock (pthread_rwlock_t *rwlock) +{ + xpthread_check_return ("pthread_rwlock_wrlock", + pthread_rwlock_wrlock (rwlock)); +} diff --git a/support/xpthread_rwlockattr_init.c b/support/xpthread_rwlockattr_init.c new file mode 100644 index 0000000000..48baf247f3 --- /dev/null +++ b/support/xpthread_rwlockattr_init.c @@ -0,0 +1,26 @@ +/* pthread_rwlockattr_init with error checking. + Copyright (C) 2017 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_rwlockattr_init (pthread_rwlockattr_t *attr) +{ + xpthread_check_return ("pthread_rwlockattr_init", + pthread_rwlockattr_init (attr)); +} diff --git a/support/xpthread_rwlockattr_setkind_np.c b/support/xpthread_rwlockattr_setkind_np.c new file mode 100644 index 0000000000..958aace9f6 --- /dev/null +++ b/support/xpthread_rwlockattr_setkind_np.c @@ -0,0 +1,27 @@ +/* pthread_rwlockattr_setkind_np with error checking. + Copyright (C) 2017 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_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, + int pref) +{ + xpthread_check_return ("pthread_rwlockattr_setkind_np", + pthread_rwlockattr_setkind_np (attr, pref)); +} diff --git a/support/xsocket.h b/support/xsocket.h index 0dbf13ace9..d6724948d8 100644 --- a/support/xsocket.h +++ b/support/xsocket.h @@ -30,6 +30,7 @@ void xconnect (int, const struct sockaddr *, socklen_t); void xbind (int, const struct sockaddr *, socklen_t); void xlisten (int, int); int xaccept (int, struct sockaddr *, socklen_t *); +int xaccept4 (int, struct sockaddr *, socklen_t *, int); void xsendto (int, const void *, size_t, int, const struct sockaddr *, socklen_t); size_t xrecvfrom (int, void *, size_t, int, struct sockaddr *, socklen_t *); diff --git a/support/xthread.h b/support/xthread.h index 6dd7e709be..472763ebe8 100644 --- a/support/xthread.h +++ b/support/xthread.h @@ -67,11 +67,21 @@ void xpthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate); void xpthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize); +void xpthread_attr_setguardsize (pthread_attr_t *attr, + size_t guardsize); /* This function returns non-zero if pthread_barrier_wait returned PTHREAD_BARRIER_SERIAL_THREAD. */ int xpthread_barrier_wait (pthread_barrier_t *barrier); +void xpthread_rwlock_init (pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *attr); +void xpthread_rwlockattr_init (pthread_rwlockattr_t *attr); +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); + __END_DECLS #endif /* SUPPORT_THREAD_H */ diff --git a/support/xunistd.h b/support/xunistd.h index a83b1f4541..c947bfd8fb 100644 --- a/support/xunistd.h +++ b/support/xunistd.h @@ -22,20 +22,33 @@ #ifndef SUPPORT_XUNISTD_H #define SUPPORT_XUNISTD_H -#include #include +#include +#include __BEGIN_DECLS +struct stat64; + pid_t xfork (void); pid_t xwaitpid (pid_t, int *status, int flags); +void xpipe (int[2]); +void xdup2 (int, int); +int xopen (const char *path, int flags, mode_t); +void xstat (const char *path, struct stat64 *); +void xmkdir (const char *path, mode_t); +void xchroot (const char *path); + +/* Close the file descriptor. Ignore EINTR errors, but terminate the + process on other errors. */ +void xclose (int); /* Write the buffer. Retry on short writes. */ void xwrite (int, const void *, size_t); /* Invoke mmap with a zero file offset. */ void *xmmap (void *addr, size_t length, int prot, int flags, int fd); - +void xmprotect (void *addr, size_t length, int prot); void xmunmap (void *addr, size_t length); __END_DECLS diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h index 84b8aecfb8..6067a1d8a0 100644 --- a/sysdeps/aarch64/dl-machine.h +++ b/sysdeps/aarch64/dl-machine.h @@ -193,8 +193,8 @@ _dl_start_user: \n\ cmp " PTR "0, #0 \n\ bne 1b \n\ // Update _dl_argv \n\ - adrp x3, _dl_argv \n\ - str " PTR "2, [x3, #:lo12:_dl_argv] \n\ + adrp x3, __GI__dl_argv \n\ + str " PTR "2, [x3, #:lo12:__GI__dl_argv] \n\ .L_done_stack_adjust: \n\ // compute envp \n\ add " PTR "3, " PTR "2, " PTR "1, lsl #" PTR_SIZE_LOG " \n\ diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h index a74083786e..5ea8a4a259 100644 --- a/sysdeps/generic/unsecvars.h +++ b/sysdeps/generic/unsecvars.h @@ -16,6 +16,7 @@ "LD_DEBUG\0" \ "LD_DEBUG_OUTPUT\0" \ "LD_DYNAMIC_WEAK\0" \ + "LD_HWCAP_MASK\0" \ "LD_LIBRARY_PATH\0" \ "LD_ORIGIN_PATH\0" \ "LD_PRELOAD\0" \ diff --git a/sysdeps/gnu/glob64.c b/sysdeps/gnu/glob64.c index d1e4e6f0d5..52e97e2f6a 100644 --- a/sysdeps/gnu/glob64.c +++ b/sysdeps/gnu/glob64.c @@ -15,11 +15,8 @@ #undef __stat #define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) -#define NO_GLOB_PATTERN_P 1 - #define COMPILE_GLOB64 1 #include libc_hidden_def (glob64) -libc_hidden_def (globfree64) diff --git a/sysdeps/gnu/globfree64.c b/sysdeps/gnu/globfree64.c new file mode 100644 index 0000000000..f092d0bf8b --- /dev/null +++ b/sysdeps/gnu/globfree64.c @@ -0,0 +1,10 @@ +#include +#include +#include + +#define glob_t glob64_t +#define globfree(pglob) globfree64 (pglob) + +#include + +libc_hidden_def (globfree64) diff --git a/sysdeps/hppa/__longjmp.c b/sysdeps/hppa/__longjmp.c index a7eefc7ad6..2fedb1d738 100644 --- a/sysdeps/hppa/__longjmp.c +++ b/sysdeps/hppa/__longjmp.c @@ -24,15 +24,15 @@ void __longjmp (__jmp_buf env, int val) { +#ifdef CHECK_SP + CHECK_SP (env[0].__jmp_buf.__sp); +#endif + /* We must use one of the non-callee saves registers for env. */ register unsigned long r26 asm ("r26") = (unsigned long)&env[0]; register unsigned long r25 asm ("r25") = (unsigned long)(val == 0 ? 1 : val); -#ifdef CHECK_SP - CHECK_SP (env[0].__jmp_buf.__sp); -#endif - asm volatile( /* Set return value. */ "copy %0, %%r28\n\t" @@ -79,6 +79,7 @@ __longjmp (__jmp_buf env, int val) : /* No outputs. */ : "r" (r25), "r" (r26) : /* No point in clobbers. */ ); + /* Avoid `volatile function does return' warnings. */ for (;;); } diff --git a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c index 83bdb91202..f74abc02c2 100644 --- a/sysdeps/hppa/dl-fptr.c +++ b/sysdeps/hppa/dl-fptr.c @@ -181,24 +181,29 @@ make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp) static inline ElfW(Addr) * __attribute__ ((always_inline)) make_fptr_table (struct link_map *map) { - const ElfW(Sym) *symtab - = (const void *) D_PTR (map, l_info[DT_SYMTAB]); + const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]); const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]); ElfW(Addr) *fptr_table; size_t size; size_t len; + const ElfW(Sym) *symtabend; - /* XXX Apparently the only way to find out the size of the dynamic - symbol section is to assume that the string table follows right - afterwards... */ - len = ((strtab - (char *) symtab) + /* Determine the end of the dynamic symbol table using the hash. */ + if (map->l_info[DT_HASH] != NULL) + symtabend = (symtab + ((Elf_Symndx *) D_PTR (map, l_info[DT_HASH]))[1]); + else + /* There is no direct way to determine the number of symbols in the + dynamic symbol table and no hash table is present. The ELF + binary is ill-formed but what shall we do? Use the beginning of + the string table which generally follows the symbol table. */ + symtabend = (const ElfW(Sym) *) strtab; + + len = (((char *) symtabend - (char *) symtab) / map->l_info[DT_SYMENT]->d_un.d_val); - size = ((len * sizeof (fptr_table[0]) + GLRO(dl_pagesize) - 1) - & -GLRO(dl_pagesize)); - /* XXX We don't support here in the moment systems without MAP_ANON. - There probably are none for IA-64. In case this is proven wrong - we will have to open /dev/null here and use the file descriptor - instead of the hard-coded -1. */ + size = ALIGN_UP (len * sizeof (fptr_table[0]), GLRO(dl_pagesize)); + + /* We don't support systems without MAP_ANON. We avoid using malloc + because this might get called before malloc is setup. */ fptr_table = __mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); @@ -331,22 +336,45 @@ elf_machine_resolve (void) return addr; } +static inline int +_dl_read_access_allowed (unsigned int *addr) +{ + int result; + + asm ("proberi (%1),3,%0" : "=r" (result) : "r" (addr) : ); + + return result; +} + ElfW(Addr) _dl_lookup_address (const void *address) { ElfW(Addr) addr = (ElfW(Addr)) address; unsigned int *desc, *gptr; - /* Check for special cases. */ - if ((int) addr == -1 - || (unsigned int) addr < 4096 - || !((unsigned int) addr & 2)) + /* Return ADDR if the least-significant two bits of ADDR are not consistent + with ADDR being a linker defined function pointer. The normal value for + a code address in a backtrace is 3. */ + if (((unsigned int) addr & 3) != 2) + return addr; + + /* Handle special case where ADDR points to page 0. */ + if ((unsigned int) addr < 4096) return addr; /* Clear least-significant two bits from descriptor address. */ desc = (unsigned int *) ((unsigned int) addr & ~3); + if (!_dl_read_access_allowed (desc)) + return addr; - /* Check if descriptor requires resolution. The following trampoline is + /* Load first word of candidate descriptor. It should be a pointer + with word alignment and point to memory that can be read. */ + gptr = (unsigned int *) desc[0]; + if (((unsigned int) gptr & 3) != 0 + || !_dl_read_access_allowed (gptr)) + return addr; + + /* See if descriptor requires resolution. The following trampoline is used in each global offset table for function resolution: ldw 0(r20),r22 @@ -358,7 +386,6 @@ _dl_lookup_address (const void *address) .word "_dl_runtime_resolve ltp" got: .word _DYNAMIC .word "struct link map address" */ - gptr = (unsigned int *) desc[0]; if (gptr[0] == 0xea9f1fdd /* b,l .-12,r20 */ && gptr[1] == 0xd6801c1e /* depwi 0,31,2,r20 */ && (ElfW(Addr)) gptr[2] == elf_machine_resolve ()) diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h index 339c7bb771..787b95f502 100644 --- a/sysdeps/hppa/dl-machine.h +++ b/sysdeps/hppa/dl-machine.h @@ -302,6 +302,10 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) #define ARCH_LA_PLTENTER hppa_gnu_pltenter #define ARCH_LA_PLTEXIT hppa_gnu_pltexit +/* Adjust DL_STACK_END to get value we want in __libc_stack_end. */ +#define DL_STACK_END(cookie) \ + ((void *) (((long) (cookie)) + 0x160)) + /* Initial entry point code for the dynamic linker. The C function `_dl_start' is the real entry point; its return value is the user program's entry point. */ @@ -401,11 +405,6 @@ asm ( \ /* Save the entry point in %r3. */ \ " copy %ret0,%r3\n" \ \ - /* Remember the lowest stack address. */ \ -" addil LT'__libc_stack_end,%r19\n" \ -" ldw RT'__libc_stack_end(%r1),%r20\n" \ -" stw %sp,0(%r20)\n" \ - \ /* See if we were called as a command with the executable file \ name as an extra leading argument. */ \ " addil LT'_dl_skip_args,%r19\n" \ diff --git a/sysdeps/hppa/dl-trampoline.S b/sysdeps/hppa/dl-trampoline.S index 856339bffe..3165c6f0e2 100644 --- a/sysdeps/hppa/dl-trampoline.S +++ b/sysdeps/hppa/dl-trampoline.S @@ -82,6 +82,21 @@ _dl_runtime_resolve: bl _dl_fixup,%rp copy %r21,%r19 /* set fixup func ltp */ + /* While the linker will set a function pointer to NULL when it + encounters an undefined weak function, we need to dynamically + detect removed weak functions. The issue arises because a weak + __gmon_start__ function was added to shared executables to work + around issues in _init that are now resolved. The presence of + __gmon_start__ in every shared library breaks the linker + `--as-needed' option. This __gmon_start__ function does nothing + but removal is tricky. Depending on the binding, removal can + cause an application using it to fault. The call to _dl_fixup + returns NULL when a function isn't resolved. In order to help + with __gmon_start__ removal, we return directly to the caller + when _dl_fixup returns NULL. This check could be removed when + BZ 19170 is fixed. */ + comib,= 0,%r28,1f + /* Load up the returned func descriptor */ copy %r28, %r22 copy %r29, %r19 @@ -107,6 +122,13 @@ _dl_runtime_resolve: /* Jump to new function, but return to previous function */ bv %r0(%r22) ldw -20(%sp),%rp + +1: + /* Return to previous function */ + ldw -148(%sp),%rp + bv %r0(%rp) + ldo -128(%sp),%sp + .EXIT .PROCEND cfi_endproc diff --git a/sysdeps/hppa/math-tests.h b/sysdeps/hppa/math-tests.h new file mode 100644 index 0000000000..bb205907ad --- /dev/null +++ b/sysdeps/hppa/math-tests.h @@ -0,0 +1,22 @@ +/* Configuration for math tests. hppa version. + Copyright (C) 2017 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 + . */ + +/* SNaN tests do not preserve payloads. */ +#define SNAN_TESTS_PRESERVE_PAYLOAD 0 + +#include_next diff --git a/sysdeps/hppa/nptl/bits/pthreadtypes.h b/sysdeps/hppa/nptl/bits/pthreadtypes.h index e37111a2f3..579047b732 100644 --- a/sysdeps/hppa/nptl/bits/pthreadtypes.h +++ b/sysdeps/hppa/nptl/bits/pthreadtypes.h @@ -106,36 +106,34 @@ typedef union /* Data structure for conditional variable handling. The structure of the attribute type is not exposed on purpose. However, this structure - is exposed via PTHREAD_COND_INITIALIZER, and because of this, the - Linuxthreads version sets the first four ints to one. In the NPTL - version we must check, in every function using pthread_cond_t, - for the static Linuxthreads initializer and clear the appropriate - words. */ + is exposed via PTHREAD_COND_INITIALIZER. Support for Linuxthreads has + been dropped but we still need to retain the alignment of the original + lock field from Linuxthreads. */ typedef union { struct { - /* In the old Linuxthreads pthread_cond_t, this is the - start of the 4-word lock structure, the next four words - are set all to 1 by the Linuxthreads - PTHREAD_COND_INITIALIZER. */ - int __lock __attribute__ ((__aligned__(16))); - /* Tracks the initialization of this structure: - 0 initialized with NPTL PTHREAD_COND_INITIALIZER. - 1 initialized with Linuxthreads PTHREAD_COND_INITIALIZER. - 2 initialization in progress. */ - int __initializer; - unsigned int __futex; - void *__mutex; - /* In the old Linuxthreads this would have been the start - of the pthread_fastlock status word. */ - __extension__ unsigned long long int __total_seq; - __extension__ unsigned long long int __wakeup_seq; - __extension__ unsigned long long int __woken_seq; - unsigned int __nwaiters; - unsigned int __broadcast_seq; - /* The NPTL pthread_cond_t is exactly the same size as - the Linuxthreads version, there are no words to spare. */ + __extension__ union + { + __extension__ unsigned long long int __wseq; + struct { + unsigned int __low; + unsigned int __high; + } __wseq32; + }; + __extension__ union + { + __extension__ unsigned long long int __g1_start; + struct { + unsigned int __low; + unsigned int __high; + } __g1_start32; + }; + unsigned int __g_refs[2] __attribute__ ((__aligned__(16))); + unsigned int __g_size[2]; + unsigned int __g1_orig_size; + unsigned int __wrefs; + unsigned int __g_signals[2]; } __data; char __size[__SIZEOF_PTHREAD_COND_T]; __extension__ long long int __align; diff --git a/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps b/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps index cb82d3ee36..275dbbe804 100644 --- a/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps +++ b/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps @@ -42,7 +42,7 @@ ldouble: 4 Function: "acosh_upward": double: 1 idouble: 1 -ildouble: 4 +ildouble: 5 ldouble: 3 Function: "asin": @@ -900,8 +900,8 @@ double: 3 float: 3 idouble: 3 ifloat: 3 -ildouble: 7 -ldouble: 7 +ildouble: 8 +ldouble: 8 Function: Imaginary part of "clog10_upward": double: 1 @@ -1591,8 +1591,8 @@ double: 3 float: 4 idouble: 3 ifloat: 4 -ildouble: 5 -ldouble: 5 +ildouble: 6 +ldouble: 6 Function: "hypot": double: 1 @@ -1743,8 +1743,8 @@ double: 3 float: 4 idouble: 3 ifloat: 4 -ildouble: 5 -ldouble: 5 +ildouble: 6 +ldouble: 6 Function: "log": double: 1 diff --git a/sysdeps/i386/i686/multiarch/memchr-sse2.S b/sysdeps/i386/i686/multiarch/memchr-sse2.S index 910679cfc0..e41f324a77 100644 --- a/sysdeps/i386/i686/multiarch/memchr-sse2.S +++ b/sysdeps/i386/i686/multiarch/memchr-sse2.S @@ -117,7 +117,6 @@ L(crosscache): # ifndef USE_AS_RAWMEMCHR jnz L(match_case2_prolog1) - lea -16(%edx), %edx /* Calculate the last acceptable address and check for possible addition overflow by using satured math: edx = ecx + edx @@ -125,6 +124,7 @@ L(crosscache): add %ecx, %edx sbb %eax, %eax or %eax, %edx + sub $16, %edx jbe L(return_null) lea 16(%edi), %edi # else diff --git a/sysdeps/i386/i686/multiarch/strcspn-c.c b/sysdeps/i386/i686/multiarch/strcspn-c.c index 6d61e190a8..ec230fb383 100644 --- a/sysdeps/i386/i686/multiarch/strcspn-c.c +++ b/sysdeps/i386/i686/multiarch/strcspn-c.c @@ -1,2 +1,4 @@ -#define __strcspn_sse2 __strcspn_ia32 -#include +#if IS_IN (libc) +# define __strcspn_sse2 __strcspn_ia32 +# include +#endif diff --git a/sysdeps/i386/i686/multiarch/varshift.c b/sysdeps/i386/i686/multiarch/varshift.c index 7760b966e2..6742a35d41 100644 --- a/sysdeps/i386/i686/multiarch/varshift.c +++ b/sysdeps/i386/i686/multiarch/varshift.c @@ -1 +1,3 @@ -#include +#if IS_IN (libc) +# include +#endif diff --git a/sysdeps/mips/bits/long-double.h b/sysdeps/mips/bits/long-double.h deleted file mode 100644 index 604188e181..0000000000 --- a/sysdeps/mips/bits/long-double.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Properties of long double type. MIPS version. - Copyright (C) 2016-2017 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 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 - -#if !defined __NO_LONG_DOUBLE_MATH && _MIPS_SIM == _ABIO32 -# define __NO_LONG_DOUBLE_MATH 1 -#endif diff --git a/sysdeps/mips/ieee754/bits/long-double.h b/sysdeps/mips/ieee754/bits/long-double.h new file mode 100644 index 0000000000..604188e181 --- /dev/null +++ b/sysdeps/mips/ieee754/bits/long-double.h @@ -0,0 +1,23 @@ +/* Properties of long double type. MIPS version. + Copyright (C) 2016-2017 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 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 + +#if !defined __NO_LONG_DOUBLE_MATH && _MIPS_SIM == _ABIO32 +# define __NO_LONG_DOUBLE_MATH 1 +#endif diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c index db6d721fce..4bb87e2331 100644 --- a/sysdeps/nptl/fork.c +++ b/sysdeps/nptl/fork.c @@ -131,10 +131,6 @@ __libc_fork (void) call_function_static_weak (__malloc_fork_lock_parent); } -#ifndef NDEBUG - pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid); -#endif - #ifdef ARCH_FORK pid = ARCH_FORK (); #else @@ -147,8 +143,6 @@ __libc_fork (void) { struct pthread *self = THREAD_SELF; - assert (THREAD_GETMEM (self, tid) != ppid); - /* See __pthread_once. */ if (__fork_generation_pointer != NULL) *__fork_generation_pointer += __PTHREAD_ONCE_FORK_GEN_INCR; @@ -230,8 +224,6 @@ __libc_fork (void) } else { - assert (THREAD_GETMEM (THREAD_SELF, tid) == ppid); - /* Release acquired locks in the multi-threaded case. */ if (multiple_threads) { diff --git a/sysdeps/powerpc/power7/fpu/s_logbl.c b/sysdeps/powerpc/power7/fpu/s_logbl.c index f7ecbd105a..3ae383a831 100644 --- a/sysdeps/powerpc/power7/fpu/s_logbl.c +++ b/sysdeps/powerpc/power7/fpu/s_logbl.c @@ -35,14 +35,16 @@ static const union { long double __logbl (long double x) { - double xh; + double xh, xl; double ret; + int64_t hx; if (__builtin_expect (x == 0.0L, 0)) /* Raise FE_DIVBYZERO and return -HUGE_VAL[LF]. */ return -1.0L / __builtin_fabsl (x); - xh = ldbl_high (x); + ldbl_unpack (x, &xh, &xl); + EXTRACT_WORDS64 (hx, xh); /* ret = x & 0x7ff0000000000000; */ asm ( "xxland %x0,%x1,%x2\n" @@ -58,10 +60,20 @@ __logbl (long double x) { /* POSIX specifies that denormal number is treated as though it were normalized. */ - int64_t hx; - - EXTRACT_WORDS64 (hx, xh); - return (long double) (-1023 - (__builtin_clzll (hx) - 12)); + return (long double) (- (__builtin_clzll (hx & 0x7fffffffffffffffLL) \ + - 12) - 1023); + } + else if ((hx & 0x000fffffffffffffLL) == 0) + { + /* If the high part is a power of 2, and the low part is nonzero + with the opposite sign, the low part affects the + exponent. */ + int64_t lx, rhx; + EXTRACT_WORDS64 (lx, xl); + rhx = (hx & 0x7ff0000000000000LL) >> 52; + if ((hx ^ lx) < 0 && (lx & 0x7fffffffffffffffLL) != 0) + rhx--; + return (long double) (rhx - 1023); } /* Test to avoid logb_downward (0.0) == -0.0. */ return ret == -0.0 ? 0.0 : ret; diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h index 28eb50f92d..9b5a99fcc7 100644 --- a/sysdeps/powerpc/powerpc32/dl-machine.h +++ b/sysdeps/powerpc/powerpc32/dl-machine.h @@ -309,7 +309,10 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, against local symbols. */ if (__builtin_expect (ELF32_ST_BIND (sym->st_info) == STB_LOCAL, 0) && sym->st_shndx != SHN_UNDEF) - value = map->l_addr; + { + sym_map = map; + value = map->l_addr; + } else { sym_map = RESOLVE_MAP (&sym, version, r_type); diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h index cf7272f359..3e03fd091c 100644 --- a/sysdeps/sparc/sparc32/dl-machine.h +++ b/sysdeps/sparc/sparc32/dl-machine.h @@ -375,6 +375,7 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc, if (__builtin_expect (ELF32_ST_BIND (sym->st_info) == STB_LOCAL, 0) && sym->st_shndx != SHN_UNDEF) { + sym_map = map; value = map->l_addr; } else diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h index 99c00f493d..0694ac1362 100644 --- a/sysdeps/sparc/sparc64/dl-machine.h +++ b/sysdeps/sparc/sparc64/dl-machine.h @@ -402,6 +402,7 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc, if (__builtin_expect (ELF64_ST_BIND (sym->st_info) == STB_LOCAL, 0) && sym->st_shndx != SHN_UNDEF) { + sym_map = map; value = map->l_addr; } else diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index b3d68665f9..beb1c29111 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -141,7 +141,7 @@ endif ifeq ($(subdir),posix) sysdep_headers += bits/initspin.h -sysdep_routines += sched_getcpu +sysdep_routines += sched_getcpu oldglob tests += tst-affinity tst-affinity-pid diff --git a/sysdeps/unix/sysv/linux/alpha/glob.c b/sysdeps/unix/sysv/linux/alpha/glob.c index 2d7d287a25..1b813c1cfd 100644 --- a/sysdeps/unix/sysv/linux/alpha/glob.c +++ b/sysdeps/unix/sysv/linux/alpha/glob.c @@ -42,10 +42,6 @@ extern void __new_globfree (glob_t *__pglob); #undef globfree64 versioned_symbol (libc, __new_glob, glob, GLIBC_2_1); -versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1); libc_hidden_ver (__new_glob, glob) -libc_hidden_ver (__new_globfree, globfree) weak_alias (__new_glob, glob64) -weak_alias (__new_globfree, globfree64) -libc_hidden_ver (__new_globfree, globfree64) diff --git a/sysdeps/unix/sysv/linux/alpha/globfree.c b/sysdeps/unix/sysv/linux/alpha/globfree.c new file mode 100644 index 0000000000..98cf1c200b --- /dev/null +++ b/sysdeps/unix/sysv/linux/alpha/globfree.c @@ -0,0 +1,37 @@ +/* Compat globfree. Linux/alpha version. + Copyright (C) 2017 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 globfree64 __no_globfree64_decl +#include +#include +#include + +#define globfree(pglob) \ + __new_globfree (pglob) + +extern void __new_globfree (glob_t *__pglob); + +#include + +#undef globfree64 + +versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1); +libc_hidden_ver (__new_globfree, globfree) + +weak_alias (__new_globfree, globfree64) +libc_hidden_ver (__new_globfree, globfree64) diff --git a/sysdeps/unix/sysv/linux/alpha/localplt.data b/sysdeps/unix/sysv/linux/alpha/localplt.data index cca17f1e34..1f0e3b494e 100644 --- a/sysdeps/unix/sysv/linux/alpha/localplt.data +++ b/sysdeps/unix/sysv/linux/alpha/localplt.data @@ -20,7 +20,7 @@ libc.so: free + RELA R_ALPHA_GLOB_DAT libc.so: malloc + RELA R_ALPHA_GLOB_DAT libc.so: memalign + RELA R_ALPHA_GLOB_DAT libc.so: realloc + RELA R_ALPHA_GLOB_DAT -libm.so: matherr +libm.so: matherr + RELA R_ALPHA_GLOB_DAT # We used to offer inline functions that used this, so it must be exported. # Ought to reorg things such that carg isn't thus forced to use a plt. libm.so: __atan2 diff --git a/sysdeps/unix/sysv/linux/hppa/bits/shm.h b/sysdeps/unix/sysv/linux/hppa/bits/shm.h index 794f0ab2da..495dae8afc 100644 --- a/sysdeps/unix/sysv/linux/hppa/bits/shm.h +++ b/sysdeps/unix/sysv/linux/hppa/bits/shm.h @@ -37,7 +37,7 @@ #define SHM_UNLOCK 12 /* unlock segment (root only) */ /* Segment low boundary address multiple. */ -#define SHMLBA 0x00400000 /* address needs to be 4 Mb aligned */ +#define SHMLBA 0x1000 /* Type to count number of attaches. */ typedef unsigned long int shmatt_t; diff --git a/sysdeps/unix/sysv/linux/hppa/clone.S b/sysdeps/unix/sysv/linux/hppa/clone.S index d36b302199..f6b4e3eea8 100644 --- a/sysdeps/unix/sysv/linux/hppa/clone.S +++ b/sysdeps/unix/sysv/linux/hppa/clone.S @@ -64,9 +64,12 @@ ENTRY(__clone) /* Prologue */ stwm %r4, 64(%sp) + .cfi_def_cfa_offset -64 + .cfi_offset 4, 0 stw %sp, -4(%sp) #ifdef PIC stw %r19, -32(%sp) + .cfi_offset 19, 32 #endif /* Sanity check arguments. */ @@ -147,9 +150,9 @@ ENTRY(__clone) #ifdef PIC copy %r4, %r19 #endif - /* The call to _exit needs saved r19. */ - bl _exit, %rp - copy %ret0, %arg0 + copy %r28, %r26 + ble 0x100(%sr2, %r0) + ldi __NR_exit, %r20 /* We should not return from _exit. We do not restore r4, or the stack state. */ diff --git a/sysdeps/unix/sysv/linux/hppa/getcontext.S b/sysdeps/unix/sysv/linux/hppa/getcontext.S index 6f52f2149d..68a74a0b7e 100644 --- a/sysdeps/unix/sysv/linux/hppa/getcontext.S +++ b/sysdeps/unix/sysv/linux/hppa/getcontext.S @@ -130,8 +130,11 @@ ENTRY(__getcontext) /* Prologue */ stwm %r4, 64(%sp) + .cfi_def_cfa_offset -64 + .cfi_offset 4, 0 #ifdef PIC stw %r19, -32(%sp) + .cfi_offset 19, 32 #endif /* Set up the trampoline registers. @@ -156,7 +159,7 @@ ENTRY(__getcontext) /* Epilogue */ ldw -84(%sp), %r2 #ifdef PIC - ldw -96(%sp), %r19 + ldw -32(%sp), %r19 #endif bv %r0(%r2) ldwm -64(%sp), %r4 diff --git a/sysdeps/unix/sysv/linux/hppa/internaltypes.h b/sysdeps/unix/sysv/linux/hppa/internaltypes.h deleted file mode 100644 index d6496579da..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/internaltypes.h +++ /dev/null @@ -1,84 +0,0 @@ -#include_next -#ifndef _INTERNAL_TYPES_H_HPPA_ -#define _INTERNAL_TYPES_H_HPPA_ 1 -#include - -/* In GLIBC 2.10 HPPA switched from Linuxthreads to NPTL, and in order -to maintain ABI compatibility with pthread_cond_t, some care had to be -taken. - -The NPTL pthread_cond_t grew in size. When HPPA switched to NPTL, we -dropped the use of ldcw, and switched to the kernel helper routine for -compare-and-swap. This allowed HPPA to use the 4-word 16-byte aligned -lock words, and alignment words to store the additional pthread_cond_t -data. Once organized properly the new NPTL pthread_cond_t was 1 word -smaller than the Linuxthreads version. - -However, we were faced with the case that users may have initialized the -pthread_cond_t with PTHREAD_COND_INITIALIZER. In this case, the first -four words were set to one, and must be cleared before any NPTL code -used these words. - -We didn't want to use LDCW, because it continues to be a source of bugs -when applications memset pthread_cond_t to all zeroes by accident. This -works on all other architectures where lock words are unlocked at zero. -Remember that because of the semantics of LDCW, a locked word is set to -zero, and an unlocked word is set to 1. - -Instead we used atomic_compare_and_exchange_val_acq, but we couldn't use -this on any of the pthread_cond_t words, otherwise it might interfere -with the current operation of the structure. To solve this problem we -used the left over word. - -If the stucture was initialized by a legacy Linuxthread -PTHREAD_COND_INITIALIZER it contained a 1, and this indicates that the -structure requires zeroing for NPTL. The first thread to come upon a -pthread_cond_t with a 1 in the __initializer field, will -compare-and-swap the value, placing a 2 there which will cause all other -threads using the same pthread_cond_t to wait for the completion of the -initialization. Lastly, we use a store (with memory barrier) to change -__initializer from 2 to 0. Note that the store is strongly ordered, but -we use the PA 1.1 compatible form which is ",ma" with zero offset. - -In the future, when the application is recompiled with NPTL -PTHREAD_COND_INITIALIZER it will be a quick compare-and-swap, which -fails because __initializer is zero, and the structure will be used as -is correctly. */ - -#define cond_compat_clear(var) \ -({ \ - int tmp = 0; \ - var->__data.__wseq = 0; \ - var->__data.__signals_sent = 0; \ - var->__data.__confirmed = 0; \ - var->__data.__generation = 0; \ - var->__data.__mutex = NULL; \ - var->__data.__quiescence_waiters = 0; \ - var->__data.__clockid = 0; \ - /* Clear __initializer last, to indicate initialization is done. */ \ - /* This synchronizes-with the acquire load below. */ \ - atomic_store_release (&var->__data.__initializer, 0); \ -}) - -#define cond_compat_check_and_clear(var) \ -({ \ - int v; \ - int *value = &var->__data.__initializer; \ - /* This synchronizes-with the release store above. */ \ - while ((v = atomic_load_acquire (value)) != 0) \ - { \ - if (v == 1 \ - /* Relaxed MO is fine; it only matters who's first. */ \ - && atomic_compare_exchange_acquire_weak_relaxed (value, 1, 2)) \ - { \ - /* We're first; initialize structure. */ \ - cond_compat_clear (var); \ - break; \ - } \ - else \ - /* Yield before we re-check initialization status. */ \ - sched_yield (); \ - } \ -}) - -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/ipc_priv.h b/sysdeps/unix/sysv/linux/hppa/ipc_priv.h new file mode 100644 index 0000000000..d880f5029a --- /dev/null +++ b/sysdeps/unix/sysv/linux/hppa/ipc_priv.h @@ -0,0 +1,21 @@ +/* Old SysV permission definition for Linux. Hppa version. + Copyright (C) 2017 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 /* For __key_t */ + +#define __IPC_64 0x0 diff --git a/sysdeps/unix/sysv/linux/hppa/localplt.data b/sysdeps/unix/sysv/linux/hppa/localplt.data index 9dd81b47c8..db9e24b090 100644 --- a/sysdeps/unix/sysv/linux/hppa/localplt.data +++ b/sysdeps/unix/sysv/linux/hppa/localplt.data @@ -6,7 +6,6 @@ libc.so: free libc.so: malloc libc.so: memalign libc.so: realloc -libc.so: _exit libc.so: __sigsetjmp libc.so: _IO_funlockfile libc.so: sigprocmask diff --git a/sysdeps/unix/sysv/linux/hppa/pt-vfork.S b/sysdeps/unix/sysv/linux/hppa/pt-vfork.S index fc4573c86b..8b7d7df2fe 100644 --- a/sysdeps/unix/sysv/linux/hppa/pt-vfork.S +++ b/sysdeps/unix/sysv/linux/hppa/pt-vfork.S @@ -58,7 +58,10 @@ ENTRY(__vfork) that there is no child now, so it's safe to create a frame. */ stw %rp, -20(%sp) + .cfi_offset 2, -20 stwm %r3, 64(%sp) + .cfi_def_cfa_offset -64 + .cfi_offset 3, 0 stw %sp, -4(%sp) sub %r0,%ret0,%r3 diff --git a/sysdeps/unix/sysv/linux/hppa/pthread.h b/sysdeps/unix/sysv/linux/hppa/pthread.h index ac617201d2..806072cde4 100644 --- a/sysdeps/unix/sysv/linux/hppa/pthread.h +++ b/sysdeps/unix/sysv/linux/hppa/pthread.h @@ -26,6 +26,7 @@ #include #include #include +#include /* Detach state. */ @@ -82,32 +83,18 @@ enum #endif -#ifdef __PTHREAD_MUTEX_HAVE_PREV -# define PTHREAD_MUTEX_INITIALIZER \ - { { 0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 } } } -# ifdef __USE_GNU -# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __PTHREAD_SPINS, { 0, 0 } } } -# define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, __PTHREAD_SPINS, { 0, 0 } } } -# define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, __PTHREAD_SPINS, { 0, 0 } } } -# define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, __PTHREAD_SPINS, { 0, 0 } } } - -# endif -#else -# define PTHREAD_MUTEX_INITIALIZER \ - { { 0, 0, 0, 0, 0, { __PTHREAD_SPINS } } } -# ifdef __USE_GNU -# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, 0, { __PTHREAD_SPINS } } } -# define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, 0, { __PTHREAD_SPINS } } } -# define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, 0, { __PTHREAD_SPINS } } } - -# endif +#define PTHREAD_MUTEX_INITIALIZER \ + { { 0, 0, 0, 0, { 0, 0, 0, 0 }, 0, { __PTHREAD_SPINS }, 0, 0 } } +#ifdef __USE_GNU +# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ + { { 0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, { 0, 0, 0, 0 }, 0, \ + { __PTHREAD_SPINS }, 0, 0 } } +# define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \ + { { 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, { 0, 0, 0, 0 }, 0, \ + { __PTHREAD_SPINS }, 0, 0 } } +# define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \ + { { 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, { 0, 0, 0, 0 }, 0, \ + { __PTHREAD_SPINS }, 0, 0 } } #endif @@ -130,25 +117,14 @@ enum # endif #endif + /* Read-write lock initializers. */ # define PTHREAD_RWLOCK_INITIALIZER \ - { { 0, 0, 0, 0, 0, 0, 0, 0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0, 0 } } + { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } # ifdef __USE_GNU -# ifdef __PTHREAD_RWLOCK_INT_FLAGS_SHARED -# define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \ - { { 0, 0, 0, 0, 0, 0, 0, 0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0, \ - PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP } } -# else -# if __BYTE_ORDER == __LITTLE_ENDIAN -# define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \ - { { 0, 0, 0, 0, 0, 0, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, \ - 0, __PTHREAD_RWLOCK_ELISION_EXTRA, 0, 0 } } -# else -# define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \ - { { 0, 0, 0, 0, 0, 0, 0, 0, 0, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,\ - 0 } } -# endif -# endif +# define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \ + { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, 0, 0, 0 } } # endif #endif /* Unix98 or XOpen2K */ @@ -183,9 +159,8 @@ enum }; - /* Conditional variable handling. */ -#define PTHREAD_COND_INITIALIZER { { 0, 0, 0, 0, 0, (void *) 0, 0, 0 } } +#define PTHREAD_COND_INITIALIZER { { {0}, {0}, {0, 0}, {0, 0}, 0, 0, {0, 0} } } /* Cleanup buffers */ @@ -1161,43 +1136,3 @@ __NTH (pthread_equal (pthread_t __thread1, pthread_t __thread2)) __END_DECLS #endif /* pthread.h */ - -#ifndef _PTHREAD_H_HPPA_ -#define _PTHREAD_H_HPPA_ 1 - -/* The pthread_cond_t initializer is compatible only with NPTL. We do not - want to be forwards compatible, we eventually want to drop the code - that has to clear the old LT initializer. */ -#undef PTHREAD_COND_INITIALIZER -#define PTHREAD_COND_INITIALIZER { { 0, 0, 0, (void *) 0, 0, 0, 0, 0, 0 } } - -/* The pthread_mutex_t and pthread_rwlock_t initializers are compatible - only with NPTL. NPTL assumes pthread_rwlock_t is all zero. */ -#undef PTHREAD_MUTEX_INITIALIZER -#undef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -#undef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -#undef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP -/* Mutex initializers. */ -#define PTHREAD_MUTEX_INITIALIZER \ - { { 0, 0, 0, 0, { 0, 0, 0, 0 }, 0, { 0 }, 0, 0 } } -#ifdef __USE_GNU -# define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, { 0, 0, 0, 0 }, 0, { 0 }, 0, 0 } } -# define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, { 0, 0, 0, 0 }, 0, { 0 }, 0, 0 } } -# define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP \ - { { 0, 0, 0, PTHREAD_MUTEX_ADAPTIVE_NP, { 0, 0, 0, 0 }, 0, { 0 }, 0, 0 } } -#endif - -#undef PTHREAD_RWLOCK_INITIALIZER -#undef PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP -/* Read-write lock initializers. */ -#define PTHREAD_RWLOCK_INITIALIZER \ - { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } -#ifdef __USE_GNU -# define PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP \ - { { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,\ - 0, 0, 0 } } -#endif /* Unix98 or XOpen2K */ - -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/pthread_cond_broadcast.c b/sysdeps/unix/sysv/linux/hppa/pthread_cond_broadcast.c deleted file mode 100644 index a6f9f5d433..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/pthread_cond_broadcast.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Carlos O'Donell , 2009. - - 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 INCLUDED_SELF -# define INCLUDED_SELF -# include -#else -# include -# include -# include -# include -int -__pthread_cond_broadcast (pthread_cond_t *cond) -{ - cond_compat_check_and_clear (cond); - return __pthread_cond_broadcast_internal (cond); -} -versioned_symbol (libpthread, __pthread_cond_broadcast, pthread_cond_broadcast, - GLIBC_2_3_2); -# undef versioned_symbol -# define versioned_symbol(lib, local, symbol, version) -# undef __pthread_cond_broadcast -# define __pthread_cond_broadcast __pthread_cond_broadcast_internal -# include_next -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/pthread_cond_destroy.c b/sysdeps/unix/sysv/linux/hppa/pthread_cond_destroy.c deleted file mode 100644 index 49af087bb4..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/pthread_cond_destroy.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Carlos O'Donell , 2009. - - 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 INCLUDED_SELF -# define INCLUDED_SELF -# include -#else -# include -# include -# include -# include -int -__pthread_cond_destroy (pthread_cond_t *cond) -{ - cond_compat_check_and_clear (cond); - return __pthread_cond_destroy_internal (cond); -} -versioned_symbol (libpthread, __pthread_cond_destroy, pthread_cond_destroy, - GLIBC_2_3_2); -# undef versioned_symbol -# define versioned_symbol(lib, local, symbol, version) -# undef __pthread_cond_destroy -# define __pthread_cond_destroy __pthread_cond_destroy_internal -# include_next -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/pthread_cond_init.c b/sysdeps/unix/sysv/linux/hppa/pthread_cond_init.c deleted file mode 100644 index ccb3de07ff..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/pthread_cond_init.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Carlos O'Donell , 2009. - - 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 INCLUDED_SELF -# define INCLUDED_SELF -# include -#else -# include -# include -# include -# include -int -__pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *cond_attr) -{ - cond_compat_clear (cond); - return __pthread_cond_init_internal (cond, cond_attr); -} -versioned_symbol (libpthread, __pthread_cond_init, pthread_cond_init, - GLIBC_2_3_2); -# undef versioned_symbol -# define versioned_symbol(lib, local, symbol, version) -# undef __pthread_cond_init -# define __pthread_cond_init __pthread_cond_init_internal -# include_next -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/pthread_cond_signal.c b/sysdeps/unix/sysv/linux/hppa/pthread_cond_signal.c deleted file mode 100644 index 2bf32af933..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/pthread_cond_signal.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Carlos O'Donell , 2009. - - 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 INCLUDED_SELF -# define INCLUDED_SELF -# include -#else -# include -# include -# include -# include -int -__pthread_cond_signal (pthread_cond_t *cond) -{ - cond_compat_check_and_clear (cond); - return __pthread_cond_signal_internal (cond); -} -versioned_symbol (libpthread, __pthread_cond_signal, pthread_cond_signal, - GLIBC_2_3_2); -# undef versioned_symbol -# define versioned_symbol(lib, local, symbol, version) -# undef __pthread_cond_signal -# define __pthread_cond_signal __pthread_cond_signal_internal -# include_next -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/pthread_cond_wait.c b/sysdeps/unix/sysv/linux/hppa/pthread_cond_wait.c deleted file mode 100644 index 1cc2fc15d4..0000000000 --- a/sysdeps/unix/sysv/linux/hppa/pthread_cond_wait.c +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (C) 2009-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Carlos O'Donell , 2009. - - 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 INCLUDED_SELF -# define INCLUDED_SELF -# include -#else -# include -# include -# include -# include -int -__pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - cond_compat_check_and_clear (cond); - return __pthread_cond_wait_internal (cond, mutex); -} -versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait, - GLIBC_2_3_2); -int -__pthread_cond_timedwait (cond, mutex, abstime) - pthread_cond_t *cond; - pthread_mutex_t *mutex; - const struct timespec *abstime; -{ - cond_compat_check_and_clear (cond); - return __pthread_cond_timedwait_internal (cond, mutex, abstime); -} -versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait, - GLIBC_2_3_2); -# undef versioned_symbol -# define versioned_symbol(lib, local, symbol, version) -# undef __pthread_cond_wait -# define __pthread_cond_wait __pthread_cond_wait_internal -# undef __pthread_cond_timedwait -# define __pthread_cond_timedwait __pthread_cond_timedwait_internal -# include_next -#endif diff --git a/sysdeps/unix/sysv/linux/hppa/setcontext.S b/sysdeps/unix/sysv/linux/hppa/setcontext.S index 3f4da7938f..92cb204f8d 100644 --- a/sysdeps/unix/sysv/linux/hppa/setcontext.S +++ b/sysdeps/unix/sysv/linux/hppa/setcontext.S @@ -26,8 +26,11 @@ ENTRY(__setcontext) /* Prologue */ stwm %r3, 64(%sp) + .cfi_def_cfa_offset -64 + .cfi_offset 3, 0 #ifdef PIC stw %r19, -32(%sp) + .cfi_offset 19, 32 #endif /* Save ucp. */ @@ -141,7 +144,7 @@ ENTRY(__setcontext) /* No further context available. Exit now. */ bl HIDDEN_JUMPTARGET(exit), %r2 - ldi -1, %r26 + ldi 0, %r26 .Lerror: diff --git a/sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h b/sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h index 5ea297267f..8b7f2b2095 100644 --- a/sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h +++ b/sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h @@ -62,12 +62,11 @@ ENTRY (__##syscall_name##_nocancel) \ DOARGS_##args ASM_LINE_SEP \ stwm TREG, 64(%sp) ASM_LINE_SEP \ + .cfi_def_cfa_offset -64 ASM_LINE_SEP \ .cfi_offset TREG, 0 ASM_LINE_SEP \ - .cfi_adjust_cfa_offset 64 ASM_LINE_SEP \ stw %sp, -4(%sp) ASM_LINE_SEP \ - .cfi_offset 30, -4 ASM_LINE_SEP \ stw %r19, -32(%sp) ASM_LINE_SEP \ - .cfi_offset 19, -32 ASM_LINE_SEP \ + .cfi_offset 19, 32 ASM_LINE_SEP \ /* Save r19 */ ASM_LINE_SEP \ SAVE_PIC(TREG) ASM_LINE_SEP \ /* Do syscall, delay loads # */ ASM_LINE_SEP \ @@ -91,21 +90,19 @@ L(pre_nc_end): ASM_LINE_SEP \ /* No need to LOAD_PIC */ ASM_LINE_SEP \ /* Undo frame */ ASM_LINE_SEP \ ldwm -64(%sp),TREG ASM_LINE_SEP \ - .cfi_adjust_cfa_offset -64 ASM_LINE_SEP \ /* Restore rp before exit */ ASM_LINE_SEP \ ldw -20(%sp), %rp ASM_LINE_SEP \ - .cfi_restore 2 ASM_LINE_SEP \ ret ASM_LINE_SEP \ END(__##syscall_name##_nocancel) ASM_LINE_SEP \ /**********************************************/ASM_LINE_SEP \ ENTRY (name) \ DOARGS_##args ASM_LINE_SEP \ stwm TREG, 64(%sp) ASM_LINE_SEP \ - .cfi_adjust_cfa_offset 64 ASM_LINE_SEP \ + .cfi_def_cfa_offset -64 ASM_LINE_SEP \ + .cfi_offset TREG, 0 ASM_LINE_SEP \ stw %sp, -4(%sp) ASM_LINE_SEP \ - .cfi_offset 30, -4 ASM_LINE_SEP \ stw %r19, -32(%sp) ASM_LINE_SEP \ - .cfi_offset 19, -32 ASM_LINE_SEP \ + .cfi_offset 19, 32 ASM_LINE_SEP \ /* Done setting up frame, continue... */ ASM_LINE_SEP \ SINGLE_THREAD_P ASM_LINE_SEP \ cmpib,<>,n 0,%ret0,L(pseudo_cancel) ASM_LINE_SEP \ @@ -168,40 +165,32 @@ L(pre_end): ASM_LINE_SEP \ /* No need to LOAD_PIC */ ASM_LINE_SEP \ /* Undo frame */ ASM_LINE_SEP \ ldwm -64(%sp),TREG ASM_LINE_SEP \ - .cfi_adjust_cfa_offset -64 ASM_LINE_SEP \ /* Restore rp before exit */ ASM_LINE_SEP \ - ldw -20(%sp), %rp ASM_LINE_SEP \ - .cfi_restore 2 ASM_LINE_SEP + ldw -20(%sp), %rp ASM_LINE_SEP /* Save arguments into our frame */ # define PUSHARGS_0 /* nothing to do */ # define PUSHARGS_1 PUSHARGS_0 stw %r26, -36(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 26, -36 ASM_LINE_SEP + .cfi_offset 26, 28 ASM_LINE_SEP # define PUSHARGS_2 PUSHARGS_1 stw %r25, -40(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 25, -40 ASM_LINE_SEP + .cfi_offset 25, 24 ASM_LINE_SEP # define PUSHARGS_3 PUSHARGS_2 stw %r24, -44(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 24, -44 ASM_LINE_SEP + .cfi_offset 24, 20 ASM_LINE_SEP # define PUSHARGS_4 PUSHARGS_3 stw %r23, -48(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 23, -48 ASM_LINE_SEP + .cfi_offset 23, 16 ASM_LINE_SEP # define PUSHARGS_5 PUSHARGS_4 stw %r22, -52(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 22, -52 ASM_LINE_SEP + .cfi_offset 22, 12 ASM_LINE_SEP # define PUSHARGS_6 PUSHARGS_5 stw %r21, -56(%sr0,%sp) ASM_LINE_SEP \ - .cfi_offset 21, -56 ASM_LINE_SEP + .cfi_offset 21, 8 ASM_LINE_SEP /* Bring them back from the stack */ # define POPARGS_0 /* nothing to do */ -# define POPARGS_1 POPARGS_0 ldw -36(%sr0,%sp), %r26 ASM_LINE_SEP \ - .cfi_restore 26 ASM_LINE_SEP -# define POPARGS_2 POPARGS_1 ldw -40(%sr0,%sp), %r25 ASM_LINE_SEP \ - .cfi_restore 25 ASM_LINE_SEP -# define POPARGS_3 POPARGS_2 ldw -44(%sr0,%sp), %r24 ASM_LINE_SEP \ - .cfi_restore 24 ASM_LINE_SEP -# define POPARGS_4 POPARGS_3 ldw -48(%sr0,%sp), %r23 ASM_LINE_SEP \ - .cfi_restore 23 ASM_LINE_SEP -# define POPARGS_5 POPARGS_4 ldw -52(%sr0,%sp), %r22 ASM_LINE_SEP \ - .cfi_restore 22 ASM_LINE_SEP -# define POPARGS_6 POPARGS_5 ldw -56(%sr0,%sp), %r21 ASM_LINE_SEP \ - .cfi_restore 21 ASM_LINE_SEP +# define POPARGS_1 POPARGS_0 ldw -36(%sr0,%sp), %r26 ASM_LINE_SEP +# define POPARGS_2 POPARGS_1 ldw -40(%sr0,%sp), %r25 ASM_LINE_SEP +# define POPARGS_3 POPARGS_2 ldw -44(%sr0,%sp), %r24 ASM_LINE_SEP +# define POPARGS_4 POPARGS_3 ldw -48(%sr0,%sp), %r23 ASM_LINE_SEP +# define POPARGS_5 POPARGS_4 ldw -52(%sr0,%sp), %r22 ASM_LINE_SEP +# define POPARGS_6 POPARGS_5 ldw -56(%sr0,%sp), %r21 ASM_LINE_SEP # if IS_IN (libpthread) # ifdef PIC diff --git a/sysdeps/unix/sysv/linux/hppa/sysdep.h b/sysdeps/unix/sysv/linux/hppa/sysdep.h index d8dd0431a4..c0cd59e9f5 100644 --- a/sysdeps/unix/sysv/linux/hppa/sysdep.h +++ b/sysdeps/unix/sysv/linux/hppa/sysdep.h @@ -49,11 +49,9 @@ to another function */ #define TREG 4 #define SAVE_PIC(SREG) \ - copy %r19, SREG ASM_LINE_SEP \ - .cfi_register 19, SREG + copy %r19, SREG #define LOAD_PIC(LREG) \ - copy LREG , %r19 ASM_LINE_SEP \ - .cfi_restore 19 + copy LREG , %r19 /* Inline assembly defines */ #define TREG_ASM "%r4" /* Cant clobber r3, it holds framemarker */ #define SAVE_ASM_PIC " copy %%r19, %" TREG_ASM "\n" @@ -292,12 +290,11 @@ #define DO_CALL(syscall_name, args) \ /* Create a frame */ ASM_LINE_SEP \ stwm TREG, 64(%sp) ASM_LINE_SEP \ + .cfi_def_cfa_offset -64 ASM_LINE_SEP \ .cfi_offset TREG, 0 ASM_LINE_SEP \ - .cfi_adjust_cfa_offset 64 ASM_LINE_SEP \ stw %sp, -4(%sp) ASM_LINE_SEP \ - .cfi_offset 30, -4 ASM_LINE_SEP \ stw %r19, -32(%sp) ASM_LINE_SEP \ - .cfi_offset 19, -32 ASM_LINE_SEP \ + .cfi_offset 19, 32 ASM_LINE_SEP \ /* Save r19 */ ASM_LINE_SEP \ SAVE_PIC(TREG) ASM_LINE_SEP \ /* Do syscall, delay loads # */ ASM_LINE_SEP \ @@ -320,10 +317,8 @@ L(pre_end): ASM_LINE_SEP \ /* Restore our frame, restoring TREG */ ASM_LINE_SEP \ ldwm -64(%sp), TREG ASM_LINE_SEP \ - .cfi_adjust_cfa_offset -64 ASM_LINE_SEP \ /* Restore return pointer */ ASM_LINE_SEP \ - ldw -20(%sp),%rp ASM_LINE_SEP \ - .cfi_restore 2 ASM_LINE_SEP + ldw -20(%sp),%rp ASM_LINE_SEP /* We do nothing with the return, except hand it back to someone else */ #undef DO_CALL_NOERRNO diff --git a/sysdeps/unix/sysv/linux/i386/glob64.c b/sysdeps/unix/sysv/linux/i386/glob64.c index f68195137e..230f9fc037 100644 --- a/sysdeps/unix/sysv/linux/i386/glob64.c +++ b/sysdeps/unix/sysv/linux/i386/glob64.c @@ -19,6 +19,7 @@ #include #include #include +#include #define dirent dirent64 #define __readdir(dirp) __readdir64 (dirp) @@ -33,44 +34,9 @@ #undef __stat #define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) -#define NO_GLOB_PATTERN_P 1 - #define COMPILE_GLOB64 1 #include -#include "shlib-compat.h" - -libc_hidden_def (globfree64) - versioned_symbol (libc, __glob64, glob64, GLIBC_2_2); libc_hidden_ver (__glob64, glob64) - -#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) - -#include - -int __old_glob64 (const char *__pattern, int __flags, - int (*__errfunc) (const char *, int), - glob64_t *__pglob); - -#undef dirent -#define dirent __old_dirent64 -#undef GL_READDIR -# define GL_READDIR(pglob, stream) \ - ((struct __old_dirent64 *) (pglob)->gl_readdir (stream)) -#undef __readdir -#define __readdir(dirp) __old_readdir64 (dirp) -#undef glob -#define glob(pattern, flags, errfunc, pglob) \ - __old_glob64 (pattern, flags, errfunc, pglob) -#define convert_dirent __old_convert_dirent -#define glob_in_dir __old_glob_in_dir -#define GLOB_ATTRIBUTE attribute_compat_text_section - -#define GLOB_ONLY_P 1 - -#include - -compat_symbol (libc, __old_glob64, glob64, GLIBC_2_1); -#endif diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data index 2c2584956d..8ea4333846 100644 --- a/sysdeps/unix/sysv/linux/i386/localplt.data +++ b/sysdeps/unix/sysv/linux/i386/localplt.data @@ -6,7 +6,7 @@ libc.so: free + REL R_386_GLOB_DAT libc.so: malloc + REL R_386_GLOB_DAT libc.so: memalign + REL R_386_GLOB_DAT libc.so: realloc + REL R_386_GLOB_DAT -libm.so: matherr +libm.so: matherr + REL R_386_GLOB_DAT # The main malloc is interposed into the dynamic linker, for # allocations after the initial link (when dlopen is used). ld.so: malloc + REL R_386_GLOB_DAT diff --git a/sysdeps/unix/sysv/linux/ia64/ipc_priv.h b/sysdeps/unix/sysv/linux/ia64/ipc_priv.h new file mode 100644 index 0000000000..e602eea455 --- /dev/null +++ b/sysdeps/unix/sysv/linux/ia64/ipc_priv.h @@ -0,0 +1,21 @@ +/* Old SysV permission definition for Linux. IA64 version. + Copyright (C) 2017 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 /* For __key_t */ + +#define __IPC_64 0x0 diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c new file mode 100644 index 0000000000..abc35fdd2b --- /dev/null +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c @@ -0,0 +1 @@ +/* glob64 is in globfree64.c */ diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c index 9776ee96e5..5a6379613b 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c @@ -29,5 +29,7 @@ _strong_alias (__posix_fadvise64_l64, __posix_fadvise64_l32); compat_symbol (libc, __posix_fadvise64_l32, posix_fadvise64, GLIBC_2_2); versioned_symbol (libc, __posix_fadvise64_l64, posix_fadvise64, GLIBC_2_3_3); +#else +_weak_alias (posix_fadvise, posix_fadvise64); #endif _strong_alias (__posix_fadvise64_l64, posix_fadvise); diff --git a/sysdeps/unix/sysv/linux/oldglob.c b/sysdeps/unix/sysv/linux/oldglob.c new file mode 100644 index 0000000000..8233e57ce9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/oldglob.c @@ -0,0 +1,42 @@ +#include + +#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) + +#include +#include +#include + +#include + +int __old_glob64 (const char *__pattern, int __flags, + int (*__errfunc) (const char *, int), + glob64_t *__pglob); +libc_hidden_proto (__old_glob64); + +#define dirent __old_dirent64 +#define GL_READDIR(pglob, stream) \ + ((struct __old_dirent64 *) (pglob)->gl_readdir (stream)) +#undef __readdir +#define __readdir(dirp) __old_readdir64 (dirp) + +#define glob_t glob64_t +#define glob(pattern, flags, errfunc, pglob) \ + __old_glob64 (pattern, flags, errfunc, pglob) +#define globfree(pglob) globfree64(pglob) + +#define convert_dirent __old_convert_dirent +#define glob_in_dir __old_glob_in_dir + +#undef stat +#define stat stat64 +#undef __stat +#define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) + +#define GLOB_ATTRIBUTE attribute_compat_text_section + +#include + +libc_hidden_def (__old_glob64); + +compat_symbol (libc, __old_glob64, glob64, GLIBC_2_1); +#endif diff --git a/sysdeps/unix/sysv/linux/s390/pt-longjmp.c b/sysdeps/unix/sysv/linux/s390/pt-longjmp.c index d324237edd..0221ac2cf5 100644 --- a/sysdeps/unix/sysv/linux/s390/pt-longjmp.c +++ b/sysdeps/unix/sysv/linux/s390/pt-longjmp.c @@ -26,8 +26,8 @@ /* In glibc release 2.19 new versions of longjmp-functions were introduced, but were reverted before 2.20. Thus both versions are the same function. */ -strong_alias (longjmp_ifunc, __v2longjmp) +strong_alias (longjmp_alias, __v2longjmp) compat_symbol (libpthread, __v2longjmp, longjmp, GLIBC_2_19); -strong_alias (siglongjmp_ifunc, __v2siglongjmp) +strong_alias (siglongjmp_alias, __v2siglongjmp) compat_symbol (libpthread, __v2siglongjmp, siglongjmp, GLIBC_2_19); #endif /* SHLIB_COMPAT (libpthread, GLIBC_2_19, GLIBC_2_20)) */ diff --git a/sysdeps/unix/sysv/linux/sparc/bits/long-double.h b/sysdeps/unix/sysv/linux/sparc/bits/long-double.h deleted file mode 100644 index 094e05124b..0000000000 --- a/sysdeps/unix/sysv/linux/sparc/bits/long-double.h +++ /dev/null @@ -1,26 +0,0 @@ -/* Properties of long double type. SPARC version. - Copyright (C) 2016-2017 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 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 - -#if !defined __NO_LONG_DOUBLE_MATH && __WORDSIZE == 32 -# define __LONG_DOUBLE_MATH_OPTIONAL 1 -# ifndef __LONG_DOUBLE_128__ -# define __NO_LONG_DOUBLE_MATH 1 -# endif -#endif diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/bits/long-double.h b/sysdeps/unix/sysv/linux/sparc/sparc32/bits/long-double.h new file mode 100644 index 0000000000..094e05124b --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/bits/long-double.h @@ -0,0 +1,26 @@ +/* Properties of long double type. SPARC version. + Copyright (C) 2016-2017 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 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 + +#if !defined __NO_LONG_DOUBLE_MATH && __WORDSIZE == 32 +# define __LONG_DOUBLE_MATH_OPTIONAL 1 +# ifndef __LONG_DOUBLE_128__ +# define __NO_LONG_DOUBLE_MATH 1 +# endif +#endif diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/bits/long-double.h b/sysdeps/unix/sysv/linux/sparc/sparc64/bits/long-double.h new file mode 100644 index 0000000000..094e05124b --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/bits/long-double.h @@ -0,0 +1,26 @@ +/* Properties of long double type. SPARC version. + Copyright (C) 2016-2017 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 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 + +#if !defined __NO_LONG_DOUBLE_MATH && __WORDSIZE == 32 +# define __LONG_DOUBLE_MATH_OPTIONAL 1 +# ifndef __LONG_DOUBLE_128__ +# define __NO_LONG_DOUBLE_MATH 1 +# endif +#endif diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c index 2daf0c5ef0..ee09fb762b 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -17,7 +17,6 @@ . */ #include -#include #include #include #include @@ -61,17 +60,18 @@ #define SPAWN_ERROR 127 #ifdef __ia64__ -# define CLONE(__fn, __stack, __stacksize, __flags, __args) \ - __clone2 (__fn, __stack, __stacksize, __flags, __args, 0, 0, 0) +# define CLONE(__fn, __stackbase, __stacksize, __flags, __args) \ + __clone2 (__fn, __stackbase, __stacksize, __flags, __args, 0, 0, 0) #else # define CLONE(__fn, __stack, __stacksize, __flags, __args) \ __clone (__fn, __stack, __flags, __args) #endif -#if _STACK_GROWS_DOWN -# define STACK(__stack, __stack_size) (__stack + __stack_size) -#elif _STACK_GROWS_UP +/* Since ia64 wants the stackbase w/clone2, re-use the grows-up macro. */ +#if _STACK_GROWS_UP || defined (__ia64__) # define STACK(__stack, __stack_size) (__stack) +#elif _STACK_GROWS_DOWN +# define STACK(__stack, __stack_size) (__stack + __stack_size) #endif @@ -265,7 +265,6 @@ __spawni_child (void *arguments) __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); - args->err = 0; args->exec (args->file, args->argv, args->envp); /* This is compatibility function required to enable posix_spawn run @@ -318,6 +317,11 @@ __spawnix (pid_t * pid, const char *file, /* Add a slack area for child's stack. */ size_t argv_size = (argc * sizeof (void *)) + 512; + /* We need at least a few pages in case the compiler's stack checking is + enabled. In some configs, it is known to use at least 24KiB. We use + 32KiB to be "safe" from anything the compiler might do. Besides, the + extra pages won't actually be allocated unless they get used. */ + argv_size += (32 * 1024); size_t stack_size = ALIGN_UP (argv_size, GLRO(dl_pagesize)); void *stack = __mmap (NULL, stack_size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); @@ -331,7 +335,7 @@ __spawnix (pid_t * pid, const char *file, /* Child must set args.err to something non-negative - we rely on the parent and child sharing VM. */ - args.err = -1; + args.err = 0; args.file = file; args.exec = exec; args.fa = file_actions; @@ -354,12 +358,26 @@ __spawnix (pid_t * pid, const char *file, new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size, CLONE_VM | CLONE_VFORK | SIGCHLD, &args); + /* It needs to collect the case where the auxiliary process was created + but failed to execute the file (due either any preparation step or + for execve itself). */ if (new_pid > 0) { + /* Also, it handles the unlikely case where the auxiliary process was + terminated before calling execve as if it was successfully. The + args.err is set to 0 as default and changed to a positive value + only in case of failure, so in case of premature termination + due a signal args.err will remain zeroed and it will be up to + caller to actually collect it. */ ec = args.err; - assert (ec >= 0); - if (ec != 0) - __waitpid (new_pid, NULL, 0); + if (ec > 0) + /* There still an unlikely case where the child is cancelled after + setting args.err, due to a positive error value. Also there is + possible pid reuse race (where the kernel allocated the same pid + to an unrelated process). Unfortunately due synchronization + issues where the kernel might not have the process collected + the waitpid below can not use WNOHANG. */ + __waitpid (new_pid, NULL, 0); } else ec = -new_pid; diff --git a/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c b/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c new file mode 100644 index 0000000000..af035e1514 --- /dev/null +++ b/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c @@ -0,0 +1,2 @@ +/* This file is here so sysdeps/gnu/glob64.c doesn't take precedence. */ +#include diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c b/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c new file mode 100644 index 0000000000..b76a761c17 --- /dev/null +++ b/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/wordsize-64/glob.c b/sysdeps/wordsize-64/glob.c index 082faf1c70..954e8d37e2 100644 --- a/sysdeps/wordsize-64/glob.c +++ b/sysdeps/wordsize-64/glob.c @@ -4,5 +4,3 @@ #undef glob64 #undef globfree64 weak_alias (glob, glob64) -weak_alias (globfree, globfree64) -libc_hidden_ver (globfree, globfree64) diff --git a/sysdeps/wordsize-64/globfree.c b/sysdeps/wordsize-64/globfree.c new file mode 100644 index 0000000000..ec8c35b489 --- /dev/null +++ b/sysdeps/wordsize-64/globfree.c @@ -0,0 +1,5 @@ +#define globfree64 __no_globfree64_decl +#include +#undef globfree64 +weak_alias (globfree, globfree64) +libc_hidden_ver (globfree, globfree64) diff --git a/sysdeps/wordsize-64/globfree64.c b/sysdeps/wordsize-64/globfree64.c new file mode 100644 index 0000000000..a0f57ff4b3 --- /dev/null +++ b/sysdeps/wordsize-64/globfree64.c @@ -0,0 +1 @@ +/* globfree64 is in globfree.c */ diff --git a/sysdeps/x86/cpu-features-offsets.sym b/sysdeps/x86/cpu-features-offsets.sym index f6739fae81..33dd094e37 100644 --- a/sysdeps/x86/cpu-features-offsets.sym +++ b/sysdeps/x86/cpu-features-offsets.sym @@ -15,6 +15,7 @@ CPUID_ECX_OFFSET offsetof (struct cpuid_registers, ecx) CPUID_EDX_OFFSET offsetof (struct cpuid_registers, edx) FAMILY_OFFSET offsetof (struct cpu_features, family) MODEL_OFFSET offsetof (struct cpu_features, model) +XSAVE_STATE_SIZE_OFFSET offsetof (struct cpu_features, xsave_state_size) FEATURE_OFFSET offsetof (struct cpu_features, feature) FEATURE_SIZE sizeof (unsigned int) diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c index 1c714a4017..38012912be 100644 --- a/sysdeps/x86/cpu-features.c +++ b/sysdeps/x86/cpu-features.c @@ -18,6 +18,7 @@ #include #include +#include static void get_common_indeces (struct cpu_features *cpu_features, @@ -93,6 +94,71 @@ get_common_indeces (struct cpu_features *cpu_features, } } } + + /* For _dl_runtime_resolve, set xsave_state_size to xsave area + size + integer register save size and align it to 64 bytes. */ + if (cpu_features->max_cpuid >= 0xd) + { + unsigned int eax, ebx, ecx, edx; + + __cpuid_count (0xd, 0, eax, ebx, ecx, edx); + if (ebx != 0) + { + cpu_features->xsave_state_size + = ALIGN_UP (ebx + STATE_SAVE_OFFSET, 64); + + __cpuid_count (0xd, 1, eax, ebx, ecx, edx); + + /* Check if XSAVEC is available. */ + if ((eax & (1 << 1)) != 0) + { + unsigned int xstate_comp_offsets[32]; + unsigned int xstate_comp_sizes[32]; + unsigned int i; + + xstate_comp_offsets[0] = 0; + xstate_comp_offsets[1] = 160; + xstate_comp_offsets[2] = 576; + xstate_comp_sizes[0] = 160; + xstate_comp_sizes[1] = 256; + + for (i = 2; i < 32; i++) + { + if ((STATE_SAVE_MASK & (1 << i)) != 0) + { + __cpuid_count (0xd, i, eax, ebx, ecx, edx); + xstate_comp_sizes[i] = eax; + } + else + { + ecx = 0; + xstate_comp_sizes[i] = 0; + } + + if (i > 2) + { + xstate_comp_offsets[i] + = (xstate_comp_offsets[i - 1] + + xstate_comp_sizes[i -1]); + if ((ecx & (1 << 1)) != 0) + xstate_comp_offsets[i] + = ALIGN_UP (xstate_comp_offsets[i], 64); + } + } + + /* Use XSAVEC. */ + unsigned int size + = xstate_comp_offsets[31] + xstate_comp_sizes[31]; + if (size) + { + cpu_features->xsave_state_size + = ALIGN_UP (size + STATE_SAVE_OFFSET, 64); + cpu_features->feature[index_arch_XSAVEC_Usable] + |= bit_arch_XSAVEC_Usable; + } + } + } + } } } @@ -139,8 +205,6 @@ init_cpu_features (struct cpu_features *cpu_features) case 0x57: /* Knights Landing. Enable Silvermont optimizations. */ - cpu_features->feature[index_arch_Prefer_No_VZEROUPPER] - |= bit_arch_Prefer_No_VZEROUPPER; case 0x5c: case 0x5f: @@ -226,19 +290,15 @@ init_cpu_features (struct cpu_features *cpu_features) cpu_features->feature[index_arch_AVX_Fast_Unaligned_Load] |= bit_arch_AVX_Fast_Unaligned_Load; - /* To avoid SSE transition penalty, use _dl_runtime_resolve_slow. - If XGETBV suports ECX == 1, use _dl_runtime_resolve_opt. */ - cpu_features->feature[index_arch_Use_dl_runtime_resolve_slow] - |= bit_arch_Use_dl_runtime_resolve_slow; - if (cpu_features->max_cpuid >= 0xd) - { - unsigned int eax; - - __cpuid_count (0xd, 1, eax, ebx, ecx, edx); - if ((eax & (1 << 2)) != 0) - cpu_features->feature[index_arch_Use_dl_runtime_resolve_opt] - |= bit_arch_Use_dl_runtime_resolve_opt; - } + /* Since AVX512ER is unique to Xeon Phi, set Prefer_No_VZEROUPPER + if AVX512ER is available. Don't use AVX512 to avoid lower CPU + frequency if AVX512ER isn't available. */ + if (CPU_FEATURES_CPU_P (cpu_features, AVX512ER)) + cpu_features->feature[index_arch_Prefer_No_VZEROUPPER] + |= bit_arch_Prefer_No_VZEROUPPER; + else + cpu_features->feature[index_arch_Prefer_No_AVX512] + |= bit_arch_Prefer_No_AVX512; } /* This spells out "AuthenticAMD". */ else if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65) diff --git a/sysdeps/x86/cpu-features.h b/sysdeps/x86/cpu-features.h index 95f0fcff87..af83a28512 100644 --- a/sysdeps/x86/cpu-features.h +++ b/sysdeps/x86/cpu-features.h @@ -37,8 +37,8 @@ #define bit_arch_Prefer_No_VZEROUPPER (1 << 17) #define bit_arch_Fast_Unaligned_Copy (1 << 18) #define bit_arch_Prefer_ERMS (1 << 19) -#define bit_arch_Use_dl_runtime_resolve_opt (1 << 20) -#define bit_arch_Use_dl_runtime_resolve_slow (1 << 21) +#define bit_arch_Prefer_No_AVX512 (1 << 20) +#define bit_arch_XSAVEC_Usable (1 << 21) /* CPUID Feature flags. */ @@ -62,6 +62,11 @@ #define bit_cpu_AVX2 (1 << 5) #define bit_cpu_AVX512F (1 << 16) #define bit_cpu_AVX512DQ (1 << 17) +#define bit_cpu_AVX512PF (1 << 26) +#define bit_cpu_AVX512ER (1 << 27) +#define bit_cpu_AVX512CD (1 << 28) +#define bit_cpu_AVX512BW (1 << 30) +#define bit_cpu_AVX512VL (1u << 31) /* XCR0 Feature flags. */ #define bit_XMM_state (1 << 1) @@ -76,6 +81,15 @@ /* The current maximum size of the feature integer bit array. */ #define FEATURE_INDEX_MAX 1 +/* Offset for fxsave/xsave area used by _dl_runtime_resolve. Also need + space to preserve RCX, RDX, RSI, RDI, R8, R9 and RAX. It must be + aligned to 16 bytes for fxsave and 64 bytes for xsave. */ +#define STATE_SAVE_OFFSET (8 * 7 + 8) + +/* Save SSE, AVX, AVX512, mask and bound registers. */ +#define STATE_SAVE_MASK \ + ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 5) | (1 << 6) | (1 << 7)) + #ifdef __ASSEMBLER__ # include @@ -111,6 +125,7 @@ # define index_arch_Prefer_ERMS FEATURE_INDEX_1*FEATURE_SIZE # define index_arch_Use_dl_runtime_resolve_opt FEATURE_INDEX_1*FEATURE_SIZE # define index_arch_Use_dl_runtime_resolve_slow FEATURE_INDEX_1*FEATURE_SIZE +# define index_arch_Prefer_No_AVX512 FEATURE_INDEX_1*FEATURE_SIZE # if defined (_LIBC) && !IS_IN (nonlib) @@ -199,6 +214,12 @@ struct cpu_features } cpuid[COMMON_CPUID_INDEX_MAX]; unsigned int family; unsigned int model; + /* The type must be unsigned long int so that we use + + sub xsave_state_size_offset(%rip) %RSP_LP + + in _dl_runtime_resolve. */ + unsigned long int xsave_state_size; unsigned int feature[FEATURE_INDEX_MAX]; }; @@ -236,6 +257,11 @@ extern const struct cpu_features *__get_cpu_features (void) # define index_cpu_AVX2 COMMON_CPUID_INDEX_7 # define index_cpu_AVX512F COMMON_CPUID_INDEX_7 # define index_cpu_AVX512DQ COMMON_CPUID_INDEX_7 +# define index_cpu_AVX512PF COMMON_CPUID_INDEX_7 +# define index_cpu_AVX512ER COMMON_CPUID_INDEX_7 +# define index_cpu_AVX512CD COMMON_CPUID_INDEX_7 +# define index_cpu_AVX512BW COMMON_CPUID_INDEX_7 +# define index_cpu_AVX512VL COMMON_CPUID_INDEX_7 # define index_cpu_ERMS COMMON_CPUID_INDEX_7 # define index_cpu_RTM COMMON_CPUID_INDEX_7 # define index_cpu_FMA COMMON_CPUID_INDEX_1 @@ -254,6 +280,11 @@ extern const struct cpu_features *__get_cpu_features (void) # define reg_AVX2 ebx # define reg_AVX512F ebx # define reg_AVX512DQ ebx +# define reg_AVX512PF ebx +# define reg_AVX512ER ebx +# define reg_AVX512CD ebx +# define reg_AVX512BW ebx +# define reg_AVX512VL ebx # define reg_ERMS ebx # define reg_RTM ebx # define reg_FMA ecx @@ -281,8 +312,8 @@ extern const struct cpu_features *__get_cpu_features (void) # define index_arch_Prefer_No_VZEROUPPER FEATURE_INDEX_1 # define index_arch_Fast_Unaligned_Copy FEATURE_INDEX_1 # define index_arch_Prefer_ERMS FEATURE_INDEX_1 -# define index_arch_Use_dl_runtime_resolve_opt FEATURE_INDEX_1 -# define index_arch_Use_dl_runtime_resolve_slow FEATURE_INDEX_1 +# define index_arch_Prefer_No_AVX512 FEATURE_INDEX_1 +# define index_arch_XSAVEC_Usable FEATURE_INDEX_1 #endif /* !__ASSEMBLER__ */ diff --git a/sysdeps/x86/fpu/test-math-vector-sincos.h b/sysdeps/x86/fpu/test-math-vector-sincos.h index 5043b32563..95282a3ac7 100644 --- a/sysdeps/x86/fpu/test-math-vector-sincos.h +++ b/sysdeps/x86/fpu/test-math-vector-sincos.h @@ -17,14 +17,14 @@ License along with the GNU C Library; if not, see . */ -#define INIT_VEC_PTRS_LOOP(vec, val, len) \ - do \ - { \ - for (i = 0; i < len; i++) \ - { \ - vec[i] = &val[i]; \ - } \ - } \ +#define INIT_VEC_PTRS_LOOP(vec, val, len) \ + do \ + { \ + union { VEC_INT_TYPE v; __typeof__ ((val)[0]) *a[(len)]; } u; \ + for (i = 0; i < len; i++) \ + u.a[i] = &(val)[i]; \ + (vec) = u.v; \ + } \ while (0) /* Wrapper for vector sincos/sincosf compatible with x86_64 and x32 variants @@ -40,8 +40,8 @@ void scalar_func (FLOAT x, FLOAT * r, FLOAT * r1) \ VEC_TYPE mx; \ VEC_INT_TYPE mr, mr1; \ INIT_VEC_LOOP (mx, x, VEC_LEN); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr), r_loc, VEC_LEN); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr1), r1_loc, VEC_LEN); \ + INIT_VEC_PTRS_LOOP (mr, r_loc, VEC_LEN); \ + INIT_VEC_PTRS_LOOP (mr1, r1_loc, VEC_LEN); \ vector_func (mx, mr, mr1); \ TEST_VEC_LOOP (r_loc, VEC_LEN); \ TEST_VEC_LOOP (r1_loc, VEC_LEN); \ @@ -63,8 +63,8 @@ void scalar_func (FLOAT x, FLOAT * r, FLOAT * r1) \ VEC_TYPE mx; \ VEC_INT_TYPE mr, mr1; \ INIT_VEC_LOOP (mx, x, VEC_LEN); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr), r_loc, VEC_LEN/2); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr1), r1_loc, VEC_LEN/2); \ + INIT_VEC_PTRS_LOOP (mr, r_loc, VEC_LEN/2); \ + INIT_VEC_PTRS_LOOP (mr1, r1_loc, VEC_LEN/2); \ vector_func (mx, mr, mr, mr1, mr1); \ TEST_VEC_LOOP (r_loc, VEC_LEN/2); \ TEST_VEC_LOOP (r1_loc, VEC_LEN/2); \ @@ -87,8 +87,8 @@ void scalar_func (FLOAT x, FLOAT * r, FLOAT * r1) \ VEC_TYPE mx; \ VEC_INT_TYPE mr, mr1; \ INIT_VEC_LOOP (mx, x, VEC_LEN); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr), r_loc, VEC_LEN/4); \ - INIT_VEC_PTRS_LOOP (((FLOAT **) &mr1), r1_loc, VEC_LEN/4); \ + INIT_VEC_PTRS_LOOP (mr, r_loc, VEC_LEN/4); \ + INIT_VEC_PTRS_LOOP (mr1, r1_loc, VEC_LEN/4); \ vector_func (mx, mr, mr, mr, mr, mr1, mr1, mr1, mr1); \ TEST_VEC_LOOP (r_loc, VEC_LEN/4); \ TEST_VEC_LOOP (r1_loc, VEC_LEN/4); \ diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile index 5f25893dc9..132470d9cb 100644 --- a/sysdeps/x86_64/Makefile +++ b/sysdeps/x86_64/Makefile @@ -27,7 +27,7 @@ ifeq ($(subdir),elf) CFLAGS-.os += $(if $(filter $(@F),$(patsubst %,%.os,$(all-rtld-routines))),\ -mno-mmx) -sysdep-dl-routines += tlsdesc dl-tlsdesc +sysdep-dl-routines += tlsdesc dl-tlsdesc tls_get_addr tests += ifuncmain8 modules-names += ifuncmod8 @@ -52,9 +52,12 @@ $(objpfx)tst-quad2pie: $(objpfx)tst-quadmod2pie.o CFLAGS-tst-quad1pie.c = $(PIE-ccflag) CFLAGS-tst-quad2pie.c = $(PIE-ccflag) -tests += tst-audit3 tst-audit4 tst-audit5 tst-audit6 tst-audit7 tst-audit10 -test-extras += tst-audit4-aux tst-audit10-aux -extra-test-objs += tst-audit4-aux.o tst-audit10-aux.o +tests += tst-audit3 tst-audit4 tst-audit5 tst-audit6 tst-audit7 \ + tst-audit10 tst-sse tst-avx tst-avx512 +test-extras += tst-audit4-aux tst-audit10-aux \ + tst-avx-aux tst-avx512-aux +extra-test-objs += tst-audit4-aux.o tst-audit10-aux.o \ + tst-avx-aux.o tst-avx512-aux.o tests += tst-split-dynreloc LDFLAGS-tst-split-dynreloc = -Wl,-T,$(..)sysdeps/x86_64/tst-split-dynreloc.lds @@ -65,7 +68,8 @@ modules-names += tst-auditmod3a tst-auditmod3b \ tst-auditmod5a tst-auditmod5b \ tst-auditmod6a tst-auditmod6b tst-auditmod6c \ tst-auditmod7a tst-auditmod7b \ - tst-auditmod10a tst-auditmod10b + tst-auditmod10a tst-auditmod10b \ + tst-ssemod tst-avxmod tst-avx512mod $(objpfx)tst-audit3: $(objpfx)tst-auditmod3a.so $(objpfx)tst-audit3.out: $(objpfx)tst-auditmod3b.so @@ -92,6 +96,10 @@ $(objpfx)tst-audit10: $(objpfx)tst-audit10-aux.o $(objpfx)tst-auditmod10a.so $(objpfx)tst-audit10.out: $(objpfx)tst-auditmod10b.so tst-audit10-ENV = LD_AUDIT=$(objpfx)tst-auditmod10b.so +$(objpfx)tst-sse: $(objpfx)tst-ssemod.so +$(objpfx)tst-avx: $(objpfx)tst-avx-aux.o $(objpfx)tst-avxmod.so +$(objpfx)tst-avx512: $(objpfx)tst-avx512-aux.o $(objpfx)tst-avx512mod.so + AVX-CFLAGS=-mavx -mno-vzeroupper CFLAGS-tst-audit4-aux.c += $(AVX-CFLAGS) CFLAGS-tst-auditmod4a.c += $(AVX-CFLAGS) @@ -99,14 +107,18 @@ CFLAGS-tst-auditmod4b.c += $(AVX-CFLAGS) CFLAGS-tst-auditmod6b.c += $(AVX-CFLAGS) CFLAGS-tst-auditmod6c.c += $(AVX-CFLAGS) CFLAGS-tst-auditmod7b.c += $(AVX-CFLAGS) +CFLAGS-tst-avx-aux.c += $(AVX-CFLAGS) +CFLAGS-tst-avxmod.c += $(AVX-CFLAGS) ifeq (yes,$(config-cflags-avx512)) AVX512-CFLAGS = -mavx512f CFLAGS-tst-audit10-aux.c += $(AVX512-CFLAGS) CFLAGS-tst-auditmod10a.c += $(AVX512-CFLAGS) CFLAGS-tst-auditmod10b.c += $(AVX512-CFLAGS) +CFLAGS-tst-avx512-aux.c += $(AVX512-CFLAGS) +CFLAGS-tst-avx512mod.c += $(AVX512-CFLAGS) endif endif ifeq ($(subdir),csu) -gen-as-const-headers += tlsdesc.sym +gen-as-const-headers += tlsdesc.sym rtld-offsets.sym endif diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h index daf4d8c070..be0b8616ea 100644 --- a/sysdeps/x86_64/dl-machine.h +++ b/sysdeps/x86_64/dl-machine.h @@ -66,12 +66,9 @@ static inline int __attribute__ ((unused, always_inline)) elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) { Elf64_Addr *got; - extern void _dl_runtime_resolve_sse (ElfW(Word)) attribute_hidden; - extern void _dl_runtime_resolve_avx (ElfW(Word)) attribute_hidden; - extern void _dl_runtime_resolve_avx_slow (ElfW(Word)) attribute_hidden; - extern void _dl_runtime_resolve_avx_opt (ElfW(Word)) attribute_hidden; - extern void _dl_runtime_resolve_avx512 (ElfW(Word)) attribute_hidden; - extern void _dl_runtime_resolve_avx512_opt (ElfW(Word)) attribute_hidden; + extern void _dl_runtime_resolve_fxsave (ElfW(Word)) attribute_hidden; + extern void _dl_runtime_resolve_xsave (ElfW(Word)) attribute_hidden; + extern void _dl_runtime_resolve_xsavec (ElfW(Word)) attribute_hidden; extern void _dl_runtime_profile_sse (ElfW(Word)) attribute_hidden; extern void _dl_runtime_profile_avx (ElfW(Word)) attribute_hidden; extern void _dl_runtime_profile_avx512 (ElfW(Word)) attribute_hidden; @@ -120,29 +117,14 @@ elf_machine_runtime_setup (struct link_map *l, int lazy, int profile) /* This function will get called to fix up the GOT entry indicated by the offset on the stack, and then jump to the resolved address. */ - if (HAS_ARCH_FEATURE (AVX512F_Usable)) - { - if (HAS_ARCH_FEATURE (Use_dl_runtime_resolve_opt)) - *(ElfW(Addr) *) (got + 2) - = (ElfW(Addr)) &_dl_runtime_resolve_avx512_opt; - else - *(ElfW(Addr) *) (got + 2) - = (ElfW(Addr)) &_dl_runtime_resolve_avx512; - } - else if (HAS_ARCH_FEATURE (AVX_Usable)) - { - if (HAS_ARCH_FEATURE (Use_dl_runtime_resolve_opt)) - *(ElfW(Addr) *) (got + 2) - = (ElfW(Addr)) &_dl_runtime_resolve_avx_opt; - else if (HAS_ARCH_FEATURE (Use_dl_runtime_resolve_slow)) - *(ElfW(Addr) *) (got + 2) - = (ElfW(Addr)) &_dl_runtime_resolve_avx_slow; - else - *(ElfW(Addr) *) (got + 2) - = (ElfW(Addr)) &_dl_runtime_resolve_avx; - } + if (GLRO(dl_x86_cpu_features).xsave_state_size != 0) + *(ElfW(Addr) *) (got + 2) + = (HAS_ARCH_FEATURE (XSAVEC_Usable) + ? (ElfW(Addr)) &_dl_runtime_resolve_xsavec + : (ElfW(Addr)) &_dl_runtime_resolve_xsave); else - *(ElfW(Addr) *) (got + 2) = (ElfW(Addr)) &_dl_runtime_resolve_sse; + *(ElfW(Addr) *) (got + 2) + = (ElfW(Addr)) &_dl_runtime_resolve_fxsave; } } diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c new file mode 100644 index 0000000000..3584805c8e --- /dev/null +++ b/sysdeps/x86_64/dl-tls.c @@ -0,0 +1,53 @@ +/* Thread-local storage handling in the ELF dynamic linker. x86-64 version. + Copyright (C) 2017 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 + . */ + +#ifdef SHARED +/* Work around GCC PR58066, due to which __tls_get_addr may be called + with an unaligned stack. The compat implementation is in + tls_get_addr-compat.S. */ + +# include + +/* Define __tls_get_addr within elf/dl-tls.c under a different + name. */ +extern __typeof__ (__tls_get_addr) ___tls_get_addr; + +# define __tls_get_addr ___tls_get_addr +# include +# undef __tls_get_addr + +hidden_ver (___tls_get_addr, __tls_get_addr) + +/* Only handle slow paths for __tls_get_addr. */ +attribute_hidden +void * +__tls_get_addr_slow (GET_ADDR_ARGS) +{ + dtv_t *dtv = THREAD_DTV (); + + if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) + return update_get_addr (GET_ADDR_PARAM); + + return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); +} +#else + +/* No compatibility symbol needed. */ +# include + +#endif diff --git a/sysdeps/x86_64/dl-tls.h b/sysdeps/x86_64/dl-tls.h index 4a59d2a924..c2fb56c0a1 100644 --- a/sysdeps/x86_64/dl-tls.h +++ b/sysdeps/x86_64/dl-tls.h @@ -16,6 +16,9 @@ License along with the GNU C Library; if not, see . */ +#ifndef _X86_64_DL_TLS_H +#define _X86_64_DL_TLS_H + #include /* Type used for the representation of TLS information in the GOT. */ @@ -27,3 +30,5 @@ typedef struct dl_tls_index extern void *__tls_get_addr (tls_index *ti); + +#endif /* _X86_64_DL_TLS_H */ diff --git a/sysdeps/x86_64/dl-trampoline.S b/sysdeps/x86_64/dl-trampoline.S index 33d7fcf7d0..a645572e44 100644 --- a/sysdeps/x86_64/dl-trampoline.S +++ b/sysdeps/x86_64/dl-trampoline.S @@ -34,41 +34,24 @@ # define DL_STACK_ALIGNMENT 8 #endif -#ifndef DL_RUNTIME_UNALIGNED_VEC_SIZE -/* The maximum size in bytes of unaligned vector load and store in the - dynamic linker. Since SSE optimized memory/string functions with - aligned SSE register load and store are used in the dynamic linker, - we must set this to 8 so that _dl_runtime_resolve_sse will align the - stack before calling _dl_fixup. */ -# define DL_RUNTIME_UNALIGNED_VEC_SIZE 8 -#endif - -/* True if _dl_runtime_resolve should align stack to VEC_SIZE bytes. */ +/* True if _dl_runtime_resolve should align stack for STATE_SAVE or align + stack to 16 bytes before calling _dl_fixup. */ #define DL_RUNTIME_RESOLVE_REALIGN_STACK \ - (VEC_SIZE > DL_STACK_ALIGNMENT \ - && VEC_SIZE > DL_RUNTIME_UNALIGNED_VEC_SIZE) - -/* Align vector register save area to 16 bytes. */ -#define REGISTER_SAVE_VEC_OFF 0 + (STATE_SAVE_ALIGNMENT > DL_STACK_ALIGNMENT \ + || 16 > DL_STACK_ALIGNMENT) /* Area on stack to save and restore registers used for parameter passing when calling _dl_fixup. */ #ifdef __ILP32__ -# define REGISTER_SAVE_RAX (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 8) # define PRESERVE_BND_REGS_PREFIX #else -/* Align bound register save area to 16 bytes. */ -# define REGISTER_SAVE_BND0 (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 8) -# define REGISTER_SAVE_BND1 (REGISTER_SAVE_BND0 + 16) -# define REGISTER_SAVE_BND2 (REGISTER_SAVE_BND1 + 16) -# define REGISTER_SAVE_BND3 (REGISTER_SAVE_BND2 + 16) -# define REGISTER_SAVE_RAX (REGISTER_SAVE_BND3 + 16) # ifdef HAVE_MPX_SUPPORT # define PRESERVE_BND_REGS_PREFIX bnd # else # define PRESERVE_BND_REGS_PREFIX .byte 0xf2 # endif #endif +#define REGISTER_SAVE_RAX 0 #define REGISTER_SAVE_RCX (REGISTER_SAVE_RAX + 8) #define REGISTER_SAVE_RDX (REGISTER_SAVE_RCX + 8) #define REGISTER_SAVE_RSI (REGISTER_SAVE_RDX + 8) @@ -80,69 +63,56 @@ #define VEC_SIZE 64 #define VMOVA vmovdqa64 -#if DL_RUNTIME_RESOLVE_REALIGN_STACK || VEC_SIZE <= DL_STACK_ALIGNMENT -# define VMOV vmovdqa64 -#else -# define VMOV vmovdqu64 -#endif #define VEC(i) zmm##i -#define _dl_runtime_resolve _dl_runtime_resolve_avx512 -#define _dl_runtime_resolve_opt _dl_runtime_resolve_avx512_opt #define _dl_runtime_profile _dl_runtime_profile_avx512 #include "dl-trampoline.h" -#undef _dl_runtime_resolve -#undef _dl_runtime_resolve_opt #undef _dl_runtime_profile #undef VEC -#undef VMOV #undef VMOVA #undef VEC_SIZE #define VEC_SIZE 32 #define VMOVA vmovdqa -#if DL_RUNTIME_RESOLVE_REALIGN_STACK || VEC_SIZE <= DL_STACK_ALIGNMENT -# define VMOV vmovdqa -#else -# define VMOV vmovdqu -#endif #define VEC(i) ymm##i -#define _dl_runtime_resolve _dl_runtime_resolve_avx -#define _dl_runtime_resolve_opt _dl_runtime_resolve_avx_opt #define _dl_runtime_profile _dl_runtime_profile_avx #include "dl-trampoline.h" -#undef _dl_runtime_resolve -#undef _dl_runtime_resolve_opt #undef _dl_runtime_profile #undef VEC -#undef VMOV #undef VMOVA #undef VEC_SIZE /* movaps/movups is 1-byte shorter. */ #define VEC_SIZE 16 #define VMOVA movaps -#if DL_RUNTIME_RESOLVE_REALIGN_STACK || VEC_SIZE <= DL_STACK_ALIGNMENT -# define VMOV movaps -#else -# define VMOV movups -#endif #define VEC(i) xmm##i -#define _dl_runtime_resolve _dl_runtime_resolve_sse #define _dl_runtime_profile _dl_runtime_profile_sse #undef RESTORE_AVX #include "dl-trampoline.h" -#undef _dl_runtime_resolve #undef _dl_runtime_profile -#undef VMOV +#undef VEC #undef VMOVA +#undef VEC_SIZE -/* Used by _dl_runtime_resolve_avx_opt/_dl_runtime_resolve_avx512_opt - to preserve the full vector registers with zero upper bits. */ -#define VMOVA vmovdqa -#if DL_RUNTIME_RESOLVE_REALIGN_STACK || VEC_SIZE <= DL_STACK_ALIGNMENT -# define VMOV vmovdqa -#else -# define VMOV vmovdqu -#endif -#define _dl_runtime_resolve _dl_runtime_resolve_sse_vex +#define USE_FXSAVE +#define STATE_SAVE_ALIGNMENT 16 +#define _dl_runtime_resolve _dl_runtime_resolve_fxsave +#include "dl-trampoline.h" +#undef _dl_runtime_resolve +#undef USE_FXSAVE +#undef STATE_SAVE_ALIGNMENT + +#define USE_XSAVE +#define STATE_SAVE_ALIGNMENT 64 +#define _dl_runtime_resolve _dl_runtime_resolve_xsave +#include "dl-trampoline.h" +#undef _dl_runtime_resolve +#undef USE_XSAVE +#undef STATE_SAVE_ALIGNMENT + +#define USE_XSAVEC +#define STATE_SAVE_ALIGNMENT 64 +#define _dl_runtime_resolve _dl_runtime_resolve_xsavec #include "dl-trampoline.h" +#undef _dl_runtime_resolve +#undef USE_XSAVEC +#undef STATE_SAVE_ALIGNMENT diff --git a/sysdeps/x86_64/dl-trampoline.h b/sysdeps/x86_64/dl-trampoline.h index b27fa06974..9ddaafee17 100644 --- a/sysdeps/x86_64/dl-trampoline.h +++ b/sysdeps/x86_64/dl-trampoline.h @@ -16,139 +16,47 @@ License along with the GNU C Library; if not, see . */ -#undef REGISTER_SAVE_AREA_RAW -#ifdef __ILP32__ -/* X32 saves RCX, RDX, RSI, RDI, R8 and R9 plus RAX as well as VEC0 to - VEC7. */ -# define REGISTER_SAVE_AREA_RAW (8 * 7 + VEC_SIZE * 8) -#else -/* X86-64 saves RCX, RDX, RSI, RDI, R8 and R9 plus RAX as well as - BND0, BND1, BND2, BND3 and VEC0 to VEC7. */ -# define REGISTER_SAVE_AREA_RAW (8 * 7 + 16 * 4 + VEC_SIZE * 8) -#endif + .text +#ifdef _dl_runtime_resolve -#undef REGISTER_SAVE_AREA -#undef LOCAL_STORAGE_AREA -#undef BASE -#if DL_RUNTIME_RESOLVE_REALIGN_STACK -# define REGISTER_SAVE_AREA (REGISTER_SAVE_AREA_RAW + 8) -/* Local stack area before jumping to function address: RBX. */ -# define LOCAL_STORAGE_AREA 8 -# define BASE rbx -# if (REGISTER_SAVE_AREA % VEC_SIZE) != 0 -# error REGISTER_SAVE_AREA must be multples of VEC_SIZE -# endif -#else -# define REGISTER_SAVE_AREA REGISTER_SAVE_AREA_RAW -/* Local stack area before jumping to function address: All saved - registers. */ -# define LOCAL_STORAGE_AREA REGISTER_SAVE_AREA -# define BASE rsp -# if (REGISTER_SAVE_AREA % 16) != 8 -# error REGISTER_SAVE_AREA must be odd multples of 8 +# undef REGISTER_SAVE_AREA +# undef LOCAL_STORAGE_AREA +# undef BASE + +# if (STATE_SAVE_ALIGNMENT % 16) != 0 +# error STATE_SAVE_ALIGNMENT must be multples of 16 # endif -#endif - .text -#ifdef _dl_runtime_resolve_opt -/* Use the smallest vector registers to preserve the full YMM/ZMM - registers to avoid SSE transition penalty. */ - -# if VEC_SIZE == 32 -/* Check if the upper 128 bits in %ymm0 - %ymm7 registers are non-zero - and preserve %xmm0 - %xmm7 registers with the zero upper bits. Since - there is no SSE transition penalty on AVX512 processors which don't - support XGETBV with ECX == 1, _dl_runtime_resolve_avx512_slow isn't - provided. */ - .globl _dl_runtime_resolve_avx_slow - .hidden _dl_runtime_resolve_avx_slow - .type _dl_runtime_resolve_avx_slow, @function - .align 16 -_dl_runtime_resolve_avx_slow: - cfi_startproc - cfi_adjust_cfa_offset(16) # Incorporate PLT - vorpd %ymm0, %ymm1, %ymm8 - vorpd %ymm2, %ymm3, %ymm9 - vorpd %ymm4, %ymm5, %ymm10 - vorpd %ymm6, %ymm7, %ymm11 - vorpd %ymm8, %ymm9, %ymm9 - vorpd %ymm10, %ymm11, %ymm10 - vpcmpeqd %xmm8, %xmm8, %xmm8 - vorpd %ymm9, %ymm10, %ymm10 - vptest %ymm10, %ymm8 - # Preserve %ymm0 - %ymm7 registers if the upper 128 bits of any - # %ymm0 - %ymm7 registers aren't zero. - PRESERVE_BND_REGS_PREFIX - jnc _dl_runtime_resolve_avx - # Use vzeroupper to avoid SSE transition penalty. - vzeroupper - # Preserve %xmm0 - %xmm7 registers with the zero upper 128 bits - # when the upper 128 bits of %ymm0 - %ymm7 registers are zero. - PRESERVE_BND_REGS_PREFIX - jmp _dl_runtime_resolve_sse_vex - cfi_adjust_cfa_offset(-16) # Restore PLT adjustment - cfi_endproc - .size _dl_runtime_resolve_avx_slow, .-_dl_runtime_resolve_avx_slow +# if (STATE_SAVE_OFFSET % STATE_SAVE_ALIGNMENT) != 0 +# error STATE_SAVE_OFFSET must be multples of STATE_SAVE_ALIGNMENT # endif -/* Use XGETBV with ECX == 1 to check which bits in vector registers are - non-zero and only preserve the non-zero lower bits with zero upper - bits. */ - .globl _dl_runtime_resolve_opt - .hidden _dl_runtime_resolve_opt - .type _dl_runtime_resolve_opt, @function - .align 16 -_dl_runtime_resolve_opt: - cfi_startproc - cfi_adjust_cfa_offset(16) # Incorporate PLT - pushq %rax - cfi_adjust_cfa_offset(8) - cfi_rel_offset(%rax, 0) - pushq %rcx - cfi_adjust_cfa_offset(8) - cfi_rel_offset(%rcx, 0) - pushq %rdx - cfi_adjust_cfa_offset(8) - cfi_rel_offset(%rdx, 0) - movl $1, %ecx - xgetbv - movl %eax, %r11d - popq %rdx - cfi_adjust_cfa_offset(-8) - cfi_restore (%rdx) - popq %rcx - cfi_adjust_cfa_offset(-8) - cfi_restore (%rcx) - popq %rax - cfi_adjust_cfa_offset(-8) - cfi_restore (%rax) -# if VEC_SIZE == 32 - # For YMM registers, check if YMM state is in use. - andl $bit_YMM_state, %r11d - # Preserve %xmm0 - %xmm7 registers with the zero upper 128 bits if - # YMM state isn't in use. - PRESERVE_BND_REGS_PREFIX - jz _dl_runtime_resolve_sse_vex -# elif VEC_SIZE == 64 - # For ZMM registers, check if YMM state and ZMM state are in - # use. - andl $(bit_YMM_state | bit_ZMM0_15_state), %r11d - cmpl $bit_YMM_state, %r11d - # Preserve %xmm0 - %xmm7 registers with the zero upper 384 bits if - # neither YMM state nor ZMM state are in use. - PRESERVE_BND_REGS_PREFIX - jl _dl_runtime_resolve_sse_vex - # Preserve %ymm0 - %ymm7 registers with the zero upper 256 bits if - # ZMM state isn't in use. - PRESERVE_BND_REGS_PREFIX - je _dl_runtime_resolve_avx +# if DL_RUNTIME_RESOLVE_REALIGN_STACK +/* Local stack area before jumping to function address: RBX. */ +# define LOCAL_STORAGE_AREA 8 +# define BASE rbx +# ifdef USE_FXSAVE +/* Use fxsave to save XMM registers. */ +# define REGISTER_SAVE_AREA (512 + STATE_SAVE_OFFSET) +# if (REGISTER_SAVE_AREA % 16) != 0 +# error REGISTER_SAVE_AREA must be multples of 16 +# endif +# endif # else -# error Unsupported VEC_SIZE! +# ifndef USE_FXSAVE +# error USE_FXSAVE must be defined +# endif +/* Use fxsave to save XMM registers. */ +# define REGISTER_SAVE_AREA (512 + STATE_SAVE_OFFSET + 8) +/* Local stack area before jumping to function address: All saved + registers. */ +# define LOCAL_STORAGE_AREA REGISTER_SAVE_AREA +# define BASE rsp +# if (REGISTER_SAVE_AREA % 16) != 8 +# error REGISTER_SAVE_AREA must be odd multples of 8 +# endif # endif - cfi_adjust_cfa_offset(-16) # Restore PLT adjustment - cfi_endproc - .size _dl_runtime_resolve_opt, .-_dl_runtime_resolve_opt -#endif + .globl _dl_runtime_resolve .hidden _dl_runtime_resolve .type _dl_runtime_resolve, @function @@ -156,21 +64,30 @@ _dl_runtime_resolve_opt: cfi_startproc _dl_runtime_resolve: cfi_adjust_cfa_offset(16) # Incorporate PLT -#if DL_RUNTIME_RESOLVE_REALIGN_STACK -# if LOCAL_STORAGE_AREA != 8 -# error LOCAL_STORAGE_AREA must be 8 -# endif +# if DL_RUNTIME_RESOLVE_REALIGN_STACK +# if LOCAL_STORAGE_AREA != 8 +# error LOCAL_STORAGE_AREA must be 8 +# endif pushq %rbx # push subtracts stack by 8. cfi_adjust_cfa_offset(8) cfi_rel_offset(%rbx, 0) mov %RSP_LP, %RBX_LP cfi_def_cfa_register(%rbx) - and $-VEC_SIZE, %RSP_LP -#endif + and $-STATE_SAVE_ALIGNMENT, %RSP_LP +# endif +# ifdef REGISTER_SAVE_AREA sub $REGISTER_SAVE_AREA, %RSP_LP -#if !DL_RUNTIME_RESOLVE_REALIGN_STACK +# if !DL_RUNTIME_RESOLVE_REALIGN_STACK cfi_adjust_cfa_offset(REGISTER_SAVE_AREA) -#endif +# endif +# else + # Allocate stack space of the required size to save the state. +# if IS_IN (rtld) + sub _rtld_local_ro+RTLD_GLOBAL_RO_DL_X86_CPU_FEATURES_OFFSET+XSAVE_STATE_SIZE_OFFSET(%rip), %RSP_LP +# else + sub _dl_x86_cpu_features+XSAVE_STATE_SIZE_OFFSET(%rip), %RSP_LP +# endif +# endif # Preserve registers otherwise clobbered. movq %rax, REGISTER_SAVE_RAX(%rsp) movq %rcx, REGISTER_SAVE_RCX(%rsp) @@ -179,59 +96,42 @@ _dl_runtime_resolve: movq %rdi, REGISTER_SAVE_RDI(%rsp) movq %r8, REGISTER_SAVE_R8(%rsp) movq %r9, REGISTER_SAVE_R9(%rsp) - VMOV %VEC(0), (REGISTER_SAVE_VEC_OFF)(%rsp) - VMOV %VEC(1), (REGISTER_SAVE_VEC_OFF + VEC_SIZE)(%rsp) - VMOV %VEC(2), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 2)(%rsp) - VMOV %VEC(3), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 3)(%rsp) - VMOV %VEC(4), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 4)(%rsp) - VMOV %VEC(5), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 5)(%rsp) - VMOV %VEC(6), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 6)(%rsp) - VMOV %VEC(7), (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 7)(%rsp) -#ifndef __ILP32__ - # We also have to preserve bound registers. These are nops if - # Intel MPX isn't available or disabled. -# ifdef HAVE_MPX_SUPPORT - bndmov %bnd0, REGISTER_SAVE_BND0(%rsp) - bndmov %bnd1, REGISTER_SAVE_BND1(%rsp) - bndmov %bnd2, REGISTER_SAVE_BND2(%rsp) - bndmov %bnd3, REGISTER_SAVE_BND3(%rsp) +# ifdef USE_FXSAVE + fxsave STATE_SAVE_OFFSET(%rsp) # else -# if REGISTER_SAVE_BND0 == 0 - .byte 0x66,0x0f,0x1b,0x04,0x24 + movl $STATE_SAVE_MASK, %eax + xorl %edx, %edx + # Clear the XSAVE Header. +# ifdef USE_XSAVE + movq %rdx, (STATE_SAVE_OFFSET + 512)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8)(%rsp) +# endif + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 2)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 3)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 4)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 5)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 6)(%rsp) + movq %rdx, (STATE_SAVE_OFFSET + 512 + 8 * 7)(%rsp) +# ifdef USE_XSAVE + xsave STATE_SAVE_OFFSET(%rsp) # else - .byte 0x66,0x0f,0x1b,0x44,0x24,REGISTER_SAVE_BND0 + xsavec STATE_SAVE_OFFSET(%rsp) # endif - .byte 0x66,0x0f,0x1b,0x4c,0x24,REGISTER_SAVE_BND1 - .byte 0x66,0x0f,0x1b,0x54,0x24,REGISTER_SAVE_BND2 - .byte 0x66,0x0f,0x1b,0x5c,0x24,REGISTER_SAVE_BND3 # endif -#endif # Copy args pushed by PLT in register. # %rdi: link_map, %rsi: reloc_index mov (LOCAL_STORAGE_AREA + 8)(%BASE), %RSI_LP mov LOCAL_STORAGE_AREA(%BASE), %RDI_LP call _dl_fixup # Call resolver. mov %RAX_LP, %R11_LP # Save return value -#ifndef __ILP32__ - # Restore bound registers. These are nops if Intel MPX isn't - # avaiable or disabled. -# ifdef HAVE_MPX_SUPPORT - bndmov REGISTER_SAVE_BND3(%rsp), %bnd3 - bndmov REGISTER_SAVE_BND2(%rsp), %bnd2 - bndmov REGISTER_SAVE_BND1(%rsp), %bnd1 - bndmov REGISTER_SAVE_BND0(%rsp), %bnd0 + # Get register content back. +# ifdef USE_FXSAVE + fxrstor STATE_SAVE_OFFSET(%rsp) # else - .byte 0x66,0x0f,0x1a,0x5c,0x24,REGISTER_SAVE_BND3 - .byte 0x66,0x0f,0x1a,0x54,0x24,REGISTER_SAVE_BND2 - .byte 0x66,0x0f,0x1a,0x4c,0x24,REGISTER_SAVE_BND1 -# if REGISTER_SAVE_BND0 == 0 - .byte 0x66,0x0f,0x1a,0x04,0x24 -# else - .byte 0x66,0x0f,0x1a,0x44,0x24,REGISTER_SAVE_BND0 -# endif + movl $STATE_SAVE_MASK, %eax + xorl %edx, %edx + xrstor STATE_SAVE_OFFSET(%rsp) # endif -#endif - # Get register content back. movq REGISTER_SAVE_R9(%rsp), %r9 movq REGISTER_SAVE_R8(%rsp), %r8 movq REGISTER_SAVE_RDI(%rsp), %rdi @@ -239,20 +139,12 @@ _dl_runtime_resolve: movq REGISTER_SAVE_RDX(%rsp), %rdx movq REGISTER_SAVE_RCX(%rsp), %rcx movq REGISTER_SAVE_RAX(%rsp), %rax - VMOV (REGISTER_SAVE_VEC_OFF)(%rsp), %VEC(0) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE)(%rsp), %VEC(1) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 2)(%rsp), %VEC(2) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 3)(%rsp), %VEC(3) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 4)(%rsp), %VEC(4) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 5)(%rsp), %VEC(5) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 6)(%rsp), %VEC(6) - VMOV (REGISTER_SAVE_VEC_OFF + VEC_SIZE * 7)(%rsp), %VEC(7) -#if DL_RUNTIME_RESOLVE_REALIGN_STACK +# if DL_RUNTIME_RESOLVE_REALIGN_STACK mov %RBX_LP, %RSP_LP cfi_def_cfa_register(%rsp) movq (%rsp), %rbx cfi_restore(%rbx) -#endif +# endif # Adjust stack(PLT did 2 pushes) add $(LOCAL_STORAGE_AREA + 16), %RSP_LP cfi_adjust_cfa_offset(-(LOCAL_STORAGE_AREA + 16)) @@ -261,11 +153,9 @@ _dl_runtime_resolve: jmp *%r11 # Jump to function address. cfi_endproc .size _dl_runtime_resolve, .-_dl_runtime_resolve +#endif -/* To preserve %xmm0 - %xmm7 registers, dl-trampoline.h is included - twice, for _dl_runtime_resolve_sse and _dl_runtime_resolve_sse_vex. - But we don't need another _dl_runtime_profile for XMM registers. */ #if !defined PROF && defined _dl_runtime_profile # if (LR_VECTOR_OFFSET % VEC_SIZE) != 0 # error LR_VECTOR_OFFSET must be multples of VEC_SIZE diff --git a/sysdeps/x86_64/localplt.data b/sysdeps/x86_64/localplt.data index 014a9f4554..a1840cff31 100644 --- a/sysdeps/x86_64/localplt.data +++ b/sysdeps/x86_64/localplt.data @@ -8,7 +8,7 @@ libc.so: free + RELA R_X86_64_GLOB_DAT libc.so: malloc + RELA R_X86_64_GLOB_DAT libc.so: memalign + RELA R_X86_64_GLOB_DAT libc.so: realloc + RELA R_X86_64_GLOB_DAT -libm.so: matherr +libm.so: matherr + RELA R_X86_64_GLOB_DAT # The main malloc is interposed into the dynamic linker, for # allocations after the initial link (when dlopen is used). ld.so: malloc + RELA R_X86_64_GLOB_DAT diff --git a/sysdeps/x86_64/mempcpy_chk.S b/sysdeps/x86_64/mempcpy_chk.S index f8a9260e6e..f912291576 100644 --- a/sysdeps/x86_64/mempcpy_chk.S +++ b/sysdeps/x86_64/mempcpy_chk.S @@ -19,7 +19,7 @@ #include #include "asm-syntax.h" -#ifndef PIC +#ifndef SHARED /* For libc.so this is defined in memcpy.S. For libc.a, this is a separate source to avoid mempcpy bringing in __chk_fail and all routines diff --git a/sysdeps/x86_64/multiarch/memcpy.S b/sysdeps/x86_64/multiarch/memcpy.S index 1f83ee3e84..af2770397c 100644 --- a/sysdeps/x86_64/multiarch/memcpy.S +++ b/sysdeps/x86_64/multiarch/memcpy.S @@ -32,6 +32,8 @@ ENTRY(__new_memcpy) lea __memcpy_erms(%rip), %RAX_LP HAS_ARCH_FEATURE (Prefer_ERMS) jnz 2f + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __memcpy_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/memcpy_chk.S b/sysdeps/x86_64/multiarch/memcpy_chk.S index 54923420f1..8737fb9755 100644 --- a/sysdeps/x86_64/multiarch/memcpy_chk.S +++ b/sysdeps/x86_64/multiarch/memcpy_chk.S @@ -30,6 +30,8 @@ ENTRY(__memcpy_chk) .type __memcpy_chk, @gnu_indirect_function LOAD_RTLD_GLOBAL_RO_RDX + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __memcpy_chk_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/memmove.S b/sysdeps/x86_64/multiarch/memmove.S index 2021bfc30c..8c534e83e0 100644 --- a/sysdeps/x86_64/multiarch/memmove.S +++ b/sysdeps/x86_64/multiarch/memmove.S @@ -30,6 +30,8 @@ ENTRY(__libc_memmove) lea __memmove_erms(%rip), %RAX_LP HAS_ARCH_FEATURE (Prefer_ERMS) jnz 2f + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __memmove_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/memmove_chk.S b/sysdeps/x86_64/multiarch/memmove_chk.S index 8a252adcae..7870dd0247 100644 --- a/sysdeps/x86_64/multiarch/memmove_chk.S +++ b/sysdeps/x86_64/multiarch/memmove_chk.S @@ -29,6 +29,8 @@ ENTRY(__memmove_chk) .type __memmove_chk, @gnu_indirect_function LOAD_RTLD_GLOBAL_RO_RDX + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __memmove_chk_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/mempcpy.S b/sysdeps/x86_64/multiarch/mempcpy.S index 79c840d075..b8b2b28094 100644 --- a/sysdeps/x86_64/multiarch/mempcpy.S +++ b/sysdeps/x86_64/multiarch/mempcpy.S @@ -32,6 +32,8 @@ ENTRY(__mempcpy) lea __mempcpy_erms(%rip), %RAX_LP HAS_ARCH_FEATURE (Prefer_ERMS) jnz 2f + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __mempcpy_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/mempcpy_chk.S b/sysdeps/x86_64/multiarch/mempcpy_chk.S index 6927962e81..072b22c49f 100644 --- a/sysdeps/x86_64/multiarch/mempcpy_chk.S +++ b/sysdeps/x86_64/multiarch/mempcpy_chk.S @@ -30,6 +30,8 @@ ENTRY(__mempcpy_chk) .type __mempcpy_chk, @gnu_indirect_function LOAD_RTLD_GLOBAL_RO_RDX + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 1f HAS_ARCH_FEATURE (AVX512F_Usable) jz 1f lea __mempcpy_chk_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/memset.S b/sysdeps/x86_64/multiarch/memset.S index c958b2f49f..9d33118cf8 100644 --- a/sysdeps/x86_64/multiarch/memset.S +++ b/sysdeps/x86_64/multiarch/memset.S @@ -41,6 +41,8 @@ ENTRY(memset) jnz L(AVX512F) lea __memset_avx2_unaligned(%rip), %RAX_LP L(AVX512F): + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 2f HAS_ARCH_FEATURE (AVX512F_Usable) jz 2f lea __memset_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/multiarch/memset_chk.S b/sysdeps/x86_64/multiarch/memset_chk.S index 79eaa37bb6..7e08311cdf 100644 --- a/sysdeps/x86_64/multiarch/memset_chk.S +++ b/sysdeps/x86_64/multiarch/memset_chk.S @@ -38,6 +38,8 @@ ENTRY(__memset_chk) jnz L(AVX512F) lea __memset_chk_avx2_unaligned(%rip), %RAX_LP L(AVX512F): + HAS_ARCH_FEATURE (Prefer_No_AVX512) + jnz 2f HAS_ARCH_FEATURE (AVX512F_Usable) jz 2f lea __memset_chk_avx512_no_vzeroupper(%rip), %RAX_LP diff --git a/sysdeps/x86_64/rtld-offsets.sym b/sysdeps/x86_64/rtld-offsets.sym new file mode 100644 index 0000000000..fd41b51521 --- /dev/null +++ b/sysdeps/x86_64/rtld-offsets.sym @@ -0,0 +1,6 @@ +#define SHARED +#include + +-- + +GL_TLS_GENERATION_OFFSET offsetof (struct rtld_global, _dl_tls_generation) diff --git a/sysdeps/x86_64/tls_get_addr.S b/sysdeps/x86_64/tls_get_addr.S new file mode 100644 index 0000000000..9d38fb3be5 --- /dev/null +++ b/sysdeps/x86_64/tls_get_addr.S @@ -0,0 +1,61 @@ +/* Stack-aligning implementation of __tls_get_addr. x86-64 version. + Copyright (C) 2017 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 + . */ + +#ifdef SHARED + +# include +# include "tlsdesc.h" +# include "rtld-offsets.h" + +/* See __tls_get_addr and __tls_get_addr_slow in dl-tls.c. This function + call __tls_get_addr_slow on both slow paths. It realigns the stack + before the call to work around GCC PR58066. */ + +ENTRY (__tls_get_addr) + mov %fs:DTV_OFFSET, %RDX_LP + mov GL_TLS_GENERATION_OFFSET+_rtld_local(%rip), %RAX_LP + /* GL(dl_tls_generation) == dtv[0].counter */ + cmp %RAX_LP, (%rdx) + jne 1f + mov TI_MODULE_OFFSET(%rdi), %RAX_LP + /* dtv[ti->ti_module] */ +# ifdef __LP64__ + salq $4, %rax + movq (%rdx,%rax), %rax +# else + movl (%rdx,%rax, 8), %eax +# endif + cmp $-1, %RAX_LP + je 1f + add TI_OFFSET_OFFSET(%rdi), %RAX_LP + ret +1: + /* On the slow path, align the stack. */ + pushq %rbp + cfi_def_cfa_offset (16) + cfi_offset (%rbp, -16) + mov %RSP_LP, %RBP_LP + cfi_def_cfa_register (%rbp) + and $-16, %RSP_LP + call __tls_get_addr_slow + mov %RBP_LP, %RSP_LP + popq %rbp + cfi_def_cfa (%rsp, 8) + ret +END (__tls_get_addr) +#endif /* SHARED */ diff --git a/sysdeps/x86_64/tlsdesc.sym b/sysdeps/x86_64/tlsdesc.sym index 33854975d0..fc897ab4b5 100644 --- a/sysdeps/x86_64/tlsdesc.sym +++ b/sysdeps/x86_64/tlsdesc.sym @@ -15,3 +15,6 @@ TLSDESC_ARG offsetof(struct tlsdesc, arg) TLSDESC_GEN_COUNT offsetof(struct tlsdesc_dynamic_arg, gen_count) TLSDESC_MODID offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_module) TLSDESC_MODOFF offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_offset) + +TI_MODULE_OFFSET offsetof(tls_index, ti_module) +TI_OFFSET_OFFSET offsetof(tls_index, ti_offset) diff --git a/sysdeps/x86_64/tst-avx-aux.c b/sysdeps/x86_64/tst-avx-aux.c new file mode 100644 index 0000000000..e3807de7bb --- /dev/null +++ b/sysdeps/x86_64/tst-avx-aux.c @@ -0,0 +1,47 @@ +/* Test case for preserved AVX registers in dynamic linker, -mavx part. + Copyright (C) 2017 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 + +int +tst_avx_aux (void) +{ +#ifdef __AVX__ + extern __m256i avx_test (__m256i, __m256i, __m256i, __m256i, + __m256i, __m256i, __m256i, __m256i); + + __m256i ymm0 = _mm256_set1_epi32 (0); + __m256i ymm1 = _mm256_set1_epi32 (1); + __m256i ymm2 = _mm256_set1_epi32 (2); + __m256i ymm3 = _mm256_set1_epi32 (3); + __m256i ymm4 = _mm256_set1_epi32 (4); + __m256i ymm5 = _mm256_set1_epi32 (5); + __m256i ymm6 = _mm256_set1_epi32 (6); + __m256i ymm7 = _mm256_set1_epi32 (7); + __m256i ret = avx_test (ymm0, ymm1, ymm2, ymm3, + ymm4, ymm5, ymm6, ymm7); + ymm0 = _mm256_set1_epi32 (0x12349876); + if (memcmp (&ymm0, &ret, sizeof (ret))) + abort (); + return 0; +#else /* __AVX__ */ + return 77; +#endif /* __AVX__ */ +} diff --git a/sysdeps/x86_64/tst-avx.c b/sysdeps/x86_64/tst-avx.c new file mode 100644 index 0000000000..ec2e3a79ff --- /dev/null +++ b/sysdeps/x86_64/tst-avx.c @@ -0,0 +1,49 @@ +/* Test case for preserved AVX registers in dynamic linker. + Copyright (C) 2017 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 + +int tst_avx_aux (void); + +static int +avx_enabled (void) +{ + unsigned int eax, ebx, ecx, edx; + + if (__get_cpuid (1, &eax, &ebx, &ecx, &edx) == 0 + || (ecx & (bit_AVX | bit_OSXSAVE)) != (bit_AVX | bit_OSXSAVE)) + return 0; + + /* Check the OS has AVX and SSE saving enabled. */ + asm ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (0)); + + return (eax & 6) == 6; +} + +static int +do_test (void) +{ + /* Run AVX test only if AVX is supported. */ + if (avx_enabled ()) + return tst_avx_aux (); + else + return 77; +} + +#define TEST_FUNCTION do_test () +#include "../../test-skeleton.c" diff --git a/sysdeps/x86_64/tst-avx512-aux.c b/sysdeps/x86_64/tst-avx512-aux.c new file mode 100644 index 0000000000..6cebc523f2 --- /dev/null +++ b/sysdeps/x86_64/tst-avx512-aux.c @@ -0,0 +1,48 @@ +/* Test case for preserved AVX512 registers in dynamic linker, + -mavx512 part. + Copyright (C) 2017 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 + +int +tst_avx512_aux (void) +{ +#ifdef __AVX512F__ + extern __m512i avx512_test (__m512i, __m512i, __m512i, __m512i, + __m512i, __m512i, __m512i, __m512i); + + __m512i zmm0 = _mm512_set1_epi32 (0); + __m512i zmm1 = _mm512_set1_epi32 (1); + __m512i zmm2 = _mm512_set1_epi32 (2); + __m512i zmm3 = _mm512_set1_epi32 (3); + __m512i zmm4 = _mm512_set1_epi32 (4); + __m512i zmm5 = _mm512_set1_epi32 (5); + __m512i zmm6 = _mm512_set1_epi32 (6); + __m512i zmm7 = _mm512_set1_epi32 (7); + __m512i ret = avx512_test (zmm0, zmm1, zmm2, zmm3, + zmm4, zmm5, zmm6, zmm7); + zmm0 = _mm512_set1_epi32 (0x12349876); + if (memcmp (&zmm0, &ret, sizeof (ret))) + abort (); + return 0; +#else /* __AVX512F__ */ + return 77; +#endif /* __AVX512F__ */ +} diff --git a/sysdeps/x86_64/tst-avx512.c b/sysdeps/x86_64/tst-avx512.c new file mode 100644 index 0000000000..a8e42ef553 --- /dev/null +++ b/sysdeps/x86_64/tst-avx512.c @@ -0,0 +1,57 @@ +/* Test case for preserved AVX512 registers in dynamic linker. + Copyright (C) 2017 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 + +int tst_avx512_aux (void); + +static int +avx512_enabled (void) +{ +#ifdef bit_AVX512F + unsigned int eax, ebx, ecx, edx; + + if (__get_cpuid (1, &eax, &ebx, &ecx, &edx) == 0 + || (ecx & (bit_AVX | bit_OSXSAVE)) != (bit_AVX | bit_OSXSAVE)) + return 0; + + __cpuid_count (7, 0, eax, ebx, ecx, edx); + if (!(ebx & bit_AVX512F)) + return 0; + + asm ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (0)); + + /* Verify that ZMM, YMM and XMM states are enabled. */ + return (eax & 0xe6) == 0xe6; +#else + return 0; +#endif +} + +static int +do_test (void) +{ + /* Run AVX512 test only if AVX512 is supported. */ + if (avx512_enabled ()) + return tst_avx512_aux (); + else + return 77; +} + +#define TEST_FUNCTION do_test () +#include "../../test-skeleton.c" diff --git a/sysdeps/x86_64/tst-avx512mod.c b/sysdeps/x86_64/tst-avx512mod.c new file mode 100644 index 0000000000..4cfb3a2c3d --- /dev/null +++ b/sysdeps/x86_64/tst-avx512mod.c @@ -0,0 +1,48 @@ +/* Test case for x86-64 preserved AVX512 registers in dynamic linker. */ + +#ifdef __AVX512F__ +#include +#include +#include + +__m512i +avx512_test (__m512i x0, __m512i x1, __m512i x2, __m512i x3, + __m512i x4, __m512i x5, __m512i x6, __m512i x7) +{ + __m512i zmm; + + zmm = _mm512_set1_epi32 (0); + if (memcmp (&zmm, &x0, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (1); + if (memcmp (&zmm, &x1, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (2); + if (memcmp (&zmm, &x2, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (3); + if (memcmp (&zmm, &x3, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (4); + if (memcmp (&zmm, &x4, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (5); + if (memcmp (&zmm, &x5, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (6); + if (memcmp (&zmm, &x6, sizeof (zmm))) + abort (); + + zmm = _mm512_set1_epi32 (7); + if (memcmp (&zmm, &x7, sizeof (zmm))) + abort (); + + return _mm512_set1_epi32 (0x12349876); +} +#endif diff --git a/sysdeps/x86_64/tst-avxmod.c b/sysdeps/x86_64/tst-avxmod.c new file mode 100644 index 0000000000..6e5b154997 --- /dev/null +++ b/sysdeps/x86_64/tst-avxmod.c @@ -0,0 +1,48 @@ +/* Test case for x86-64 preserved AVX registers in dynamic linker. */ + +#ifdef __AVX__ +#include +#include +#include + +__m256i +avx_test (__m256i x0, __m256i x1, __m256i x2, __m256i x3, + __m256i x4, __m256i x5, __m256i x6, __m256i x7) +{ + __m256i ymm; + + ymm = _mm256_set1_epi32 (0); + if (memcmp (&ymm, &x0, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (1); + if (memcmp (&ymm, &x1, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (2); + if (memcmp (&ymm, &x2, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (3); + if (memcmp (&ymm, &x3, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (4); + if (memcmp (&ymm, &x4, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (5); + if (memcmp (&ymm, &x5, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (6); + if (memcmp (&ymm, &x6, sizeof (ymm))) + abort (); + + ymm = _mm256_set1_epi32 (7); + if (memcmp (&ymm, &x7, sizeof (ymm))) + abort (); + + return _mm256_set1_epi32 (0x12349876); +} +#endif diff --git a/sysdeps/x86_64/tst-sse.c b/sysdeps/x86_64/tst-sse.c new file mode 100644 index 0000000000..dd1537cf27 --- /dev/null +++ b/sysdeps/x86_64/tst-sse.c @@ -0,0 +1,46 @@ +/* Test case for preserved SSE registers in dynamic linker. + Copyright (C) 2017 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 + +extern __m128i sse_test (__m128i, __m128i, __m128i, __m128i, + __m128i, __m128i, __m128i, __m128i); + +static int +do_test (void) +{ + __m128i xmm0 = _mm_set1_epi32 (0); + __m128i xmm1 = _mm_set1_epi32 (1); + __m128i xmm2 = _mm_set1_epi32 (2); + __m128i xmm3 = _mm_set1_epi32 (3); + __m128i xmm4 = _mm_set1_epi32 (4); + __m128i xmm5 = _mm_set1_epi32 (5); + __m128i xmm6 = _mm_set1_epi32 (6); + __m128i xmm7 = _mm_set1_epi32 (7); + __m128i ret = sse_test (xmm0, xmm1, xmm2, xmm3, + xmm4, xmm5, xmm6, xmm7); + xmm0 = _mm_set1_epi32 (0x12349876); + if (memcmp (&xmm0, &ret, sizeof (ret))) + abort (); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../../test-skeleton.c" diff --git a/sysdeps/x86_64/tst-ssemod.c b/sysdeps/x86_64/tst-ssemod.c new file mode 100644 index 0000000000..907a64c69e --- /dev/null +++ b/sysdeps/x86_64/tst-ssemod.c @@ -0,0 +1,46 @@ +/* Test case for x86-64 preserved SSE registers in dynamic linker. */ + +#include +#include +#include + +__m128i +sse_test (__m128i x0, __m128i x1, __m128i x2, __m128i x3, + __m128i x4, __m128i x5, __m128i x6, __m128i x7) +{ + __m128i xmm; + + xmm = _mm_set1_epi32 (0); + if (memcmp (&xmm, &x0, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (1); + if (memcmp (&xmm, &x1, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (2); + if (memcmp (&xmm, &x2, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (3); + if (memcmp (&xmm, &x3, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (4); + if (memcmp (&xmm, &x4, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (5); + if (memcmp (&xmm, &x5, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (6); + if (memcmp (&xmm, &x6, sizeof (xmm))) + abort (); + + xmm = _mm_set1_epi32 (7); + if (memcmp (&xmm, &x7, sizeof (xmm))) + abort (); + + return _mm_set1_epi32 (0x12349876); +}