commit 58520986c38e34db60e07260c64c563e3efcf353 Author: Siddhesh Poyarekar Date: Thu Feb 9 17:28:54 2017 +0100 Fix getting tunable values on big-endian (BZ #21109) The code to set value passed a tunable_val_t, which when cast to int32_t on big-endian gives the wrong value. Instead, use tunable_val_t.numval instead, which can then be safely cast into int32_t. (cherry picked from commit 8cbc826c37c0221ada65a7a622fe079b4e89a4b0) diff --git a/ChangeLog b/ChangeLog index f140ee67de..c88238e0fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +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/NEWS b/NEWS index ec15dde761..325cac78e4 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,12 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. +Version 2.25.1 + +The following bugs are resolved with this release: + + [21109] Tunables broken on big-endian + Version 2.25 * The feature test macro __STDC_WANT_LIB_EXT2__, from ISO/IEC TR 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/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); \ } commit 045e368799cd253ddbf8bdec42ed92e8ebb3ce67 Author: Florian Weimer Date: Tue Feb 28 17:05:46 2017 +0100 sunrpc: Avoid use-after-free read access in clntudp_call [BZ #21115] After commit bc779a1a5b3035133024b21e2f339fe4219fb11c (CVE-2016-4429: sunrpc: Do not use alloca in clntudp_call [BZ #20112]), ancillary data is stored on the heap, but it is accessed after it has been freed. The test case must be run under a heap debugger such as valgrind to observe the invalid access. A malloc implementation which immediately calls munmap on free would catch this bug as well. (cherry picked from commit d42eed4a044e5e10dfb885cf9891c2518a72a491) diff --git a/ChangeLog b/ChangeLog index c88238e0fa..3d781a354a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +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] diff --git a/NEWS b/NEWS index 325cac78e4..5ecc565371 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Version 2.25.1 The following bugs are resolved with this release: [21109] Tunables broken on big-endian + [21115] sunrpc: Use-after-free in error path in clntudp_call Version 2.25 diff --git a/sunrpc/Makefile b/sunrpc/Makefile index 0c1e6124ff..daf8a28f1a 100644 --- a/sunrpc/Makefile +++ b/sunrpc/Makefile @@ -93,7 +93,7 @@ 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 xtests := tst-getmyaddr ifeq ($(have-thread-library),yes) @@ -155,6 +155,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)) diff --git a/sunrpc/clnt_udp.c b/sunrpc/clnt_udp.c index 4d9acb1e6a..1de25cb771 100644 --- a/sunrpc/clnt_udp.c +++ b/sunrpc/clnt_udp.c @@ -421,9 +421,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); 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 commit 93cf93e06ce123439e41d3d62790601c313134cb Author: Florian Weimer Date: Tue Feb 28 17:36:00 2017 +0100 sunrpc: Improvements for UDP client timeout handling [BZ #20257] This commit fixes various aspects in the UDP client timeout handling. Timeouts are now applied in a more consistent fashion. Discarded UDP packets no longer prevent the timeout from happening at all. (cherry picked from commit cf0bd2f73bd65beab613865bba567d7787836888) diff --git a/ChangeLog b/ChangeLog index 3d781a354a..510ca42c2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +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] diff --git a/NEWS b/NEWS index 5ecc565371..e7d6668664 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ The following bugs are resolved with this release: [21109] Tunables broken on big-endian [21115] sunrpc: Use-after-free in error path in clntudp_call + [20257] sunrpc: clntudp_call does not enforce timeout when receiving data Version 2.25 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/sunrpc/Makefile b/sunrpc/Makefile index daf8a28f1a..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 tst-udp-error +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) @@ -235,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 1de25cb771..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); } @@ -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-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 commit 69e0a87cc4c570e3b7218392fc3e743b5bddcce2 Author: Florian Weimer Date: Thu Mar 2 20:11:27 2017 +0100 Document and fix --enable-bind-now [BZ #21015] (cherry picked from commit 2d6ab5df3b675e96ee587ae6a8c2ce004c6b1ba9) diff --git a/ChangeLog b/ChangeLog index 510ca42c2b..8aaf9f825d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +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] 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..7f0eef8096 100644 --- a/Makerules +++ b/Makerules @@ -588,7 +588,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 @@ -686,10 +686,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 +1100,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 e7d6668664..9bf8c10926 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ The following bugs are resolved with this release: [21109] Tunables broken on big-endian [21115] sunrpc: Use-after-free in error path in clntudp_call [20257] sunrpc: clntudp_call does not enforce timeout when receiving data + [21015] Document and fix --enable-bind-now Version 2.25 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/manual/install.texi b/manual/install.texi index 266add8ba9..3398cfab02 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -175,6 +175,12 @@ options to detect stack overruns. Only the dynamic linker and a small number of routines called directly from assembler are excluded from this protection. +@item --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. + @pindex pt_chown @findex grantpt @item --enable-pt_chown 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/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/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 commit 8b3caa41b9cb82651e72a0c87aa56719c134000e Author: John David Anglin Date: Mon Feb 20 20:31:57 2017 -0500 hppa: Fix setting of __libc_stack_end The binutils package was recently changed to fix -z relro support on hppa. See ld/21000 for details: https://sourceware.org/bugzilla/show_bug.cgi?id=21000 This exposed a problem with the _dl_start_user function in the RTLD_START define. We need to set __libc_stack_end before it is made read only. For this, we need to define DL_STACK_END. The offset of 0x160 gives the same stack end as the code in _dl_start_user. A build log with the attached patch is here: https://buildd.debian.org/status/fetch.php?pkg=glibc&arch=hppa&ver=2.24-9&stamp=1487639205&raw=0 (cherry picked from commit 5d20a49aaccef5ef7adac93d5ca159f6b7ba0105) diff --git a/ChangeLog b/ChangeLog index 8aaf9f825d..d8ed2e4d90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +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] 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" \ commit 0889003c67f9c2f520a37281c4b5c3b8a9861f46 Author: Mike Frysinger Date: Sun Mar 12 15:12:21 2017 -0700 x86_64: fix static build of __mempcpy_chk for compilers defaulting to PIC/PIE When glibc is compiled with gcc 6.2 that has been configured with to default to PIC/PIE, the static version of __mempcpy_chk is not built, as the test is done on PIC instead of SHARED. Fix the test to check for SHARED, like it is done for similar functions like __memcpy_chk. 2017-03-12 Mike Frysinger * sysdeps/x86_64/mempcpy_chk.S (__mempcpy_chk): Check for SHARED instead of PIC. (cherry picked from commit fbe355fbd1973d6e29561084b3eaeb4bfe9d515a) diff --git a/ChangeLog b/ChangeLog index d8ed2e4d90..c29a70aa72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +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. 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 commit 27ab0d9518746dfb59ed2ba59daefc981dc10e38 Author: Mike Frysinger Date: Mon Mar 20 04:47:56 2017 -0400 posix_spawn: fix stack setup on ia64 [BZ #21275] The ia64-specific clone2 call expects the base of the stack mapping and the stack size as sep arguments, not an initial stack value as on other stack-grows-down architectures. Reuse the stack-grows-up macro so we pass in the right stack base. Reported-by: Matt Turner (cherry picked from commit ddc3fb333469c2997798742dc0509dc1e3201d91) diff --git a/ChangeLog b/ChangeLog index c29a70aa72..ef5388c2c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +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 diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c index 2daf0c5ef0..c96aac889d 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -61,17 +61,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 commit f035c8d055f25eaf6c93772f308afac10ce31ef2 Author: Slava Barinov Date: Fri Mar 31 08:49:25 2017 +0200 fts: Fix symbol redirect for fts_set [BZ #21289] In a 32-bit environment with _FILE_OFFSET_BITS=64, the __REDIRECT macro combined with __THROW generates an invalid C++ declaration. (cherry picked from commit ce39613205dc47ceaeea76710d49e7a483b503ab) diff --git a/ChangeLog b/ChangeLog index ef5388c2c3..85d6fc6192 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2017-03-31 Slava Barinov + + [BZ #21289] + * io/fts.h (fts_set): Replace __REDIRECT with __REDIRECT_NTH. + 2017-03-20 Mike Frysinger [BZ #21275] diff --git a/NEWS b/NEWS index 9bf8c10926..fee1cf8ca8 100644 --- a/NEWS +++ b/NEWS @@ -9,10 +9,11 @@ Version 2.25.1 The following bugs are resolved with this release: - [21109] Tunables broken on big-endian - [21115] sunrpc: Use-after-free in error path in clntudp_call [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 + [21289] Fix symbol redirect for fts_set Version 2.25 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 commit 74522eeeaa4a39809a28f44171e71d36a69edb58 Author: Wladimir J. van der Laan Date: Sat Apr 1 12:39:09 2017 +0530 Call the right helper function when setting mallopt M_ARENA_MAX (BZ #21338) Fixes a typo introduced in commit be7991c0705e35b4d70a419d117addcd6c627319. This caused mallopt(M_ARENA_MAX) as well as the environment variable MALLOC_ARENA_MAX to not work as intended because it set the wrong internal parameter. [BZ #21338] * malloc/malloc.c: Call do_set_arena_max for M_ARENA_MAX instead of incorrect do_set_arena_test diff --git a/ChangeLog b/ChangeLog index 85d6fc6192..321bf53191 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +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] 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); commit df29db0bec24211cfc917db52024bf8deecac2c9 Author: Mike Frysinger Date: Wed Mar 15 23:59:31 2017 -0700 posix_spawn: use a larger min stack for -fstack-check [BZ #21253] When glibc is built with -fstack-check, trying to use posix_spawn can lead to segfaults due to gcc internally probing stack memory too far. The new spawn API will allocate a minimum of 1 page, but the stack checking logic might probe a couple of pages. When it tries to walk them, everything falls apart. The gcc internal docs [1] state the default interval checking is one page. Which means we need two pages (the current one, and the next probed). No target currently defines it larger. Further, it mentions that the default minimum stack size needed to recover from an overflow is 4/8KiB for sjlj or 8/12KiB for others. But some Linux targets (like mips and ppc) go up to 16KiB (and some non-Linux targets go up to 24KiB). Let's create each child with a minimum of 32KiB slack space to support them all, and give us future breathing room. No test is added as existing ones crash. Even a simple call is enough to trigger the problem: char *argv[] = { "/bin/ls", NULL }; posix_spawn(NULL, "/bin/ls", NULL, NULL, argv, NULL); [1] https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gccint/Stack-Checking.html (cherry picked from commit 21f042c804835d1f7a4a8e06f2c93ca35a182042) diff --git a/ChangeLog b/ChangeLog index 321bf53191..b37a054bae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +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] diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c index c96aac889d..29d8f25ab5 100644 --- a/sysdeps/unix/sysv/linux/spawni.c +++ b/sysdeps/unix/sysv/linux/spawni.c @@ -319,6 +319,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); commit 903b77defb6f2ee2552c06472339f33091e3c7b4 Author: H.J. Lu Date: Tue Mar 21 10:59:31 2017 -0700 x86-64: Improve branch predication in _dl_runtime_resolve_avx512_opt [BZ #21258] On Skylake server, _dl_runtime_resolve_avx512_opt is used to preserve the first 8 vector registers. The code layout is if only %xmm0 - %xmm7 registers are used preserve %xmm0 - %xmm7 registers if only %ymm0 - %ymm7 registers are used preserve %ymm0 - %ymm7 registers preserve %zmm0 - %zmm7 registers Branch predication always executes the fallthrough code path to preserve %zmm0 - %zmm7 registers speculatively, even though only %xmm0 - %xmm7 registers are used. This leads to lower CPU frequency on Skylake server. This patch changes the fallthrough code path to preserve %xmm0 - %xmm7 registers instead: if whole %zmm0 - %zmm7 registers are used preserve %zmm0 - %zmm7 registers if only %ymm0 - %ymm7 registers are used preserve %ymm0 - %ymm7 registers preserve %xmm0 - %xmm7 registers Tested on Skylake server. [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. (cherry picked from commit c15f8eb50cea7ad1a4ccece6e0982bf426d52c00) diff --git a/ChangeLog b/ChangeLog index b37a054bae..8479fba8c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +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] diff --git a/sysdeps/x86_64/dl-trampoline.S b/sysdeps/x86_64/dl-trampoline.S index 33d7fcf7d0..c14c61aa58 100644 --- a/sysdeps/x86_64/dl-trampoline.S +++ b/sysdeps/x86_64/dl-trampoline.S @@ -87,11 +87,9 @@ #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 @@ -145,4 +143,5 @@ # define VMOV vmovdqu #endif #define _dl_runtime_resolve _dl_runtime_resolve_sse_vex +#define _dl_runtime_resolve_opt _dl_runtime_resolve_avx512_opt #include "dl-trampoline.h" diff --git a/sysdeps/x86_64/dl-trampoline.h b/sysdeps/x86_64/dl-trampoline.h index b27fa06974..8db24c16ac 100644 --- a/sysdeps/x86_64/dl-trampoline.h +++ b/sysdeps/x86_64/dl-trampoline.h @@ -129,19 +129,20 @@ _dl_runtime_resolve_opt: # YMM state isn't in use. PRESERVE_BND_REGS_PREFIX jz _dl_runtime_resolve_sse_vex -# elif VEC_SIZE == 64 +# elif VEC_SIZE == 16 # 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 %zmm0 - %zmm7 registers if ZMM state is in use. PRESERVE_BND_REGS_PREFIX - jl _dl_runtime_resolve_sse_vex + jg _dl_runtime_resolve_avx512 # 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 + # Preserve %xmm0 - %xmm7 registers with the zero upper 384 bits if + # neither YMM state nor ZMM state are in use. # else # error Unsupported VEC_SIZE! # endif commit b30b1c97ccfe72e82b0c95bb55274b5660bc539e Author: Adhemerval Zanella Date: Tue Apr 11 15:08:02 2017 -0300 posix: Add cleanup on the trap list for globtest.sh This patch prevents lingering files for SIGSEGV failures by adding a cleanup handler on trap handler. Checked on x86_64-linux-gnu. * posix/globtest.sh: Add cleanup routine on trap 0. Cherry-pick of 4fee33f. diff --git a/ChangeLog b/ChangeLog index 8479fba8c4..4962000b47 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2017-04-11 Adhemerval Zanella + + * posix/globtest.sh: Add cleanup routine on trap 0. + 2017-04-07 H.J. Lu [BZ #21258] 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 commit 4c6f97798fe1854a32b1199c42370eac1620eebf Author: H.J. Lu Date: Fri Apr 28 10:03:09 2017 -0700 x86: Set Prefer_No_VZEROUPPER if AVX512ER is available AVX512ER won't be implemented in any Xeon processors and will be in all Xeon Phi processors. Don't check CPU model number when setting Prefer_No_VZEROUPPER for Xeon Phi. Instead, set Prefer_No_VZEROUPPER if AVX512ER is available. It works with current and future Xeon Phi and non-Xeon Phi processors. * 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. (cherry picked from commit 1c53cb49de6d82d9469ccbd5aa0c55924502bd8b) diff --git a/ChangeLog b/ChangeLog index 4962000b47..dc49c78b8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +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. diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c index 1c714a4017..41d0be2815 100644 --- a/sysdeps/x86/cpu-features.c +++ b/sysdeps/x86/cpu-features.c @@ -139,8 +139,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,6 +224,12 @@ init_cpu_features (struct cpu_features *cpu_features) cpu_features->feature[index_arch_AVX_Fast_Unaligned_Load] |= bit_arch_AVX_Fast_Unaligned_Load; + /* Since AVX512ER is unique to Xeon Phi, set Prefer_No_VZEROUPPER + if AVX512ER is available. */ + if (CPU_FEATURES_CPU_P (cpu_features, AVX512ER)) + cpu_features->feature[index_arch_Prefer_No_VZEROUPPER] + |= bit_arch_Prefer_No_VZEROUPPER; + /* 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] diff --git a/sysdeps/x86/cpu-features.h b/sysdeps/x86/cpu-features.h index 95f0fcff87..2ee8a0a350 100644 --- a/sysdeps/x86/cpu-features.h +++ b/sysdeps/x86/cpu-features.h @@ -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) @@ -236,6 +241,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 +264,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 commit 06d79808f6faf6025c5a7d4e27d949a8216275cc Author: H.J. Lu Date: Fri Apr 28 10:04:15 2017 -0700 x86: Use AVX2 memcpy/memset on Skylake server [BZ #21396] On Skylake server, AVX512 load/store instructions in memcpy/memset may lead to lower CPU turbo frequency in certain situations. Use of AVX2 in memcpy/memset has been observed to have improved overall performance in many workloads due to the higher frequency. Since AVX512ER is unique to Xeon Phi, this patch sets Prefer_No_AVX512 if AVX512ER isn't available so that AVX2 versions of memcpy/memset are used on Skylake server. [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. (cherry picked from commit 4cb334c4d6249686653137ec273d081371b3672d) diff --git a/ChangeLog b/ChangeLog index dc49c78b8c..adebc03b78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,26 @@ 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 diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c index 41d0be2815..9afd74c42e 100644 --- a/sysdeps/x86/cpu-features.c +++ b/sysdeps/x86/cpu-features.c @@ -225,10 +225,14 @@ init_cpu_features (struct cpu_features *cpu_features) |= bit_arch_AVX_Fast_Unaligned_Load; /* Since AVX512ER is unique to Xeon Phi, set Prefer_No_VZEROUPPER - if AVX512ER is available. */ + 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; /* To avoid SSE transition penalty, use _dl_runtime_resolve_slow. If XGETBV suports ECX == 1, use _dl_runtime_resolve_opt. */ diff --git a/sysdeps/x86/cpu-features.h b/sysdeps/x86/cpu-features.h index 2ee8a0a350..a409db67d8 100644 --- a/sysdeps/x86/cpu-features.h +++ b/sysdeps/x86/cpu-features.h @@ -39,6 +39,7 @@ #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 << 22) /* CPUID Feature flags. */ @@ -116,6 +117,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) @@ -298,6 +300,7 @@ extern const struct cpu_features *__get_cpu_features (void) # 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 #endif /* !__ASSEMBLER__ */ 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 commit ccb4fd7a657b0fbc4890c98f4586d58a135fc583 Author: Adhemerval Zanella Date: Tue Mar 14 14:16:13 2017 -0300 Fix i686 memchr overflow calculation (BZ#21182) This patch fixes the regression added by 23d2770 for final address overflow calculation. The subtraction of the considered size (16) at line 120 is at wrong place, for sizes less than 16 subsequent overflow check will not take in consideration an invalid size (since the subtraction will be negative). Also, the lea instruction also does not raise the carry flag (CF) that is used in subsequent jbe to check for overflow. The fix is to follow x86_64 logic from 3daef2c where the overflow is first check and a sub instruction is issued. In case of resulting negative size, CF will be set by the sub instruction and a NULL result will be returned. The patch also add similar tests reported in bug report. Checked on i686-linux-gnu and x86_64-linux-gnu. * 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. Cherry-pick of 3abeeec5f46ff036bd9df60bb096e20314ccd078. diff --git a/ChangeLog b/ChangeLog index adebc03b78..ac19e98613 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +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] 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/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 commit 34b6f41c14d09fe627c6a6224880d76d0959079e Author: Joseph Myers Date: Wed Mar 15 17:32:46 2017 +0000 Fix test-math-vector-sincos.h aliasing. x86_64 libmvec tests have been failing to build lately with GCC mainline with -Wuninitialized errors, and Markus Trippelsdorf traced this to an aliasing issue . This patch fixes the aliasing issue, so that the vectors-of-pointers are initialized using a union instead of pointer casts. This also fixes the testsuite build failures with GCC mainline. Tested for x86_64 (full testsuite with GCC 6; testsuite build with GCC mainline with build-many-glibcs.py). * 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. (cherry picked from commit ffe308e4fcf2f276c87fd405596569ba52ad0a29) diff --git a/ChangeLog b/ChangeLog index ac19e98613..290515a58e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +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] 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); \ commit b8d2e394a2900cef5bbbe0503f15960f64a943b1 Author: Florian Weimer Date: Fri Jun 9 13:21:43 2017 +0200 fork: Remove bogus parent PID assertions [BZ #21386] (cherry picked from commit 1d2bc2eae969543b89850e35e532f3144122d80a) diff --git a/ChangeLog b/ChangeLog index 290515a58e..d44aff0a36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +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): diff --git a/NEWS b/NEWS index fee1cf8ca8..d52872323d 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ The following bugs are resolved with this release: [21109] Tunables broken on big-endian [21115] sunrpc: Use-after-free in error path in clntudp_call [21289] Fix symbol redirect for fts_set + [21386] Assertion in fork for distinct parent PID is incorrect Version 2.25 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) { commit f92b1025980a939645b1ec7e550411a05ac7c76f Author: Santhosh Thottingal Date: Sun Jun 11 10:08:37 2017 -0400 Correct collation rules for Malayalam. [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. 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 commit 3c7cd21290cabdadd72984fb69bc51e64ff1002d Author: Florian Weimer Date: Mon Jun 19 18:31:27 2017 +0200 CVE-2017-1000366: Ignore LD_LIBRARY_PATH for AT_SECURE=1 programs [BZ #21624] LD_LIBRARY_PATH can only be used to reorder system search paths, which is not useful functionality. This makes an exploitable unbounded alloca in _dl_init_paths unreachable for AT_SECURE=1 programs. (cherry picked from commit f6110a8fee2ca36f8e2d2abecf3cba9fa7b8ea7d) diff --git a/ChangeLog b/ChangeLog index d44aff0a36..14ff8b8bb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +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] diff --git a/NEWS b/NEWS index d52872323d..29079e8586 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,7 @@ The following bugs are resolved with this release: [21115] sunrpc: Use-after-free in error path in clntudp_call [21289] Fix symbol redirect for fts_set [21386] Assertion in fork for distinct parent PID is incorrect + [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366) Version 2.25 diff --git a/elf/rtld.c b/elf/rtld.c index a036ece956..2fc33a6178 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -2418,7 +2418,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; commit 46703a3995aa3ca2b816814aa4ad05ed524194dd Author: Florian Weimer Date: Mon Jun 19 22:31:04 2017 +0200 ld.so: Reject overly long LD_PRELOAD path elements (cherry picked from commit 6d0ba622891bed9d8394eef1935add53003b12e8) diff --git a/ChangeLog b/ChangeLog index 14ff8b8bb3..fbe4db2cf7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 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 diff --git a/elf/rtld.c b/elf/rtld.c index 2fc33a6178..4badcf6590 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -99,6 +99,35 @@ 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. */ static struct audit_list @@ -716,6 +745,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, @@ -1462,23 +1527,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); commit c69d4a0f680a24fdbe323764a50382ad324041e9 Author: Florian Weimer Date: Mon Jun 19 22:32:12 2017 +0200 ld.so: Reject overly long LD_AUDIT path elements Also only process the last LD_AUDIT entry. (cherry picked from commit 81b82fb966ffbd94353f793ad17116c6088dedd9) diff --git a/ChangeLog b/ChangeLog index fbe4db2cf7..67ce0aa4de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 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. diff --git a/elf/rtld.c b/elf/rtld.c index 4badcf6590..369724babe 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -129,13 +129,91 @@ dso_name_valid_for_suid (const char *p) 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 @@ -1303,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. */ @@ -1318,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 @@ -1329,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; @@ -1342,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); } @@ -1446,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. */ @@ -1713,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 (); @@ -2344,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. */ @@ -2410,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: commit 3776f38fcd267c127ba5eb222e2c614c191744aa Author: Siddhesh Poyarekar Date: Tue Jun 20 05:59:17 2017 +0200 Ignore and remove LD_HWCAP_MASK for AT_SECURE programs (bug #21209) The LD_HWCAP_MASK environment variable may alter the selection of function variants for some architectures. For AT_SECURE process it means that if an outdated routine has a bug that would otherwise not affect newer platforms by default, LD_HWCAP_MASK will allow that bug to be exploited. To be on the safe side, ignore and disable LD_HWCAP_MASK for setuid binaries. [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. (cherry picked from commit 1c1243b6fc33c029488add276e56570a07803bfd) diff --git a/ChangeLog b/ChangeLog index 67ce0aa4de..85f1e0a495 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +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. diff --git a/NEWS b/NEWS index 29079e8586..7be238c404 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ The following bugs are resolved with this release: [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 [21289] Fix symbol redirect for fts_set [21386] Assertion in fork for distinct parent PID is incorrect [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366) 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/rtld.c b/elf/rtld.c index 369724babe..9362a21e73 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -2534,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; 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/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" \ commit adc7e06fb412a2a1ee52f8cb788caf436335b9f3 Author: Florian Weimer Date: Wed Jun 14 08:11:22 2017 +0200 i686: Add missing IS_IN (libc) guards to vectorized strcspn Since commit d957c4d3fa48d685ff2726c605c988127ef99395 (i386: Compile rtld-*.os with -mno-sse -mno-mmx -mfpmath=387), vector intrinsics can no longer be used in ld.so, even if the compiled code never makes it into the final ld.so link. This commit adds the missing IS_IN (libc) guard to the SSE 4.2 strcspn implementation, so that it can be used from ld.so in the future. (cherry picked from commit 69052a3a95da37169a08f9e59b2cc1808312753c) diff --git a/ChangeLog b/ChangeLog index 85f1e0a495..73546da9e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +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] 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