mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 17:58:30 +08:00
Compare commits
145
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0408ec51e | ||
|
|
8440c51d89 | ||
|
|
e73ac9ce90 | ||
|
|
d83ba68a3e | ||
|
|
60bc81ba47 | ||
|
|
aa6e767582 | ||
|
|
2ad78b78d3 | ||
|
|
f728a542bf | ||
|
|
3a84199109 | ||
|
|
c69b892fea | ||
|
|
80647620ce | ||
|
|
59c463c4ea | ||
|
|
e6597e77d1 | ||
|
|
97a5229eaf | ||
|
|
8d525c4a76 | ||
|
|
6b95c49d8e | ||
|
|
7118ba3493 | ||
|
|
1324858a3f | ||
|
|
71616d59ec | ||
|
|
e84db8f9eb | ||
|
|
9331dbdcd7 | ||
|
|
dcc03e7081 | ||
|
|
edcf13e25c | ||
|
|
abf2e34ee6 | ||
|
|
8343b9da62 | ||
|
|
ea2da72230 | ||
|
|
41acd32eb7 | ||
|
|
3e75e676da | ||
|
|
5d4c589ca4 | ||
|
|
aedc861c42 | ||
|
|
771c846a71 | ||
|
|
2ee370613c | ||
|
|
d11bdaa1f1 | ||
|
|
fc7ef00f02 | ||
|
|
8f8022df07 | ||
|
|
f67d3f0fa3 | ||
|
|
e75e7573e1 | ||
|
|
bf19b5fdcd | ||
|
|
9bb04ec133 | ||
|
|
c3fe737243 | ||
|
|
8871213e33 | ||
|
|
ea00a80db7 | ||
|
|
ab8b49432b | ||
|
|
0e64ee7986 | ||
|
|
e3ae300f3f | ||
|
|
e552550b40 | ||
|
|
5515224932 | ||
|
|
dc2d4b0ccd | ||
|
|
196990f97a | ||
|
|
346d7f942d | ||
|
|
84e7ab5b1b | ||
|
|
1ae1ef2e0d | ||
|
|
d3f885d7ea | ||
|
|
d8bda0713b | ||
|
|
dbdcd29e71 | ||
|
|
6bdff2e237 | ||
|
|
0fdd895d77 | ||
|
|
eda5f13d71 | ||
|
|
b091ef7e2f | ||
|
|
8fbf5688ed | ||
|
|
1f60575f88 | ||
|
|
8a4bf46fe7 | ||
|
|
baf35427d6 | ||
|
|
1998843fb7 | ||
|
|
26748a28ca | ||
|
|
a8920e694a | ||
|
|
778a7c1825 | ||
|
|
6474863dcb | ||
|
|
17357d93cb | ||
|
|
8f50b4a454 | ||
|
|
4444f6a92b | ||
|
|
b2b39e6671 | ||
|
|
ee68a4419a | ||
|
|
3b587362bd | ||
|
|
a06cc8caa7 | ||
|
|
717743bb07 | ||
|
|
ea54198514 | ||
|
|
2b54f16a8a | ||
|
|
7bd7ddfab1 | ||
|
|
b0afcf5db7 | ||
|
|
59f0c83e71 | ||
|
|
0e6f64d9d0 | ||
|
|
06e775f464 | ||
|
|
8182ccd9b8 | ||
|
|
46acbd0582 | ||
|
|
0fc015b6db | ||
|
|
61bebc863e | ||
|
|
74e1eb9078 | ||
|
|
595f287ae0 | ||
|
|
864ea5f657 | ||
|
|
0279dcae88 | ||
|
|
ac93084c08 | ||
|
|
b64e029866 | ||
|
|
4e4cfd7761 | ||
|
|
e0252d3c64 | ||
|
|
c71ddec7b3 | ||
|
|
bc5ace67fe | ||
|
|
4b3a995ad6 | ||
|
|
92a0e0c617 | ||
|
|
02aaa3c749 | ||
|
|
47db584c74 | ||
|
|
092788065e | ||
|
|
42f78808b1 | ||
|
|
80fadd69ca | ||
|
|
a707dc9bd2 | ||
|
|
c6f84dffdf | ||
|
|
604c01c69a | ||
|
|
725118c013 | ||
|
|
fa13872f59 | ||
|
|
4a60d7e30a | ||
|
|
d215bbdff3 | ||
|
|
f40b4e86f5 | ||
|
|
241af18538 | ||
|
|
f827b1cec3 | ||
|
|
5b3f978ccf | ||
|
|
05dcf1ea88 | ||
|
|
ea60566c17 | ||
|
|
e249a1e2a3 | ||
|
|
da3e97f94c | ||
|
|
0edeadc0d3 | ||
|
|
7cdbe5b6f9 | ||
|
|
49f97e641e | ||
|
|
adc7e06fb4 | ||
|
|
3776f38fcd | ||
|
|
c69d4a0f68 | ||
|
|
46703a3995 | ||
|
|
3c7cd21290 | ||
|
|
f92b102598 | ||
|
|
b8d2e394a2 | ||
|
|
34b6f41c14 | ||
|
|
ccb4fd7a65 | ||
|
|
06d79808f6 | ||
|
|
4c6f97798f | ||
|
|
b30b1c97cc | ||
|
|
903b77defb | ||
|
|
df29db0bec | ||
|
|
74522eeeaa | ||
|
|
f035c8d055 | ||
|
|
27ab0d9518 | ||
|
|
0889003c67 | ||
|
|
8b3caa41b9 | ||
|
|
69e0a87cc4 | ||
|
|
93cf93e06c | ||
|
|
045e368799 | ||
|
|
58520986c3 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -127,6 +127,14 @@ $(common-objpfx)cstdlib: $(c++-cstdlib-header)
|
||||
$(common-objpfx)cmath: $(c++-cmath-header)
|
||||
$(INSTALL_DATA) $< $@T
|
||||
$(move-if-change) $@T $@
|
||||
ifneq (,$(c++-bits-std_abs-h))
|
||||
# Also make a copy of <bits/std_abs.h> from GCC 7 to prevent it from
|
||||
# including /usr/include/stdlib.h.
|
||||
before-compile := $(common-objpfx)bits/std_abs.h $(before-compile)
|
||||
$(common-objpfx)bits/std_abs.h: $(c++-bits-std_abs-h)
|
||||
$(INSTALL_DATA) $< $@T
|
||||
$(move-if-change) $@T $@
|
||||
endif
|
||||
endif
|
||||
|
||||
before-compile := $(common-objpfx)libc-abis.h $(before-compile)
|
||||
@@ -588,7 +596,7 @@ $(LINK.o) -shared -static-libgcc -Wl,-O1 $(sysdep-LDFLAGS) \
|
||||
$(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \
|
||||
$(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \
|
||||
-Wl,-soname=lib$(libprefix)$(@F:lib%.so=%).so$($(@F)-version) \
|
||||
$(LDFLAGS.so) $(LDFLAGS-$(@F:lib%.so=%).so) \
|
||||
$(LDFLAGS.so) $(LDFLAGS-lib.so) $(LDFLAGS-$(@F:lib%.so=%).so) \
|
||||
-L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link)
|
||||
endef
|
||||
|
||||
@@ -669,14 +677,17 @@ $(build-module-helper) -o $@ $(shlib-lds-flags) \
|
||||
$(call after-link,$@)
|
||||
endef
|
||||
|
||||
# sofini.os must be placed last since it terminates .eh_frame section.
|
||||
build-module-helper-objlist = \
|
||||
$(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\
|
||||
$(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \
|
||||
$(elf-objpfx)sofini.os \
|
||||
$(link-libc-deps),$^))
|
||||
|
||||
build-module-objlist = $(build-module-helper-objlist) $(LDLIBS-$(@F:%.so=%).so)
|
||||
build-shlib-objlist = $(build-module-helper-objlist) \
|
||||
$(LDLIBS-$(@F:lib%.so=%).so)
|
||||
$(LDLIBS-$(@F:lib%.so=%).so) \
|
||||
$(filter $(elf-objpfx)sofini.os,$^)
|
||||
|
||||
# Don't try to use -lc when making libc.so itself.
|
||||
# Also omits crti.o and crtn.o, which we do not want
|
||||
@@ -686,10 +697,6 @@ LDFLAGS-c.so = -nostdlib -nostartfiles
|
||||
LDLIBS-c.so += $(libc.so-gnulib)
|
||||
# Give libc.so an entry point and make it directly runnable itself.
|
||||
LDFLAGS-c.so += -e __libc_main
|
||||
# If lazy relocation is disabled add the -z now flag.
|
||||
ifeq ($(bind-now),yes)
|
||||
LDFLAGS-c.so += -Wl,-z,now
|
||||
endif
|
||||
# Pre-link the objects of libc_pic.a so that we can locally resolve
|
||||
# COMMON symbols before we link against ld.so. This is because ld.so
|
||||
# contains some of libc_pic.a already, which will prevent the COMMONs
|
||||
@@ -1104,7 +1111,8 @@ $(common-objpfx)format.lds: $(..)scripts/output-format.sed \
|
||||
ifneq (unknown,$(output-format))
|
||||
echo > $@.new 'OUTPUT_FORMAT($(output-format))'
|
||||
else
|
||||
$(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) $(LDFLAGS.so) \
|
||||
$(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) \
|
||||
$(LDFLAGS.so) $(LDFLAGS-lib.so) \
|
||||
-x c /dev/null -o $@.so -Wl,--verbose -v 2>&1 \
|
||||
| sed -n -f $< > $@.new
|
||||
test -s $@.new
|
||||
|
||||
@@ -4,6 +4,113 @@ See the end for copying conditions.
|
||||
|
||||
Please send GNU C library bug reports via <http://sourceware.org/bugzilla/>
|
||||
using `glibc' in the "product" field.
|
||||
|
||||
Version 2.25.1
|
||||
|
||||
Security related changes:
|
||||
|
||||
* The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes,
|
||||
to avoid fragmentation-based spoofing attacks.
|
||||
|
||||
CVE-2017-15670: The glob function, when invoked with GLOB_TILDE, suffered
|
||||
from a one-byte overflow during ~ operator processing (either on the stack
|
||||
or the heap, depending on the length of the user name).
|
||||
|
||||
CVE-2017-15671: The glob function, when invoked with GLOB_TILDE,
|
||||
would sometimes fail to free memory allocated during ~ operator
|
||||
processing, leading to a memory leak and, potentially, to a denial
|
||||
of service.
|
||||
|
||||
CVE-2017-15804: The glob function, when invoked with GLOB_TILDE and
|
||||
without GLOB_NOESCAPE, could write past the end of a buffer while
|
||||
unescaping user names. Reported by Tim Rühsen.
|
||||
|
||||
CVE-2017-1000408: Incorrect array size computation in _dl_init_paths leads
|
||||
to the allocation of too much memory. (This is not a security bug per se,
|
||||
it is mentioned here only because of the CVE assignment.) Reported by
|
||||
Qualys.
|
||||
|
||||
CVE-2017-1000409: Buffer overflow in _dl_init_paths due to miscomputation
|
||||
of the number of search path components. (This is not a security
|
||||
vulnerability per se because no trust boundary is crossed if the fix for
|
||||
CVE-2017-1000366 has been applied, but it is mentioned here only because
|
||||
of the CVE assignment.) Reported by Qualys.
|
||||
|
||||
CVE-2017-16997: Incorrect handling of RPATH or RUNPATH containing $ORIGIN
|
||||
for AT_SECURE or SUID binaries could be used to load libraries from the
|
||||
current directory.
|
||||
|
||||
CVE-2018-1000001: Buffer underflow in realpath function when getcwd function
|
||||
succeeds without returning an absolute path due to unexpected behaviour
|
||||
of the Linux kernel getcwd syscall. Reported by halfdog.
|
||||
|
||||
CVE-2018-6485: The posix_memalign and memalign functions, when called with
|
||||
an object size near the value of SIZE_MAX, would return a pointer to a
|
||||
buffer which is too small, instead of NULL. Reported by Jakub Wilk.
|
||||
|
||||
CVE-2018-6551: The malloc function, when called with an object size near
|
||||
the value of SIZE_MAX, would return a pointer to a buffer which is too
|
||||
small, instead of NULL.
|
||||
|
||||
CVE-2019-6488: On x32, the size_t parameter may be passed in the lower
|
||||
32 bits of a 64-bit register with with non-zero upper 32 bit. When it
|
||||
happened, accessing the 32-bit size_t value as the full 64-bit register
|
||||
in the assembly string/memory functions would cause a buffer overflow.
|
||||
Reported by H.J. Lu.
|
||||
|
||||
CVE-2019-7309: x86-64 memcmp used signed Jcc instructions to check
|
||||
size. For x86-64, memcmp on an object size larger than SSIZE_MAX
|
||||
has undefined behavior. On x32, the size_t argument may be passed
|
||||
in the lower 32 bits of the 64-bit RDX register with non-zero upper
|
||||
32 bits. When it happened with the sign bit of RDX register set,
|
||||
memcmp gave the wrong result since it treated the size argument as
|
||||
zero. Reported by H.J. Lu.
|
||||
|
||||
CVE-2019-19126: ld.so failed to ignore the LD_PREFER_MAP_32BIT_EXEC
|
||||
environment variable during program execution after a security
|
||||
transition, allowing local attackers to restrict the possible mapping
|
||||
addresses for loaded libraries and thus bypass ASLR for a setuid
|
||||
program. Reported by Marcin Kościelnicki.
|
||||
|
||||
The following bugs are resolved with this release:
|
||||
|
||||
[20257] sunrpc: clntudp_call does not enforce timeout when receiving data
|
||||
[20568] Fix crash in _IO_wfile_sync
|
||||
[21015] Document and fix --enable-bind-now
|
||||
[21109] Tunables broken on big-endian
|
||||
[21115] sunrpc: Use-after-free in error path in clntudp_call
|
||||
[21209] Ignore and remove LD_HWCAP_MASK for AT_SECURE programs
|
||||
[21242] assert: Suppress pedantic warning caused by statement expression
|
||||
[21265] x86-64: Use fxsave/xsave/xsavec in _dl_runtime_resolve
|
||||
[21289] Fix symbol redirect for fts_set
|
||||
[21295] getaddrinfo: Merge IPv6 addresses and IPv4 addresses
|
||||
[21298] rwlock can deadlock on frequent reader/writer phase switching
|
||||
[21359] resolv: Support an exactly sized buffer in ns_name_pack
|
||||
[21386] Assertion in fork for distinct parent PID is incorrect
|
||||
[21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366)
|
||||
[21654] nss: Fix invalid cast in group merging
|
||||
[21744] tunables: Use direct syscall for access
|
||||
[21778] Robust mutex may deadlock
|
||||
[21915] getaddrinfo: incorrect result handling for NSS service modules
|
||||
[21922] getaddrinfo with AF_INET(6) returns EAI_NONAME, not EAI_NODATA
|
||||
[21972] assert macro requires operator== (int) for its argument type
|
||||
[22078] nss_files performance issue in hosts multi mode
|
||||
[22322] libc: [mips64] wrong bits/long-double.h installed
|
||||
[22343] malloc: Integer overflow in posix_memalign (CVE-2018-6485)
|
||||
[22636] PTHREAD_STACK_MIN is too small on x86-64
|
||||
[22627] $ORIGIN in $LD_LIBRARY_PATH is substituted twice
|
||||
[22637] nptl: Fix stack guard size accounting
|
||||
[22679] getcwd(3) can succeed without returning an absolute path
|
||||
(CVE-2018-1000001)
|
||||
[22685] powerpc: Fix syscalls during early process initialization
|
||||
[22715] x86-64: Properly align La_x86_64_retval to VEC_SIZE
|
||||
[22774] malloc: Integer overflow in malloc (CVE-2018-6551)
|
||||
[23538] pthread_cond_broadcast: Fix waiters-after-spinning case
|
||||
[24027] malloc: Integer overflow in realloc
|
||||
[24097] Can't use 64-bit register for size_t in assembly codes for x32 (CVE-2019-6488)
|
||||
[24155] x32 memcmp can treat positive length as 0 (if sign bit in RDX is set) (CVE-2019-7309)
|
||||
[25204] Ignore LD_PREFER_MAP_32BIT_EXEC for SUID programs
|
||||
|
||||
|
||||
Version 2.25
|
||||
|
||||
|
||||
+10
-1
@@ -25,6 +25,15 @@ include ../Makeconfig
|
||||
headers := assert.h
|
||||
|
||||
routines := assert assert-perr __assert
|
||||
tests := test-assert test-assert-perr
|
||||
tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++
|
||||
|
||||
ifeq ($(have-cxx-thread_local),yes)
|
||||
CFLAGS-tst-assert-c++.o = -std=c++11
|
||||
LDLIBS-tst-assert-c++ = -lstdc++
|
||||
CFLAGS-tst-assert-g++.o = -std=gnu++11
|
||||
LDLIBS-tst-assert-g++ = -lstdc++
|
||||
else
|
||||
tests-unsupported += tst-assert-c++ tst-assert-g++
|
||||
endif
|
||||
|
||||
include ../Rules
|
||||
|
||||
+14
-4
@@ -85,19 +85,29 @@ __END_DECLS
|
||||
/* When possible, define assert so that it does not add extra
|
||||
parentheses around EXPR. Otherwise, those added parentheses would
|
||||
suppress warnings we'd expect to be detected by gcc's -Wparentheses. */
|
||||
# if !defined __GNUC__ || defined __STRICT_ANSI__
|
||||
# if defined __cplusplus
|
||||
# define assert(expr) \
|
||||
(static_cast <bool> (expr) \
|
||||
? void (0) \
|
||||
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
|
||||
# elif !defined __GNUC__ || defined __STRICT_ANSI__
|
||||
# define assert(expr) \
|
||||
((expr) \
|
||||
? __ASSERT_VOID_CAST (0) \
|
||||
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
|
||||
# else
|
||||
/* The first occurrence of EXPR is not evaluated due to the sizeof,
|
||||
but will trigger any pedantic warnings masked by the __extension__
|
||||
for the second occurrence. The ternary operator is required to
|
||||
support function pointers and bit fields in this context, and to
|
||||
suppress the evaluation of variable length arrays. */
|
||||
# define assert(expr) \
|
||||
({ \
|
||||
((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
|
||||
if (expr) \
|
||||
; /* empty */ \
|
||||
else \
|
||||
__assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
|
||||
})
|
||||
}))
|
||||
# endif
|
||||
|
||||
# ifdef __USE_GNU
|
||||
@@ -113,7 +123,7 @@ __END_DECLS
|
||||
C9x has a similar variable called __func__, but prefer the GCC one since
|
||||
it demangles C++ function names. */
|
||||
# if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4)
|
||||
# define __ASSERT_FUNCTION __PRETTY_FUNCTION__
|
||||
# define __ASSERT_FUNCTION __extension__ __PRETTY_FUNCTION__
|
||||
# else
|
||||
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
|
||||
# define __ASSERT_FUNCTION __func__
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Tests for interactions between C++ and assert.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/* The C++ standard requires that if the assert argument is a constant
|
||||
subexpression, then the assert itself is one, too. */
|
||||
constexpr int
|
||||
check_constexpr ()
|
||||
{
|
||||
return (assert (true), 1);
|
||||
}
|
||||
|
||||
/* Objects of this class can be contextually converted to bool, but
|
||||
cannot be compared to int. */
|
||||
struct no_int
|
||||
{
|
||||
no_int () = default;
|
||||
no_int (const no_int &) = delete;
|
||||
|
||||
explicit operator bool () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator! () const; /* No definition. */
|
||||
template <class T> bool operator== (T) const; /* No definition. */
|
||||
template <class T> bool operator!= (T) const; /* No definition. */
|
||||
};
|
||||
|
||||
/* This class tests that operator== is not used by assert. */
|
||||
struct bool_and_int
|
||||
{
|
||||
bool_and_int () = default;
|
||||
bool_and_int (const no_int &) = delete;
|
||||
|
||||
explicit operator bool () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator! () const; /* No definition. */
|
||||
template <class T> bool operator== (T) const; /* No definition. */
|
||||
template <class T> bool operator!= (T) const; /* No definition. */
|
||||
};
|
||||
|
||||
static int
|
||||
do_test ()
|
||||
{
|
||||
{
|
||||
no_int value;
|
||||
assert (value);
|
||||
}
|
||||
|
||||
{
|
||||
bool_and_int value;
|
||||
assert (value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Tests for interactions between C++ and assert. GNU C++11 version.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <tst-assert-c++.cc>
|
||||
@@ -47,6 +47,7 @@ sysincludes = @SYSINCLUDES@
|
||||
c++-sysincludes = @CXX_SYSINCLUDES@
|
||||
c++-cstdlib-header = @CXX_CSTDLIB_HEADER@
|
||||
c++-cmath-header = @CXX_CMATH_HEADER@
|
||||
c++-bits-std_abs-h = @CXX_BITS_STD_ABS_H@
|
||||
all-warnings = @all_warnings@
|
||||
enable-werror = @enable_werror@
|
||||
|
||||
|
||||
@@ -634,6 +634,7 @@ BISON
|
||||
INSTALL_INFO
|
||||
PERL
|
||||
BASH_SHELL
|
||||
CXX_BITS_STD_ABS_H
|
||||
CXX_CMATH_HEADER
|
||||
CXX_CSTDLIB_HEADER
|
||||
CXX_SYSINCLUDES
|
||||
@@ -5318,14 +5319,17 @@ fi
|
||||
# copy of those headers in Makerules.
|
||||
if test -n "$CXX"; then
|
||||
find_cxx_header () {
|
||||
echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
|
||||
echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \
|
||||
| sed -n "\,$1:,{s/:\$//;p}"
|
||||
}
|
||||
CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
|
||||
CXX_CMATH_HEADER="$(find_cxx_header cmath)"
|
||||
CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
# Test if LD_LIBRARY_PATH contains the notation for the current directory
|
||||
# since this would lead to problems installing/building glibc.
|
||||
# LD_LIBRARY_PATH contains the current directory if one of the following
|
||||
|
||||
+4
-1
@@ -1176,13 +1176,16 @@ AC_SUBST(CXX_SYSINCLUDES)
|
||||
# copy of those headers in Makerules.
|
||||
if test -n "$CXX"; then
|
||||
find_cxx_header () {
|
||||
echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
|
||||
echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \
|
||||
| sed -n "\,$1:,{s/:\$//;p}"
|
||||
}
|
||||
CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
|
||||
CXX_CMATH_HEADER="$(find_cxx_header cmath)"
|
||||
CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)"
|
||||
fi
|
||||
AC_SUBST(CXX_CSTDLIB_HEADER)
|
||||
AC_SUBST(CXX_CMATH_HEADER)
|
||||
AC_SUBST(CXX_BITS_STD_ABS_H)
|
||||
|
||||
# Test if LD_LIBRARY_PATH contains the notation for the current directory
|
||||
# since this would lead to problems installing/building glibc.
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+47
-53
@@ -37,6 +37,7 @@
|
||||
#include <sysdep.h>
|
||||
#include <stap-probe.h>
|
||||
#include <libc-internal.h>
|
||||
#include <array_length.h>
|
||||
|
||||
#include <dl-dst.h>
|
||||
#include <dl-load.h>
|
||||
@@ -103,7 +104,9 @@ static size_t ncapstr attribute_relro;
|
||||
static size_t max_capstrlen attribute_relro;
|
||||
|
||||
|
||||
/* Get the generated information about the trusted directories. */
|
||||
/* Get the generated information about the trusted directories. Use
|
||||
an array of concatenated strings to avoid relocations. See
|
||||
gen-trusted-dirs.awk. */
|
||||
#include "trusted-dirs.h"
|
||||
|
||||
static const char system_dirs[] = SYSTEM_DIRS;
|
||||
@@ -111,9 +114,7 @@ static const size_t system_dirs_len[] =
|
||||
{
|
||||
SYSTEM_DIRS_LEN
|
||||
};
|
||||
#define nsystem_dirs_len \
|
||||
(sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
|
||||
|
||||
#define nsystem_dirs_len array_length (system_dirs_len)
|
||||
|
||||
static bool
|
||||
is_trusted_path (const char *path, size_t len)
|
||||
@@ -433,32 +434,41 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
|
||||
{
|
||||
char *cp;
|
||||
size_t nelems = 0;
|
||||
char *to_free;
|
||||
|
||||
while ((cp = __strsep (&rpath, sep)) != NULL)
|
||||
{
|
||||
struct r_search_path_elem *dirp;
|
||||
char *to_free = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
to_free = cp = expand_dynamic_string_token (l, cp, 1);
|
||||
|
||||
size_t len = strlen (cp);
|
||||
|
||||
/* `strsep' can pass an empty string. This has to be
|
||||
interpreted as `use the current directory'. */
|
||||
if (len == 0)
|
||||
/* `strsep' can pass an empty string. */
|
||||
if (*cp != '\0')
|
||||
{
|
||||
static const char curwd[] = "./";
|
||||
cp = (char *) curwd;
|
||||
to_free = cp = expand_dynamic_string_token (l, cp, 1);
|
||||
|
||||
/* expand_dynamic_string_token can return NULL in case of empty
|
||||
path or memory allocation failure. */
|
||||
if (cp == NULL)
|
||||
continue;
|
||||
|
||||
/* Compute the length after dynamic string token expansion and
|
||||
ignore empty paths. */
|
||||
len = strlen (cp);
|
||||
if (len == 0)
|
||||
{
|
||||
free (to_free);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Remove trailing slashes (except for "/"). */
|
||||
while (len > 1 && cp[len - 1] == '/')
|
||||
--len;
|
||||
|
||||
/* Now add one if there is none so far. */
|
||||
if (len > 0 && cp[len - 1] != '/')
|
||||
cp[len++] = '/';
|
||||
}
|
||||
|
||||
/* Remove trailing slashes (except for "/"). */
|
||||
while (len > 1 && cp[len - 1] == '/')
|
||||
--len;
|
||||
|
||||
/* Now add one if there is none so far. */
|
||||
if (len > 0 && cp[len - 1] != '/')
|
||||
cp[len++] = '/';
|
||||
|
||||
/* Make sure we don't use untrusted directories if we run SUID. */
|
||||
if (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len))
|
||||
{
|
||||
@@ -621,6 +631,14 @@ decompose_rpath (struct r_search_path_struct *sps,
|
||||
necessary. */
|
||||
free (copy);
|
||||
|
||||
/* There is no path after expansion. */
|
||||
if (result[0] == NULL)
|
||||
{
|
||||
free (result);
|
||||
sps->dirs = (struct r_search_path_elem **) -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
sps->dirs = result;
|
||||
/* The caller will change this value if we haven't used a real malloc. */
|
||||
sps->malloced = 1;
|
||||
@@ -688,9 +706,8 @@ _dl_init_paths (const char *llp)
|
||||
+ ncapstr * sizeof (enum r_dir_status))
|
||||
/ sizeof (struct r_search_path_elem));
|
||||
|
||||
rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
|
||||
malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
|
||||
* round_size * sizeof (struct r_search_path_elem));
|
||||
rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size
|
||||
* sizeof (*rtld_search_dirs.dirs[0]));
|
||||
if (rtld_search_dirs.dirs[0] == NULL)
|
||||
{
|
||||
errstring = N_("cannot create cache for search path");
|
||||
@@ -776,37 +793,14 @@ _dl_init_paths (const char *llp)
|
||||
|
||||
if (llp != NULL && *llp != '\0')
|
||||
{
|
||||
size_t nllp;
|
||||
const char *cp = llp;
|
||||
char *llp_tmp;
|
||||
|
||||
#ifdef SHARED
|
||||
/* Expand DSTs. */
|
||||
size_t cnt = DL_DST_COUNT (llp, 1);
|
||||
if (__glibc_likely (cnt == 0))
|
||||
llp_tmp = strdupa (llp);
|
||||
else
|
||||
{
|
||||
/* Determine the length of the substituted string. */
|
||||
size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt);
|
||||
|
||||
/* Allocate the necessary memory. */
|
||||
llp_tmp = (char *) alloca (total + 1);
|
||||
llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1);
|
||||
}
|
||||
#else
|
||||
llp_tmp = strdupa (llp);
|
||||
#endif
|
||||
char *llp_tmp = strdupa (llp);
|
||||
|
||||
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
|
||||
elements it has. */
|
||||
nllp = 1;
|
||||
while (*cp)
|
||||
{
|
||||
if (*cp == ':' || *cp == ';')
|
||||
++nllp;
|
||||
++cp;
|
||||
}
|
||||
size_t nllp = 1;
|
||||
for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
|
||||
if (*cp == ':' || *cp == ';')
|
||||
++nllp;
|
||||
|
||||
env_path_list.dirs = (struct r_search_path_elem **)
|
||||
malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
# define _TUNABLE_TYPES_H_
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#define TUNABLES_INTERNAL 1
|
||||
#include "dl-tunables.h"
|
||||
|
||||
#include <not-errno.h>
|
||||
|
||||
#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
|
||||
# define GLIBC_TUNABLES "GLIBC_TUNABLES"
|
||||
#endif
|
||||
@@ -455,6 +457,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;
|
||||
|
||||
|
||||
+165
-33
@@ -99,14 +99,121 @@ uintptr_t __pointer_chk_guard_local
|
||||
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
|
||||
#endif
|
||||
|
||||
/* Length limits for names and paths, to protect the dynamic linker,
|
||||
particularly when __libc_enable_secure is active. */
|
||||
#ifdef NAME_MAX
|
||||
# define SECURE_NAME_LIMIT NAME_MAX
|
||||
#else
|
||||
# define SECURE_NAME_LIMIT 255
|
||||
#endif
|
||||
#ifdef PATH_MAX
|
||||
# define SECURE_PATH_LIMIT PATH_MAX
|
||||
#else
|
||||
# define SECURE_PATH_LIMIT 1024
|
||||
#endif
|
||||
|
||||
/* List of auditing DSOs. */
|
||||
/* 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';
|
||||
}
|
||||
|
||||
/* LD_AUDIT variable contents. Must be processed before the
|
||||
audit_list below. */
|
||||
const char *audit_list_string;
|
||||
|
||||
/* Cyclic list of auditing DSOs. audit_list->next is the first
|
||||
element. */
|
||||
static struct audit_list
|
||||
{
|
||||
const char *name;
|
||||
struct audit_list *next;
|
||||
} *audit_list;
|
||||
|
||||
/* Iterator for audit_list_string followed by audit_list. */
|
||||
struct audit_list_iter
|
||||
{
|
||||
/* Tail of audit_list_string still needing processing, or NULL. */
|
||||
const char *audit_list_tail;
|
||||
|
||||
/* The list element returned in the previous iteration. NULL before
|
||||
the first element. */
|
||||
struct audit_list *previous;
|
||||
|
||||
/* Scratch buffer for returning a name which is part of
|
||||
audit_list_string. */
|
||||
char fname[SECURE_NAME_LIMIT];
|
||||
};
|
||||
|
||||
/* Initialize an audit list iterator. */
|
||||
static void
|
||||
audit_list_iter_init (struct audit_list_iter *iter)
|
||||
{
|
||||
iter->audit_list_tail = audit_list_string;
|
||||
iter->previous = NULL;
|
||||
}
|
||||
|
||||
/* Iterate through both audit_list_string and audit_list. */
|
||||
static const char *
|
||||
audit_list_iter_next (struct audit_list_iter *iter)
|
||||
{
|
||||
if (iter->audit_list_tail != NULL)
|
||||
{
|
||||
/* First iterate over audit_list_string. */
|
||||
while (*iter->audit_list_tail != '\0')
|
||||
{
|
||||
/* Split audit list at colon. */
|
||||
size_t len = strcspn (iter->audit_list_tail, ":");
|
||||
if (len > 0 && len < sizeof (iter->fname))
|
||||
{
|
||||
memcpy (iter->fname, iter->audit_list_tail, len);
|
||||
iter->fname[len] = '\0';
|
||||
}
|
||||
else
|
||||
/* Do not return this name to the caller. */
|
||||
iter->fname[0] = '\0';
|
||||
|
||||
/* Skip over the substring and the following delimiter. */
|
||||
iter->audit_list_tail += len;
|
||||
if (*iter->audit_list_tail == ':')
|
||||
++iter->audit_list_tail;
|
||||
|
||||
/* If the name is valid, return it. */
|
||||
if (dso_name_valid_for_suid (iter->fname))
|
||||
return iter->fname;
|
||||
/* Otherwise, wrap around and try the next name. */
|
||||
}
|
||||
/* Fall through to the procesing of audit_list. */
|
||||
}
|
||||
|
||||
if (iter->previous == NULL)
|
||||
{
|
||||
if (audit_list == NULL)
|
||||
/* No pre-parsed audit list. */
|
||||
return NULL;
|
||||
/* Start of audit list. The first list element is at
|
||||
audit_list->next (cyclic list). */
|
||||
iter->previous = audit_list->next;
|
||||
return iter->previous->name;
|
||||
}
|
||||
if (iter->previous == audit_list)
|
||||
/* Cyclic list wrap-around. */
|
||||
return NULL;
|
||||
iter->previous = iter->previous->next;
|
||||
return iter->previous->name;
|
||||
}
|
||||
|
||||
#ifndef HAVE_INLINED_SYSCALLS
|
||||
/* Set nonzero during loading and initialization of executable and
|
||||
libraries, cleared before the executable's entry point runs. This
|
||||
@@ -716,6 +823,42 @@ static const char *preloadlist attribute_relro;
|
||||
/* Nonzero if information about versions has to be printed. */
|
||||
static int version_info attribute_relro;
|
||||
|
||||
/* The LD_PRELOAD environment variable gives list of libraries
|
||||
separated by white space or colons that are loaded before the
|
||||
executable's dependencies and prepended to the global scope list.
|
||||
(If the binary is running setuid all elements containing a '/' are
|
||||
ignored since it is insecure.) Return the number of preloads
|
||||
performed. */
|
||||
unsigned int
|
||||
handle_ld_preload (const char *preloadlist, struct link_map *main_map)
|
||||
{
|
||||
unsigned int npreloads = 0;
|
||||
const char *p = preloadlist;
|
||||
char fname[SECURE_PATH_LIMIT];
|
||||
|
||||
while (*p != '\0')
|
||||
{
|
||||
/* Split preload list at space/colon. */
|
||||
size_t len = strcspn (p, " :");
|
||||
if (len > 0 && len < sizeof (fname))
|
||||
{
|
||||
memcpy (fname, p, len);
|
||||
fname[len] = '\0';
|
||||
}
|
||||
else
|
||||
fname[0] = '\0';
|
||||
|
||||
/* Skip over the substring and the following delimiter. */
|
||||
p += len;
|
||||
if (*p != '\0')
|
||||
++p;
|
||||
|
||||
if (dso_name_valid_for_suid (fname))
|
||||
npreloads += do_preload (fname, main_map, "LD_PRELOAD");
|
||||
}
|
||||
return npreloads;
|
||||
}
|
||||
|
||||
static void
|
||||
dl_main (const ElfW(Phdr) *phdr,
|
||||
ElfW(Word) phnum,
|
||||
@@ -1238,11 +1381,13 @@ of this helper program; chances are you did not intend to run this program.\n\
|
||||
GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
|
||||
|
||||
/* If we have auditing DSOs to load, do it now. */
|
||||
if (__glibc_unlikely (audit_list != NULL))
|
||||
bool need_security_init = true;
|
||||
if (__glibc_unlikely (audit_list != NULL)
|
||||
|| __glibc_unlikely (audit_list_string != NULL))
|
||||
{
|
||||
/* Iterate over all entries in the list. The order is important. */
|
||||
struct audit_ifaces *last_audit = NULL;
|
||||
struct audit_list *al = audit_list->next;
|
||||
struct audit_list_iter al_iter;
|
||||
audit_list_iter_init (&al_iter);
|
||||
|
||||
/* Since we start using the auditing DSOs right away we need to
|
||||
initialize the data structures now. */
|
||||
@@ -1253,9 +1398,14 @@ of this helper program; chances are you did not intend to run this program.\n\
|
||||
use different values (especially the pointer guard) and will
|
||||
fail later on. */
|
||||
security_init ();
|
||||
need_security_init = false;
|
||||
|
||||
do
|
||||
while (true)
|
||||
{
|
||||
const char *name = audit_list_iter_next (&al_iter);
|
||||
if (name == NULL)
|
||||
break;
|
||||
|
||||
int tls_idx = GL(dl_tls_max_dtv_idx);
|
||||
|
||||
/* Now it is time to determine the layout of the static TLS
|
||||
@@ -1264,7 +1414,7 @@ of this helper program; chances are you did not intend to run this program.\n\
|
||||
no DF_STATIC_TLS bit is set. The reason is that we know
|
||||
glibc will use the static model. */
|
||||
struct dlmopen_args dlmargs;
|
||||
dlmargs.fname = al->name;
|
||||
dlmargs.fname = name;
|
||||
dlmargs.map = NULL;
|
||||
|
||||
const char *objname;
|
||||
@@ -1277,7 +1427,7 @@ of this helper program; chances are you did not intend to run this program.\n\
|
||||
not_loaded:
|
||||
_dl_error_printf ("\
|
||||
ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||||
al->name, err_str);
|
||||
name, err_str);
|
||||
if (malloced)
|
||||
free ((char *) err_str);
|
||||
}
|
||||
@@ -1381,10 +1531,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||||
goto not_loaded;
|
||||
}
|
||||
}
|
||||
|
||||
al = al->next;
|
||||
}
|
||||
while (al != audit_list->next);
|
||||
|
||||
/* If we have any auditing modules, announce that we already
|
||||
have two objects loaded. */
|
||||
@@ -1462,23 +1609,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||||
|
||||
if (__glibc_unlikely (preloadlist != NULL))
|
||||
{
|
||||
/* The LD_PRELOAD environment variable gives list of libraries
|
||||
separated by white space or colons that are loaded before the
|
||||
executable's dependencies and prepended to the global scope
|
||||
list. If the binary is running setuid all elements
|
||||
containing a '/' are ignored since it is insecure. */
|
||||
char *list = strdupa (preloadlist);
|
||||
char *p;
|
||||
|
||||
HP_TIMING_NOW (start);
|
||||
|
||||
/* Prevent optimizing strsep. Speed is not important here. */
|
||||
while ((p = (strsep) (&list, " :")) != NULL)
|
||||
if (p[0] != '\0'
|
||||
&& (__builtin_expect (! __libc_enable_secure, 1)
|
||||
|| strchr (p, '/') == NULL))
|
||||
npreloads += do_preload (p, main_map, "LD_PRELOAD");
|
||||
|
||||
npreloads += handle_ld_preload (preloadlist, main_map);
|
||||
HP_TIMING_NOW (stop);
|
||||
HP_TIMING_DIFF (diff, start, stop);
|
||||
HP_TIMING_ACCUM_NT (load_time, diff);
|
||||
@@ -1663,7 +1795,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
||||
if (tcbp == NULL)
|
||||
tcbp = init_tls ();
|
||||
|
||||
if (__glibc_likely (audit_list == NULL))
|
||||
if (__glibc_likely (need_security_init))
|
||||
/* Initialize security features. But only if we have not done it
|
||||
earlier. */
|
||||
security_init ();
|
||||
@@ -2294,9 +2426,7 @@ process_dl_audit (char *str)
|
||||
char *p;
|
||||
|
||||
while ((p = (strsep) (&str, ":")) != NULL)
|
||||
if (p[0] != '\0'
|
||||
&& (__builtin_expect (! __libc_enable_secure, 1)
|
||||
|| strchr (p, '/') == NULL))
|
||||
if (dso_name_valid_for_suid (p))
|
||||
{
|
||||
/* This is using the local malloc, not the system malloc. The
|
||||
memory can never be freed. */
|
||||
@@ -2360,7 +2490,7 @@ process_envvars (enum mode *modep)
|
||||
break;
|
||||
}
|
||||
if (memcmp (envline, "AUDIT", 5) == 0)
|
||||
process_dl_audit (&envline[6]);
|
||||
audit_list_string = &envline[6];
|
||||
break;
|
||||
|
||||
case 7:
|
||||
@@ -2404,7 +2534,8 @@ process_envvars (enum mode *modep)
|
||||
|
||||
case 10:
|
||||
/* Mask for the important hardware capabilities. */
|
||||
if (memcmp (envline, "HWCAP_MASK", 10) == 0)
|
||||
if (!__libc_enable_secure
|
||||
&& memcmp (envline, "HWCAP_MASK", 10) == 0)
|
||||
GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL,
|
||||
0, 0);
|
||||
break;
|
||||
@@ -2418,7 +2549,8 @@ process_envvars (enum mode *modep)
|
||||
|
||||
case 12:
|
||||
/* The library search path. */
|
||||
if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
|
||||
if (!__libc_enable_secure
|
||||
&& memcmp (envline, "LIBRARY_PATH", 12) == 0)
|
||||
{
|
||||
library_path = &envline[13];
|
||||
break;
|
||||
|
||||
@@ -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
|
||||
|
||||
+17
-1
@@ -85,6 +85,14 @@ __copy_grp (const struct group srcgrp, const size_t buflen,
|
||||
}
|
||||
members[i] = NULL;
|
||||
|
||||
/* Align for pointers. We can't simply align C because we need to
|
||||
align destbuf[c]. */
|
||||
if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0)
|
||||
{
|
||||
uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1);
|
||||
c += __alignof__(char **) - mis_align;
|
||||
}
|
||||
|
||||
/* Copy the pointers from the members array into the buffer and assign them
|
||||
to the gr_mem member of destgrp. */
|
||||
destgrp->gr_mem = (char **) &destbuf[c];
|
||||
@@ -129,7 +137,7 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
|
||||
|
||||
/* Get the count of group members from the last sizeof (size_t) bytes in the
|
||||
mergegrp buffer. */
|
||||
savedmemcount = (size_t) *(savedend - sizeof (size_t));
|
||||
savedmemcount = *(size_t *) (savedend - sizeof (size_t));
|
||||
|
||||
/* Get the count of new members to add. */
|
||||
for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++)
|
||||
@@ -168,6 +176,14 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
|
||||
/* Add the NULL-terminator. */
|
||||
members[savedmemcount + memcount] = NULL;
|
||||
|
||||
/* Align for pointers. We can't simply align C because we need to
|
||||
align savedbuf[c]. */
|
||||
if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0)
|
||||
{
|
||||
uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1);
|
||||
c += __alignof__(char **) - mis_align;
|
||||
}
|
||||
|
||||
/* Copy the member array back into the buffer after the member list and free
|
||||
the member array. */
|
||||
savedgrp->gr_mem = (char **) &savedbuf[c];
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
/* Allocation from a fixed-size buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Allocation buffers are used to carve out sub-allocations from a
|
||||
larger allocation. Their primary application is in writing NSS
|
||||
modules, which receive a caller-allocated buffer in which they are
|
||||
expected to store variable-length results:
|
||||
|
||||
void *buffer = ...;
|
||||
size_t buffer_size = ...;
|
||||
|
||||
struct alloc_buffer buf = alloc_buffer_create (buffer, buffer_size);
|
||||
result->gr_name = alloc_buffer_copy_string (&buf, name);
|
||||
|
||||
// Allocate a list of group_count groups and copy strings into it.
|
||||
char **group_list = alloc_buffer_alloc_array
|
||||
(&buf, char *, group_count + 1);
|
||||
if (group_list == NULL)
|
||||
return ...; // Request a larger buffer.
|
||||
for (int i = 0; i < group_count; ++i)
|
||||
group_list[i] = alloc_buffer_copy_string (&buf, group_list_src[i]);
|
||||
group_list[group_count] = NULL;
|
||||
...
|
||||
|
||||
if (alloc_buffer_has_failed (&buf))
|
||||
return ...; // Request a larger buffer.
|
||||
result->gr_mem = group_list;
|
||||
...
|
||||
|
||||
Note that it is not necessary to check the results of individual
|
||||
allocation operations if the returned pointer is not dereferenced.
|
||||
Allocation failure is sticky, so one check using
|
||||
alloc_buffer_has_failed at the end covers all previous failures.
|
||||
|
||||
A different use case involves combining multiple heap allocations
|
||||
into a single, large one. In the following example, an array of
|
||||
doubles and an array of ints is allocated:
|
||||
|
||||
size_t double_array_size = ...;
|
||||
size_t int_array_size = ...;
|
||||
|
||||
void *heap_ptr;
|
||||
struct alloc_buffer buf = alloc_buffer_allocate
|
||||
(double_array_size * sizeof (double) + int_array_size * sizeof (int),
|
||||
&heap_ptr);
|
||||
_Static_assert (__alignof__ (double) >= __alignof__ (int),
|
||||
"no padding after double array");
|
||||
double *double_array = alloc_buffer_alloc_array
|
||||
(&buf, double, double_array_size);
|
||||
int *int_array = alloc_buffer_alloc_array (&buf, int, int_array_size);
|
||||
if (alloc_buffer_has_failed (&buf))
|
||||
return ...; // Report error.
|
||||
...
|
||||
free (heap_ptr);
|
||||
|
||||
The advantage over manual coding is that the computation of the
|
||||
allocation size does not need an overflow check. In case of an
|
||||
overflow, one of the subsequent allocations from the buffer will
|
||||
fail. The initial size computation is checked for consistency at
|
||||
run time, too. */
|
||||
|
||||
#ifndef _ALLOC_BUFFER_H
|
||||
#define _ALLOC_BUFFER_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* struct alloc_buffer objects refer to a region of bytes in memory of a
|
||||
fixed size. The functions below can be used to allocate single
|
||||
objects and arrays from this memory region, or write to its end.
|
||||
On allocation failure (or if an attempt to write beyond the end of
|
||||
the buffer with one of the copy functions), the buffer enters a
|
||||
failed state.
|
||||
|
||||
struct alloc_buffer objects can be copied. The backing buffer will
|
||||
be shared, but the current write position will be independent.
|
||||
|
||||
Conceptually, the memory region consists of a current write pointer
|
||||
and a limit, beyond which the write pointer cannot move. */
|
||||
struct alloc_buffer
|
||||
{
|
||||
/* uintptr_t is used here to simplify the alignment code, and to
|
||||
avoid issues undefined subtractions if the buffer covers more
|
||||
than half of the address space (which would result in differences
|
||||
which could not be represented as a ptrdiff_t value). */
|
||||
uintptr_t __alloc_buffer_current;
|
||||
uintptr_t __alloc_buffer_end;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* The value for the __alloc_buffer_current member which marks the
|
||||
buffer as invalid (together with a zero-length buffer). */
|
||||
__ALLOC_BUFFER_INVALID_POINTER = 0,
|
||||
};
|
||||
|
||||
/* Internal function. Terminate the process using __libc_fatal. */
|
||||
void __libc_alloc_buffer_create_failure (void *start, size_t size);
|
||||
|
||||
/* Create a new allocation buffer. The byte range from START to START
|
||||
+ SIZE - 1 must be valid, and the allocation buffer allocates
|
||||
objects from that range. If START is NULL (so that SIZE must be
|
||||
0), the buffer is marked as failed immediately. */
|
||||
static inline struct alloc_buffer
|
||||
alloc_buffer_create (void *start, size_t size)
|
||||
{
|
||||
uintptr_t current = (uintptr_t) start;
|
||||
uintptr_t end = (uintptr_t) start + size;
|
||||
if (end < current)
|
||||
__libc_alloc_buffer_create_failure (start, size);
|
||||
return (struct alloc_buffer) { current, end };
|
||||
}
|
||||
|
||||
/* Internal function. See alloc_buffer_allocate below. */
|
||||
struct alloc_buffer __libc_alloc_buffer_allocate (size_t size, void **pptr)
|
||||
__attribute__ ((nonnull (2)));
|
||||
|
||||
/* Allocate a buffer of SIZE bytes using malloc. The returned buffer
|
||||
is in a failed state if malloc fails. *PPTR points to the start of
|
||||
the buffer and can be used to free it later, after the returned
|
||||
buffer has been freed. */
|
||||
static __always_inline __attribute__ ((nonnull (2)))
|
||||
struct alloc_buffer alloc_buffer_allocate (size_t size, void **pptr)
|
||||
{
|
||||
return __libc_alloc_buffer_allocate (size, pptr);
|
||||
}
|
||||
|
||||
/* Mark the buffer as failed. */
|
||||
static inline void __attribute__ ((nonnull (1)))
|
||||
alloc_buffer_mark_failed (struct alloc_buffer *buf)
|
||||
{
|
||||
buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER;
|
||||
buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER;
|
||||
}
|
||||
|
||||
/* Return the remaining number of bytes in the buffer. */
|
||||
static __always_inline __attribute__ ((nonnull (1))) size_t
|
||||
alloc_buffer_size (const struct alloc_buffer *buf)
|
||||
{
|
||||
return buf->__alloc_buffer_end - buf->__alloc_buffer_current;
|
||||
}
|
||||
|
||||
/* Return true if the buffer has been marked as failed. */
|
||||
static inline bool __attribute__ ((nonnull (1)))
|
||||
alloc_buffer_has_failed (const struct alloc_buffer *buf)
|
||||
{
|
||||
return buf->__alloc_buffer_current == __ALLOC_BUFFER_INVALID_POINTER;
|
||||
}
|
||||
|
||||
/* Add a single byte to the buffer (consuming the space for this
|
||||
byte). Mark the buffer as failed if there is not enough room. */
|
||||
static inline void __attribute__ ((nonnull (1)))
|
||||
alloc_buffer_add_byte (struct alloc_buffer *buf, unsigned char b)
|
||||
{
|
||||
if (__glibc_likely (buf->__alloc_buffer_current < buf->__alloc_buffer_end))
|
||||
{
|
||||
*(unsigned char *) buf->__alloc_buffer_current = b;
|
||||
++buf->__alloc_buffer_current;
|
||||
}
|
||||
else
|
||||
alloc_buffer_mark_failed (buf);
|
||||
}
|
||||
|
||||
/* Obtain a pointer to LENGTH bytes in BUF, and consume these bytes.
|
||||
NULL is returned if there is not enough room, and the buffer is
|
||||
marked as failed, or if the buffer has already failed.
|
||||
(Zero-length allocations from an empty buffer which has not yet
|
||||
failed succeed.) */
|
||||
static inline __attribute__ ((nonnull (1))) void *
|
||||
alloc_buffer_alloc_bytes (struct alloc_buffer *buf, size_t length)
|
||||
{
|
||||
if (length <= alloc_buffer_size (buf))
|
||||
{
|
||||
void *result = (void *) buf->__alloc_buffer_current;
|
||||
buf->__alloc_buffer_current += length;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_buffer_mark_failed (buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal function. Statically assert that the type size is
|
||||
constant and valid. */
|
||||
static __always_inline size_t
|
||||
__alloc_buffer_assert_size (size_t size)
|
||||
{
|
||||
if (!__builtin_constant_p (size))
|
||||
{
|
||||
__errordecl (error, "type size is not constant");
|
||||
error ();
|
||||
}
|
||||
else if (size == 0)
|
||||
{
|
||||
__errordecl (error, "type size is zero");
|
||||
error ();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Internal function. Statically assert that the type alignment is
|
||||
constant and valid. */
|
||||
static __always_inline size_t
|
||||
__alloc_buffer_assert_align (size_t align)
|
||||
{
|
||||
if (!__builtin_constant_p (align))
|
||||
{
|
||||
__errordecl (error, "type alignment is not constant");
|
||||
error ();
|
||||
}
|
||||
else if (align == 0)
|
||||
{
|
||||
__errordecl (error, "type alignment is zero");
|
||||
error ();
|
||||
}
|
||||
else if (!powerof2 (align))
|
||||
{
|
||||
__errordecl (error, "type alignment is not a power of two");
|
||||
error ();
|
||||
}
|
||||
return align;
|
||||
}
|
||||
|
||||
/* Internal function. Obtain a pointer to an object. */
|
||||
static inline __attribute__ ((nonnull (1))) void *
|
||||
__alloc_buffer_alloc (struct alloc_buffer *buf, size_t size, size_t align)
|
||||
{
|
||||
if (size == 1 && align == 1)
|
||||
return alloc_buffer_alloc_bytes (buf, size);
|
||||
|
||||
size_t current = buf->__alloc_buffer_current;
|
||||
size_t aligned = roundup (current, align);
|
||||
size_t new_current = aligned + size;
|
||||
if (aligned >= current /* No overflow in align step. */
|
||||
&& new_current >= size /* No overflow in size computation. */
|
||||
&& new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
|
||||
{
|
||||
buf->__alloc_buffer_current = new_current;
|
||||
return (void *) aligned;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_buffer_mark_failed (buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Obtain a TYPE * pointer to an object in BUF of TYPE. Consume these
|
||||
bytes from the buffer. Return NULL and mark the buffer as failed
|
||||
if if there is not enough room in the buffer, or if the buffer has
|
||||
failed before. */
|
||||
#define alloc_buffer_alloc(buf, type) \
|
||||
((type *) __alloc_buffer_alloc \
|
||||
(buf, __alloc_buffer_assert_size (sizeof (type)), \
|
||||
__alloc_buffer_assert_align (__alignof__ (type))))
|
||||
|
||||
/* Internal function. Obtain a pointer to an object which is
|
||||
subsequently added. */
|
||||
static inline const __attribute__ ((nonnull (1))) void *
|
||||
__alloc_buffer_next (struct alloc_buffer *buf, size_t align)
|
||||
{
|
||||
if (align == 1)
|
||||
return (const void *) buf->__alloc_buffer_current;
|
||||
|
||||
size_t current = buf->__alloc_buffer_current;
|
||||
size_t aligned = roundup (current, align);
|
||||
if (aligned >= current /* No overflow in align step. */
|
||||
&& aligned <= buf->__alloc_buffer_end) /* Room in buffer. */
|
||||
{
|
||||
buf->__alloc_buffer_current = aligned;
|
||||
return (const void *) aligned;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_buffer_mark_failed (buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Like alloc_buffer_alloc, but do not advance the pointer beyond the
|
||||
object (so a subseqent call to alloc_buffer_next or
|
||||
alloc_buffer_alloc returns the same pointer). Note that the buffer
|
||||
is still aligned according to the requirements of TYPE. The effect
|
||||
of this function is similar to allocating a zero-length array from
|
||||
the buffer. */
|
||||
#define alloc_buffer_next(buf, type) \
|
||||
((const type *) __alloc_buffer_next \
|
||||
(buf, __alloc_buffer_assert_align (__alignof__ (type))))
|
||||
|
||||
/* Internal function. Allocate an array. */
|
||||
void * __libc_alloc_buffer_alloc_array (struct alloc_buffer *buf,
|
||||
size_t size, size_t align,
|
||||
size_t count)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
/* Obtain a TYPE * pointer to an array of COUNT objects in BUF of
|
||||
TYPE. Consume these bytes from the buffer. Return NULL and mark
|
||||
the buffer as failed if if there is not enough room in the buffer,
|
||||
or if the buffer has failed before. (Zero-length allocations from
|
||||
an empty buffer which has not yet failed succeed.) */
|
||||
#define alloc_buffer_alloc_array(buf, type, count) \
|
||||
((type *) __libc_alloc_buffer_alloc_array \
|
||||
(buf, __alloc_buffer_assert_size (sizeof (type)), \
|
||||
__alloc_buffer_assert_align (__alignof__ (type)), \
|
||||
count))
|
||||
|
||||
/* Internal function. See alloc_buffer_copy_bytes below. */
|
||||
struct alloc_buffer __libc_alloc_buffer_copy_bytes (struct alloc_buffer,
|
||||
const void *, size_t)
|
||||
__attribute__ ((nonnull (2)));
|
||||
|
||||
/* Copy SIZE bytes starting at SRC into the buffer. If there is not
|
||||
enough room in the buffer, the buffer is marked as failed. No
|
||||
alignment of the buffer is performed. */
|
||||
static inline __attribute__ ((nonnull (1, 2))) void
|
||||
alloc_buffer_copy_bytes (struct alloc_buffer *buf, const void *src, size_t size)
|
||||
{
|
||||
*buf = __libc_alloc_buffer_copy_bytes (*buf, src, size);
|
||||
}
|
||||
|
||||
/* Internal function. See alloc_buffer_copy_string below. */
|
||||
struct alloc_buffer __libc_alloc_buffer_copy_string (struct alloc_buffer,
|
||||
const char *)
|
||||
__attribute__ ((nonnull (2)));
|
||||
|
||||
/* Copy the string at SRC into the buffer, including its null
|
||||
terminator. If there is not enough room in the buffer, the buffer
|
||||
is marked as failed. Return a pointer to the string. */
|
||||
static inline __attribute__ ((nonnull (1, 2))) char *
|
||||
alloc_buffer_copy_string (struct alloc_buffer *buf, const char *src)
|
||||
{
|
||||
char *result = (char *) buf->__alloc_buffer_current;
|
||||
*buf = __libc_alloc_buffer_copy_string (*buf, src);
|
||||
if (alloc_buffer_has_failed (buf))
|
||||
result = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef _ISOMAC
|
||||
libc_hidden_proto (__libc_alloc_buffer_alloc_array)
|
||||
libc_hidden_proto (__libc_alloc_buffer_allocate)
|
||||
libc_hidden_proto (__libc_alloc_buffer_copy_bytes)
|
||||
libc_hidden_proto (__libc_alloc_buffer_copy_string)
|
||||
libc_hidden_proto (__libc_alloc_buffer_create_failure)
|
||||
#endif
|
||||
|
||||
#endif /* _ALLOC_BUFFER_H */
|
||||
@@ -0,0 +1,36 @@
|
||||
/* The array_length and array_end macros.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _ARRAY_LENGTH_H
|
||||
#define _ARRAY_LENGTH_H
|
||||
|
||||
/* array_length (VAR) is the number of elements in the array VAR. VAR
|
||||
must evaluate to an array, not a pointer. */
|
||||
#define array_length(var) \
|
||||
__extension__ ({ \
|
||||
_Static_assert (!__builtin_types_compatible_p \
|
||||
(__typeof (var), __typeof (&(var)[0])), \
|
||||
"argument must be an array"); \
|
||||
sizeof (var) / sizeof ((var)[0]); \
|
||||
})
|
||||
|
||||
/* array_end (VAR) is a pointer one past the end of the array VAR.
|
||||
VAR must evaluate to an array, not a pointer. */
|
||||
#define array_end(var) (&(var)[array_length (var)])
|
||||
|
||||
#endif /* _ARRAY_LENGTH_H */
|
||||
@@ -0,0 +1 @@
|
||||
#include <libc-internal.h>
|
||||
@@ -37,8 +37,6 @@ extern void res_pquery (const res_state __statp, const unsigned char *__msg,
|
||||
extern int res_ourserver_p (const res_state __statp,
|
||||
const struct sockaddr_in6 *__inp);
|
||||
extern void __res_iclose (res_state statp, bool free_addr);
|
||||
extern int __res_nopt(res_state statp, int n0, unsigned char *buf, int buflen,
|
||||
int anslen);
|
||||
libc_hidden_proto (__res_ninit)
|
||||
libc_hidden_proto (__res_maybe_init)
|
||||
libc_hidden_proto (__res_nclose)
|
||||
@@ -91,7 +89,6 @@ libresolv_hidden_proto (__res_nameinquery)
|
||||
libresolv_hidden_proto (__res_queriesmatch)
|
||||
libresolv_hidden_proto (__res_nsend)
|
||||
libresolv_hidden_proto (__b64_ntop)
|
||||
libresolv_hidden_proto (__res_nopt)
|
||||
libresolv_hidden_proto (__dn_count_labels)
|
||||
libresolv_hidden_proto (__p_secstodate)
|
||||
|
||||
|
||||
@@ -181,13 +181,6 @@ extern int __getlogin_r_loginuid (char *name, size_t namesize)
|
||||
# include <dl-unistd.h>
|
||||
# endif
|
||||
|
||||
# if IS_IN (rtld) || !defined SHARED
|
||||
/* __access variant that does not set errno. Used in very early initialization
|
||||
code in libc.a and ld.so. It follows access return semantics (zero for
|
||||
sucess otherwise a value different than 0). */
|
||||
extern __typeof (__access) __access_noerrno attribute_hidden;
|
||||
# endif
|
||||
|
||||
__END_DECLS
|
||||
# endif
|
||||
|
||||
|
||||
+6
-2
@@ -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
|
||||
|
||||
|
||||
+122
@@ -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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <net-internal.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -20,11 +20,100 @@
|
||||
#define _NET_INTERNAL_H 1
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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 */
|
||||
|
||||
@@ -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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <inet/net-internal.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* 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 <support/test-driver.c>
|
||||
+1
-1
@@ -72,7 +72,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
|
||||
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
|
||||
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
|
||||
tst-posix_fallocate tst-posix_fallocate64 \
|
||||
tst-fts tst-fts-lfs tst-open-tmpfile
|
||||
tst-fts tst-fts-lfs tst-open-tmpfile tst-getcwd-abspath
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += $(objpfx)ftwtest.out
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/* BZ #22679 getcwd(3) should not succeed without returning an absolute path.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <support/check.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static char *chroot_dir;
|
||||
|
||||
/* The actual test. Run it in a subprocess, so that the test harness
|
||||
can remove the temporary directory in --direct mode. */
|
||||
static void
|
||||
getcwd_callback (void *closure)
|
||||
{
|
||||
xchroot (chroot_dir);
|
||||
|
||||
errno = 0;
|
||||
char *cwd = getcwd (NULL, 0);
|
||||
TEST_COMPARE (errno, ENOENT);
|
||||
TEST_VERIFY (cwd == NULL);
|
||||
|
||||
errno = 0;
|
||||
cwd = realpath (".", NULL);
|
||||
TEST_COMPARE (errno, ENOENT);
|
||||
TEST_VERIFY (cwd == NULL);
|
||||
|
||||
_exit (0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
support_become_root ();
|
||||
if (!support_can_chroot ())
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
chroot_dir = support_create_temp_directory ("tst-getcwd-abspath-");
|
||||
support_isolate_in_subprocess (getcwd_callback, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+2
-1
@@ -61,7 +61,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
|
||||
bug-memstream1 bug-wmemstream1 \
|
||||
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
|
||||
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
|
||||
tst-ftell-append tst-fputws
|
||||
tst-ftell-append tst-fputws tst-wfile-sync
|
||||
ifeq (yes,$(build-shared))
|
||||
# Add test-fopenloc only if shared library is enabled since it depends on
|
||||
# shared localedata objects.
|
||||
@@ -198,6 +198,7 @@ $(objpfx)tst-ungetwc1.out: $(gen-locales)
|
||||
$(objpfx)tst-ungetwc2.out: $(gen-locales)
|
||||
$(objpfx)tst-widetext.out: $(gen-locales)
|
||||
$(objpfx)tst_wprintf2.out: $(gen-locales)
|
||||
$(objpfx)tst-wfile-sync.out: $(gen-locales)
|
||||
endif
|
||||
|
||||
$(objpfx)test-freopen.out: test-freopen.sh $(objpfx)test-freopen
|
||||
|
||||
@@ -87,6 +87,11 @@ _IO_check_libio (void)
|
||||
stdout->_vtable_offset = stderr->_vtable_offset =
|
||||
((int) sizeof (struct _IO_FILE)
|
||||
- (int) sizeof (struct _IO_FILE_complete));
|
||||
|
||||
if (_IO_stdin_.vtable != &_IO_old_file_jumps
|
||||
|| _IO_stdout_.vtable != &_IO_old_file_jumps
|
||||
|| _IO_stderr_.vtable != &_IO_old_file_jumps)
|
||||
IO_set_accept_foreign_vtables (&_IO_vtable_check);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Test that _IO_wfile_sync does not crash (bug 20568).
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
TEST_VERIFY_EXIT (setlocale (LC_ALL, "de_DE.UTF-8") != NULL);
|
||||
/* Fill the stdio buffer and advance the read pointer. */
|
||||
TEST_VERIFY_EXIT (fgetwc (stdin) != WEOF);
|
||||
/* This calls _IO_wfile_sync, it should not crash. */
|
||||
TEST_VERIFY_EXIT (setvbuf (stdin, NULL, _IONBF, 0) == 0);
|
||||
/* Verify that the external file offset has been synchronized. */
|
||||
TEST_COMPARE (xlseek (0, 0, SEEK_CUR), 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1 @@
|
||||
This is a test of _IO_wfile_sync.
|
||||
@@ -70,3 +70,19 @@ _IO_vtable_check (void)
|
||||
|
||||
__libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
|
||||
}
|
||||
|
||||
/* Some variants of libstdc++ interpose _IO_2_1_stdin_ etc. and
|
||||
install their own vtables directly, without calling _IO_init or
|
||||
other functions. Detect this by looking at the vtables values
|
||||
during startup, and disable vtable validation in this case. */
|
||||
#ifdef SHARED
|
||||
__attribute__ ((constructor))
|
||||
static void
|
||||
check_stdfiles_vtables (void)
|
||||
{
|
||||
if (_IO_2_1_stdin_.vtable != &_IO_file_jumps
|
||||
|| _IO_2_1_stdout_.vtable != &_IO_file_jumps
|
||||
|| _IO_2_1_stderr_.vtable != &_IO_file_jumps)
|
||||
IO_set_accept_foreign_vtables (&_IO_vtable_check);
|
||||
}
|
||||
#endif
|
||||
|
||||
+3
-2
@@ -526,11 +526,12 @@ _IO_wfile_sync (_IO_FILE *fp)
|
||||
generate the wide characters up to the current reading
|
||||
position. */
|
||||
int nread;
|
||||
|
||||
size_t wnread = (fp->_wide_data->_IO_read_ptr
|
||||
- fp->_wide_data->_IO_read_base);
|
||||
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
|
||||
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
|
||||
fp->_IO_read_base,
|
||||
fp->_IO_read_end, delta);
|
||||
fp->_IO_read_end, wnread);
|
||||
fp->_IO_read_ptr = fp->_IO_read_base + nread;
|
||||
delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
2017-06-11 Santhosh Thottingal <santhosh.thottingal@gmail.com>
|
||||
|
||||
[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 <joseph@codesourcery.com>
|
||||
|
||||
* All files with FSF copyright notices: Update copyright dates
|
||||
|
||||
@@ -1042,9 +1042,9 @@ collating-element <ml-bh> from "<U0D2D><U0D4D>"
|
||||
collating-element <ml-m> from "<U0D2E><U0D4D>"
|
||||
collating-element <ml-y> from "<U0D2F><U0D4D>"
|
||||
collating-element <ml-v> from "<U0D35><U0D4D>"
|
||||
collating-element <ml-s> from "<U0D38><U0D4D>"
|
||||
collating-element <ml-ss> from "<U0D36><U0D4D>"
|
||||
collating-element <ml-sh> from "<U0D37><U0D4D>"
|
||||
collating-element <ml-s> from "<U0D38><U0D4D>"
|
||||
collating-element <ml-h> from "<U0D39><U0D4D>"
|
||||
collating-element <ml-zh> from "<U0D34><U0D4D>"
|
||||
collating-element <ml-rr> from "<U0D31><U0D4D>"
|
||||
@@ -1103,8 +1103,8 @@ collating-symbol <ml-rra>
|
||||
collating-symbol <ml-la>
|
||||
collating-symbol <ml-lla>
|
||||
collating-symbol <ml-va>
|
||||
collating-symbol <ml-sha>
|
||||
collating-symbol <ml-ssa>
|
||||
collating-symbol <ml-sha>
|
||||
collating-symbol <ml-sa>
|
||||
collating-symbol <ml-ha>
|
||||
collating-symbol <ml-avagrah>
|
||||
@@ -1126,6 +1126,12 @@ collating-symbol <mlvs-o>
|
||||
collating-symbol <mlvs-au>
|
||||
collating-symbol <ml-visarga>
|
||||
collating-symbol <ml-virama>
|
||||
collating-symbol <ml-atomic-chillu-k>
|
||||
collating-symbol <ml-atomic-chillu-n>
|
||||
collating-symbol <ml-atomic-chillu-nn>
|
||||
collating-symbol <ml-atomic-chillu-l>
|
||||
collating-symbol <ml-atomic-chillu-ll>
|
||||
collating-symbol <ml-atomic-chillu-r>
|
||||
#
|
||||
# <BENGALI>
|
||||
#
|
||||
@@ -4552,6 +4558,12 @@ collating-symbol <TIB-subA>
|
||||
<mlvs-o>
|
||||
<mlvs-au>
|
||||
<ml-visarga>
|
||||
<ml-atomic-chillu-k>
|
||||
<ml-atomic-chillu-n>
|
||||
<ml-atomic-chillu-nn>
|
||||
<ml-atomic-chillu-l>
|
||||
<ml-atomic-chillu-ll>
|
||||
<ml-atomic-chillu-r>
|
||||
#
|
||||
# <BENGALI>
|
||||
#
|
||||
@@ -7252,6 +7264,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
|
||||
<U0D13> <mlvw-o>;<BAS>;<MIN>;IGNORE
|
||||
<U0D14> <mlvw-au>;<BAS>;<MIN>;IGNORE
|
||||
<ml-chillu-k> "<ml-ka><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D7F> "<ml-ka><ml-virama>";<ml-atomic-chillu-k>;<MIN>;IGNORE
|
||||
<U0D15> "<ml-ka><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-kh> "<ml-kha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D16> "<ml-kha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
@@ -7280,6 +7293,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
|
||||
<ml-dh> "<ml-dha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D22> "<ml-dha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-chillu-nn> "<ml-nna><ml-virama>";<BAS>;<MIN>;IGNORE # ണ് = ണ + ് + zwj
|
||||
<U0D7A> "<ml-nna><ml-virama>";<ml-atomic-chillu-nn>;<MIN>;IGNORE
|
||||
<U0D23> "<ml-nna><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ണ = ണ + ് + അ
|
||||
<ml-th> "<ml-tha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D24> "<ml-tha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
@@ -7290,6 +7304,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
|
||||
<ml-ddh> "<ml-ddha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D27> "<ml-ddha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-chillu-n> "<ml-na><ml-virama>";<BAS>;<MIN>;IGNORE # ന്= ന + ് + zwj
|
||||
<U0D7B> "<ml-na><ml-virama>";<ml-atomic-chillu-n>;<MIN>;IGNORE
|
||||
<U0D28> "<ml-na><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE #ന = ന + ് + അ
|
||||
<ml-p> "<ml-pa><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D2A> "<ml-pa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
@@ -7305,20 +7320,23 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
|
||||
<ml-y> "<ml-ya><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D2F> "<ml-ya><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-chillu-r> "<ml-ra><ml-virama>";<BAS>;<MIN>;IGNORE # ര = ര + ് + zwj
|
||||
<U0D7C> "<ml-ra><ml-virama>";<ml-atomic-chillu-r>;<MIN>;IGNORE
|
||||
<U0D30> "<ml-ra><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ര = ര + ് + അ
|
||||
<ml-chillu-l> <ml-la>;<BAS>;<MIN>;IGNORE # ല് = ല + ് + zwj
|
||||
<U0D7D> "<ml-la><ml-virama>";<ml-atomic-chillu-l>;<MIN>;IGNORE
|
||||
<U0D32> "<ml-la><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ല = ല + ് + അ
|
||||
<ml-v> "<ml-va><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D35> "<ml-va><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-ss> "<ml-ssa><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D37> "<ml-ssa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<U0D36> "<ml-ssa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-sh> "<ml-sha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D36> "<ml-sha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<U0D37> "<ml-sha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-s> "<ml-sa><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D38> "<ml-sa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-h> "<ml-ha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D39> "<ml-ha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
<ml-chillu-ll> "<ml-lla><ml-virama>";<BAS>;<MIN>;IGNORE # ള് = ള + ് + zwj
|
||||
<U0D7E> "<ml-lla><ml-virama>";<ml-atomic-chillu-ll>;<MIN>;IGNORE
|
||||
<U0D33> "<ml-lla><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ള = ള + ് + അ
|
||||
<ml-zh> "<ml-zha><ml-virama>";<BAS>;<MIN>;IGNORE
|
||||
<U0D34> "<ml-zha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
|
||||
|
||||
+29
-2
@@ -33,6 +33,11 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
|
||||
tst-mallocfork2 \
|
||||
tst-interpose-nothread \
|
||||
tst-interpose-thread \
|
||||
tst-dynarray \
|
||||
tst-dynarray-fail \
|
||||
tst-dynarray-at-fail \
|
||||
tst-alloc_buffer \
|
||||
tst-malloc-too-large \
|
||||
|
||||
tests-static := \
|
||||
tst-interpose-static-nothread \
|
||||
@@ -45,11 +50,21 @@ tests-static += tst-malloc-usable-static-tunables
|
||||
endif
|
||||
|
||||
tests += $(tests-static)
|
||||
test-srcs = tst-mtrace
|
||||
test-srcs = tst-mtrace tst-dynarray tst-dynarray-fail
|
||||
|
||||
routines = malloc morecore mcheck mtrace obstack \
|
||||
scratch_buffer_grow scratch_buffer_grow_preserve \
|
||||
scratch_buffer_set_array_size
|
||||
scratch_buffer_set_array_size \
|
||||
dynarray_at_failure \
|
||||
dynarray_emplace_enlarge \
|
||||
dynarray_finalize \
|
||||
dynarray_resize \
|
||||
dynarray_resize_clear \
|
||||
alloc_buffer_alloc_array \
|
||||
alloc_buffer_allocate \
|
||||
alloc_buffer_copy_bytes \
|
||||
alloc_buffer_copy_string \
|
||||
alloc_buffer_create_failure \
|
||||
|
||||
install-lib := libmcheck.a
|
||||
non-lib.a := libmcheck.a
|
||||
@@ -135,6 +150,8 @@ ifeq ($(run-built-tests),yes)
|
||||
ifeq (yes,$(build-shared))
|
||||
ifneq ($(PERL),no)
|
||||
tests-special += $(objpfx)tst-mtrace.out
|
||||
tests-special += $(objpfx)tst-dynarray-mem.out
|
||||
tests-special += $(objpfx)tst-dynarray-fail-mem.out
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -206,3 +223,13 @@ $(objpfx)tst-interpose-thread: \
|
||||
$(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o
|
||||
$(objpfx)tst-interpose-static-thread: \
|
||||
$(objpfx)tst-interpose-aux-thread.o $(static-thread-library)
|
||||
|
||||
tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace
|
||||
$(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace
|
||||
$(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
@@ -72,5 +72,19 @@ libc {
|
||||
__libc_scratch_buffer_grow;
|
||||
__libc_scratch_buffer_grow_preserve;
|
||||
__libc_scratch_buffer_set_array_size;
|
||||
|
||||
# dynarray support
|
||||
__libc_dynarray_at_failure;
|
||||
__libc_dynarray_emplace_enlarge;
|
||||
__libc_dynarray_finalize;
|
||||
__libc_dynarray_resize;
|
||||
__libc_dynarray_resize_clear;
|
||||
|
||||
# struct alloc_buffer support
|
||||
__libc_alloc_buffer_alloc_array;
|
||||
__libc_alloc_buffer_allocate;
|
||||
__libc_alloc_buffer_copy_bytes;
|
||||
__libc_alloc_buffer_copy_string;
|
||||
__libc_alloc_buffer_create_failure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Array allocation from a fixed-size buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
#include <malloc-internal.h>
|
||||
#include <libc-pointer-arith.h>
|
||||
|
||||
void *
|
||||
__libc_alloc_buffer_alloc_array (struct alloc_buffer *buf, size_t element_size,
|
||||
size_t align, size_t count)
|
||||
{
|
||||
size_t current = buf->__alloc_buffer_current;
|
||||
/* The caller asserts that align is a power of two. */
|
||||
size_t aligned = ALIGN_UP (current, align);
|
||||
size_t size;
|
||||
bool overflow = check_mul_overflow_size_t (element_size, count, &size);
|
||||
size_t new_current = aligned + size;
|
||||
if (!overflow /* Multiplication did not overflow. */
|
||||
&& aligned >= current /* No overflow in align step. */
|
||||
&& new_current >= size /* No overflow in size computation. */
|
||||
&& new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
|
||||
{
|
||||
buf->__alloc_buffer_current = new_current;
|
||||
return (void *) aligned;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_buffer_mark_failed (buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
libc_hidden_def (__libc_alloc_buffer_alloc_array)
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Allocate a fixed-size allocation buffer using malloc.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct alloc_buffer
|
||||
__libc_alloc_buffer_allocate (size_t size, void **pptr)
|
||||
{
|
||||
*pptr = malloc (size);
|
||||
if (*pptr == NULL)
|
||||
return (struct alloc_buffer)
|
||||
{
|
||||
.__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
|
||||
.__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
|
||||
};
|
||||
else
|
||||
return alloc_buffer_create (*pptr, size);
|
||||
}
|
||||
libc_hidden_def (__libc_alloc_buffer_allocate)
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copy an array of bytes into the buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* This function works on a copy of the buffer object, so that it can
|
||||
remain non-addressable in the caller. */
|
||||
struct alloc_buffer
|
||||
__libc_alloc_buffer_copy_bytes (struct alloc_buffer buf,
|
||||
const void *src, size_t len)
|
||||
{
|
||||
void *ptr = alloc_buffer_alloc_bytes (&buf, len);
|
||||
if (ptr != NULL)
|
||||
memcpy (ptr, src, len);
|
||||
return buf;
|
||||
}
|
||||
libc_hidden_def (__libc_alloc_buffer_copy_bytes)
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Copy a string into the allocation buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* This function works on a copy of the buffer object, so that it can
|
||||
remain non-addressable in the caller. */
|
||||
struct alloc_buffer
|
||||
__libc_alloc_buffer_copy_string (struct alloc_buffer buf, const char *src)
|
||||
{
|
||||
return __libc_alloc_buffer_copy_bytes (buf, src, strlen (src) + 1);
|
||||
}
|
||||
libc_hidden_def (__libc_alloc_buffer_copy_string)
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Terminate the process as the result of an invalid allocation buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
__libc_alloc_buffer_create_failure (void *start, size_t size)
|
||||
{
|
||||
char buf[200];
|
||||
__snprintf (buf, sizeof (buf), "Fatal glibc error: "
|
||||
"invalid allocation buffer of size %zu\n",
|
||||
size);
|
||||
__libc_fatal (buf);
|
||||
}
|
||||
libc_hidden_def (__libc_alloc_buffer_create_failure)
|
||||
+4
-4
@@ -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); \
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,521 @@
|
||||
/* Type-safe arrays which grow dynamically.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Pre-processor macros which act as parameters:
|
||||
|
||||
DYNARRAY_STRUCT
|
||||
The struct tag of dynamic array to be defined.
|
||||
DYNARRAY_ELEMENT
|
||||
The type name of the element type. Elements are copied
|
||||
as if by memcpy, and can change address as the dynamic
|
||||
array grows.
|
||||
DYNARRAY_PREFIX
|
||||
The prefix of the functions which are defined.
|
||||
|
||||
The following parameters are optional:
|
||||
|
||||
DYNARRAY_ELEMENT_FREE
|
||||
DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
|
||||
contents of elements. E is of type DYNARRAY_ELEMENT *.
|
||||
DYNARRAY_ELEMENT_INIT
|
||||
DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
|
||||
element. E is of type DYNARRAY_ELEMENT *.
|
||||
If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
|
||||
defined, new elements are automatically zero-initialized.
|
||||
Otherwise, new elements have undefined contents.
|
||||
DYNARRAY_INITIAL_SIZE
|
||||
The size of the statically allocated array (default:
|
||||
at least 2, more elements if they fit into 128 bytes).
|
||||
Must be a preprocessor constant. If DYNARRAY_INITIAL_SIZE is 0,
|
||||
there is no statically allocated array at, and all non-empty
|
||||
arrays are heap-allocated.
|
||||
DYNARRAY_FINAL_TYPE
|
||||
The name of the type which holds the final array. If not
|
||||
defined, is PREFIX##finalize not provided. DYNARRAY_FINAL_TYPE
|
||||
must be a struct type, with members of type DYNARRAY_ELEMENT and
|
||||
size_t at the start (in this order).
|
||||
|
||||
These macros are undefined after this header file has been
|
||||
included.
|
||||
|
||||
The following types are provided (their members are private to the
|
||||
dynarray implementation):
|
||||
|
||||
struct DYNARRAY_STRUCT
|
||||
|
||||
The following functions are provided:
|
||||
|
||||
void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *);
|
||||
void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *);
|
||||
bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *);
|
||||
void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *);
|
||||
size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *);
|
||||
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *);
|
||||
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *);
|
||||
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t);
|
||||
void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT);
|
||||
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *);
|
||||
bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t);
|
||||
void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *);
|
||||
void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *);
|
||||
|
||||
The following functions are provided are provided if the
|
||||
prerequisites are met:
|
||||
|
||||
bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
|
||||
DYNARRAY_FINAL_TYPE *);
|
||||
(if DYNARRAY_FINAL_TYPE is defined)
|
||||
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
|
||||
size_t *);
|
||||
(if DYNARRAY_FINAL_TYPE is not defined)
|
||||
*/
|
||||
|
||||
#include <malloc/dynarray.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef DYNARRAY_STRUCT
|
||||
# error "DYNARRAY_STRUCT must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef DYNARRAY_ELEMENT
|
||||
# error "DYNARRAY_ELEMENT must be defined"
|
||||
#endif
|
||||
|
||||
#ifndef DYNARRAY_PREFIX
|
||||
# error "DYNARRAY_PREFIX must be defined"
|
||||
#endif
|
||||
|
||||
#ifdef DYNARRAY_INITIAL_SIZE
|
||||
# if DYNARRAY_INITIAL_SIZE < 0
|
||||
# error "DYNARRAY_INITIAL_SIZE must be non-negative"
|
||||
# endif
|
||||
# if DYNARRAY_INITIAL_SIZE > 0
|
||||
# define DYNARRAY_HAVE_SCRATCH 1
|
||||
# else
|
||||
# define DYNARRAY_HAVE_SCRATCH 0
|
||||
# endif
|
||||
#else
|
||||
/* Provide a reasonable default which limits the size of
|
||||
DYNARRAY_STRUCT. */
|
||||
# define DYNARRAY_INITIAL_SIZE \
|
||||
(sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT))
|
||||
# define DYNARRAY_HAVE_SCRATCH 1
|
||||
#endif
|
||||
|
||||
/* Public type definitions. */
|
||||
|
||||
/* All fields of this struct are private to the implementation. */
|
||||
struct DYNARRAY_STRUCT
|
||||
{
|
||||
union
|
||||
{
|
||||
struct dynarray_header dynarray_abstract;
|
||||
struct
|
||||
{
|
||||
/* These fields must match struct dynarray_header. */
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
DYNARRAY_ELEMENT *array;
|
||||
} dynarray_header;
|
||||
};
|
||||
|
||||
#if DYNARRAY_HAVE_SCRATCH
|
||||
/* Initial inline allocation. */
|
||||
DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE];
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Internal use only: Helper macros. */
|
||||
|
||||
/* Ensure macro-expansion of DYNARRAY_PREFIX. */
|
||||
#define DYNARRAY_CONCAT0(prefix, name) prefix##name
|
||||
#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name)
|
||||
#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name)
|
||||
|
||||
/* Address of the scratch buffer if any. */
|
||||
#if DYNARRAY_HAVE_SCRATCH
|
||||
# define DYNARRAY_SCRATCH(list) (list)->scratch
|
||||
#else
|
||||
# define DYNARRAY_SCRATCH(list) NULL
|
||||
#endif
|
||||
|
||||
/* Internal use only: Helper functions. */
|
||||
|
||||
/* Internal function. Call DYNARRAY_ELEMENT_FREE with the array
|
||||
elements. Name mangling needed due to the DYNARRAY_ELEMENT_FREE
|
||||
macro expansion. */
|
||||
static inline void
|
||||
DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array,
|
||||
size_t __dynarray_used)
|
||||
{
|
||||
#ifdef DYNARRAY_ELEMENT_FREE
|
||||
for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i)
|
||||
DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]);
|
||||
#endif /* DYNARRAY_ELEMENT_FREE */
|
||||
}
|
||||
|
||||
/* Internal function. Free the non-scratch array allocation. */
|
||||
static inline void
|
||||
DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
#if DYNARRAY_HAVE_SCRATCH
|
||||
if (list->dynarray_header.array != list->scratch)
|
||||
free (list->dynarray_header.array);
|
||||
#else
|
||||
free (list->dynarray_header.array);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Public functions. */
|
||||
|
||||
/* Initialize a dynamic array object. This must be called before any
|
||||
use of the object. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static void
|
||||
DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
list->dynarray_header.used = 0;
|
||||
list->dynarray_header.allocated = DYNARRAY_INITIAL_SIZE;
|
||||
list->dynarray_header.array = DYNARRAY_SCRATCH (list);
|
||||
}
|
||||
|
||||
/* Deallocate the dynamic array and its elements. */
|
||||
__attribute__ ((unused, nonnull (1)))
|
||||
static void
|
||||
DYNARRAY_NAME (free) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
DYNARRAY_NAME (free__elements__)
|
||||
(list->dynarray_header.array, list->dynarray_header.used);
|
||||
DYNARRAY_NAME (free__array__) (list);
|
||||
DYNARRAY_NAME (init) (list);
|
||||
}
|
||||
|
||||
/* Return true if the dynamic array is in an error state. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static inline bool
|
||||
DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
return list->dynarray_header.allocated == __dynarray_error_marker ();
|
||||
}
|
||||
|
||||
/* Mark the dynamic array as failed. All elements are deallocated as
|
||||
a side effect. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static void
|
||||
DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
DYNARRAY_NAME (free__elements__)
|
||||
(list->dynarray_header.array, list->dynarray_header.used);
|
||||
DYNARRAY_NAME (free__array__) (list);
|
||||
list->dynarray_header.array = DYNARRAY_SCRATCH (list);
|
||||
list->dynarray_header.used = 0;
|
||||
list->dynarray_header.allocated = __dynarray_error_marker ();
|
||||
}
|
||||
|
||||
/* Return the number of elements which have been added to the dynamic
|
||||
array. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static inline size_t
|
||||
DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
return list->dynarray_header.used;
|
||||
}
|
||||
|
||||
/* Return a pointer to the array element at INDEX. Terminate the
|
||||
process if INDEX is out of bounds. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static inline DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index)
|
||||
{
|
||||
if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list)))
|
||||
__libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index);
|
||||
return list->dynarray_header.array + index;
|
||||
}
|
||||
|
||||
/* Return a pointer to the first array element, if any. For a
|
||||
zero-length array, the pointer can be NULL even though the dynamic
|
||||
array has not entered the failure state. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static inline DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (begin) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
return list->dynarray_header.array;
|
||||
}
|
||||
|
||||
/* Return a pointer one element past the last array element. For a
|
||||
zero-length array, the pointer can be NULL even though the dynamic
|
||||
array has not entered the failure state. */
|
||||
__attribute__ ((nonnull (1)))
|
||||
static inline DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (end) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
return list->dynarray_header.array + list->dynarray_header.used;
|
||||
}
|
||||
|
||||
/* Internal function. Slow path for the add function below. */
|
||||
static void
|
||||
DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
|
||||
{
|
||||
if (__glibc_unlikely
|
||||
(!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
|
||||
DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT))))
|
||||
{
|
||||
DYNARRAY_NAME (mark_failed) (list);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy the new element and increase the array length. */
|
||||
list->dynarray_header.array[list->dynarray_header.used++] = item;
|
||||
}
|
||||
|
||||
/* Add ITEM at the end of the array, enlarging it by one element.
|
||||
Mark *LIST as failed if the dynamic array allocation size cannot be
|
||||
increased. */
|
||||
__attribute__ ((unused, nonnull (1)))
|
||||
static inline void
|
||||
DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
|
||||
{
|
||||
/* Do nothing in case of previous error. */
|
||||
if (DYNARRAY_NAME (has_failed) (list))
|
||||
return;
|
||||
|
||||
/* Enlarge the array if necessary. */
|
||||
if (__glibc_unlikely (list->dynarray_header.used
|
||||
== list->dynarray_header.allocated))
|
||||
{
|
||||
DYNARRAY_NAME (add__) (list, item);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy the new element and increase the array length. */
|
||||
list->dynarray_header.array[list->dynarray_header.used++] = item;
|
||||
}
|
||||
|
||||
/* Internal function. Building block for the emplace functions below.
|
||||
Assumes space for one more element in *LIST. */
|
||||
static inline DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
DYNARRAY_ELEMENT *result
|
||||
= &list->dynarray_header.array[list->dynarray_header.used];
|
||||
++list->dynarray_header.used;
|
||||
#if defined (DYNARRAY_ELEMENT_INIT)
|
||||
DYNARRAY_ELEMENT_INIT (result);
|
||||
#elif defined (DYNARRAY_ELEMENT_FREE)
|
||||
memset (result, 0, sizeof (*result));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Internal function. Slow path for the emplace function below. */
|
||||
static DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
if (__glibc_unlikely
|
||||
(!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
|
||||
DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT))))
|
||||
{
|
||||
DYNARRAY_NAME (mark_failed) (list);
|
||||
return NULL;
|
||||
}
|
||||
return DYNARRAY_NAME (emplace__tail__) (list);
|
||||
}
|
||||
|
||||
/* Allocate a place for a new element in *LIST and return a pointer to
|
||||
it. The pointer can be NULL if the dynamic array cannot be
|
||||
enlarged due to a memory allocation failure. */
|
||||
__attribute__ ((unused, warn_unused_result, nonnull (1)))
|
||||
static
|
||||
/* Avoid inlining with the larger initialization code. */
|
||||
#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE))
|
||||
inline
|
||||
#endif
|
||||
DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
/* Do nothing in case of previous error. */
|
||||
if (DYNARRAY_NAME (has_failed) (list))
|
||||
return NULL;
|
||||
|
||||
/* Enlarge the array if necessary. */
|
||||
if (__glibc_unlikely (list->dynarray_header.used
|
||||
== list->dynarray_header.allocated))
|
||||
return (DYNARRAY_NAME (emplace__) (list));
|
||||
return DYNARRAY_NAME (emplace__tail__) (list);
|
||||
}
|
||||
|
||||
/* Change the size of *LIST to SIZE. If SIZE is larger than the
|
||||
existing size, new elements are added (which can be initialized).
|
||||
Otherwise, the list is truncated, and elements are freed. Return
|
||||
false on memory allocation failure (and mark *LIST as failed). */
|
||||
__attribute__ ((unused, nonnull (1)))
|
||||
static bool
|
||||
DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size)
|
||||
{
|
||||
if (size > list->dynarray_header.used)
|
||||
{
|
||||
bool ok;
|
||||
#if defined (DYNARRAY_ELEMENT_INIT)
|
||||
/* The new elements have to be initialized. */
|
||||
size_t old_size = list->dynarray_header.used;
|
||||
ok = __libc_dynarray_resize (&list->dynarray_abstract,
|
||||
size, DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT));
|
||||
if (ok)
|
||||
for (size_t i = old_size; i < size; ++i)
|
||||
{
|
||||
DYNARRAY_ELEMENT_INIT (&list->dynarray_header.array[i]);
|
||||
}
|
||||
#elif defined (DYNARRAY_ELEMENT_FREE)
|
||||
/* Zero initialization is needed so that the elements can be
|
||||
safely freed. */
|
||||
ok = __libc_dynarray_resize_clear
|
||||
(&list->dynarray_abstract, size,
|
||||
DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT));
|
||||
#else
|
||||
ok = __libc_dynarray_resize (&list->dynarray_abstract,
|
||||
size, DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT));
|
||||
#endif
|
||||
if (__glibc_unlikely (!ok))
|
||||
DYNARRAY_NAME (mark_failed) (list);
|
||||
return ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The list has shrunk in size. Free the removed elements. */
|
||||
DYNARRAY_NAME (free__elements__)
|
||||
(list->dynarray_header.array + size,
|
||||
list->dynarray_header.used - size);
|
||||
list->dynarray_header.used = size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the last element of LIST if it is present. */
|
||||
__attribute__ ((unused, nonnull (1)))
|
||||
static void
|
||||
DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
/* used > 0 implies that the array is the non-failed state. */
|
||||
if (list->dynarray_header.used > 0)
|
||||
{
|
||||
size_t new_length = list->dynarray_header.used - 1;
|
||||
#ifdef DYNARRAY_ELEMENT_FREE
|
||||
DYNARRAY_ELEMENT_FREE (&list->dynarray_header.array[new_length]);
|
||||
#endif
|
||||
list->dynarray_header.used = new_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove all elements from the list. The elements are freed, but the
|
||||
list itself is not. */
|
||||
__attribute__ ((unused, nonnull (1)))
|
||||
static void
|
||||
DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list)
|
||||
{
|
||||
/* free__elements__ does nothing if the list is in the failed
|
||||
state. */
|
||||
DYNARRAY_NAME (free__elements__)
|
||||
(list->dynarray_header.array, list->dynarray_header.used);
|
||||
list->dynarray_header.used = 0;
|
||||
}
|
||||
|
||||
#ifdef DYNARRAY_FINAL_TYPE
|
||||
/* Transfer the dynamic array to a permanent location at *RESULT.
|
||||
Returns true on success on false on allocation failure. In either
|
||||
case, *LIST is re-initialized and can be reused. A NULL pointer is
|
||||
stored in *RESULT if LIST refers to an empty list. On success, the
|
||||
pointer in *RESULT is heap-allocated and must be deallocated using
|
||||
free. */
|
||||
__attribute__ ((unused, warn_unused_result, nonnull (1, 2)))
|
||||
static bool
|
||||
DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list,
|
||||
DYNARRAY_FINAL_TYPE *result)
|
||||
{
|
||||
struct dynarray_finalize_result res;
|
||||
if (__libc_dynarray_finalize (&list->dynarray_abstract,
|
||||
DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT), &res))
|
||||
{
|
||||
/* On success, the result owns all the data. */
|
||||
DYNARRAY_NAME (init) (list);
|
||||
*result = (DYNARRAY_FINAL_TYPE) { res.array, res.length };
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* On error, we need to free all data. */
|
||||
DYNARRAY_NAME (free) (list);
|
||||
errno = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else /* !DYNARRAY_FINAL_TYPE */
|
||||
/* Transfer the dynamic array to a heap-allocated array and return a
|
||||
pointer to it. The pointer is NULL if memory allocation fails, or
|
||||
if the array is empty, so this function should be used only for
|
||||
arrays which are known not be empty (usually because they always
|
||||
have a sentinel at the end). If LENGTHP is not NULL, the array
|
||||
length is written to *LENGTHP. *LIST is re-initialized and can be
|
||||
reused. */
|
||||
__attribute__ ((unused, warn_unused_result, nonnull (1)))
|
||||
static DYNARRAY_ELEMENT *
|
||||
DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp)
|
||||
{
|
||||
struct dynarray_finalize_result res;
|
||||
if (__libc_dynarray_finalize (&list->dynarray_abstract,
|
||||
DYNARRAY_SCRATCH (list),
|
||||
sizeof (DYNARRAY_ELEMENT), &res))
|
||||
{
|
||||
/* On success, the result owns all the data. */
|
||||
DYNARRAY_NAME (init) (list);
|
||||
if (lengthp != NULL)
|
||||
*lengthp = res.length;
|
||||
return res.array;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* On error, we need to free all data. */
|
||||
DYNARRAY_NAME (free) (list);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif /* !DYNARRAY_FINAL_TYPE */
|
||||
|
||||
/* Undo macro definitions. */
|
||||
|
||||
#undef DYNARRAY_CONCAT0
|
||||
#undef DYNARRAY_CONCAT1
|
||||
#undef DYNARRAY_NAME
|
||||
#undef DYNARRAY_SCRATCH
|
||||
#undef DYNARRAY_HAVE_SCRATCH
|
||||
|
||||
#undef DYNARRAY_STRUCT
|
||||
#undef DYNARRAY_ELEMENT
|
||||
#undef DYNARRAY_PREFIX
|
||||
#undef DYNARRAY_ELEMENT_FREE
|
||||
#undef DYNARRAY_ELEMENT_INIT
|
||||
#undef DYNARRAY_INITIAL_SIZE
|
||||
#undef DYNARRAY_FINAL_TYPE
|
||||
@@ -0,0 +1,176 @@
|
||||
/* Type-safe arrays which grow dynamically. Shared definitions.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* To use the dynarray facility, you need to include
|
||||
<malloc/dynarray-skeleton.c> and define the parameter macros
|
||||
documented in that file.
|
||||
|
||||
A minimal example which provides a growing list of integers can be
|
||||
defined like this:
|
||||
|
||||
struct int_array
|
||||
{
|
||||
// Pointer to result array followed by its length,
|
||||
// as required by DYNARRAY_FINAL_TYPE.
|
||||
int *array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_int
|
||||
#define DYNARRAY_ELEMENT int
|
||||
#define DYNARRAY_PREFIX dynarray_int_
|
||||
#define DYNARRAY_FINAL_TYPE struct int_array
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
To create a three-element array with elements 1, 2, 3, use this
|
||||
code:
|
||||
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
for (int i = 1; i <= 3; ++i)
|
||||
{
|
||||
int *place = dynarray_int_emplace (&dyn);
|
||||
assert (place != NULL);
|
||||
*place = i;
|
||||
}
|
||||
struct int_array result;
|
||||
bool ok = dynarray_int_finalize (&dyn, &result);
|
||||
assert (ok);
|
||||
assert (result.length == 3);
|
||||
assert (result.array[0] == 1);
|
||||
assert (result.array[1] == 2);
|
||||
assert (result.array[2] == 3);
|
||||
free (result.array);
|
||||
|
||||
If the elements contain resources which must be freed, define
|
||||
DYNARRAY_ELEMENT_FREE appropriately, like this:
|
||||
|
||||
struct str_array
|
||||
{
|
||||
char **array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_str
|
||||
#define DYNARRAY_ELEMENT char *
|
||||
#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
|
||||
#define DYNARRAY_PREFIX dynarray_str_
|
||||
#define DYNARRAY_FINAL_TYPE struct str_array
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
Compared to scratch buffers, dynamic arrays have the following
|
||||
features:
|
||||
|
||||
- They have an element type, and are not just an untyped buffer of
|
||||
bytes.
|
||||
|
||||
- When growing, previously stored elements are preserved. (It is
|
||||
expected that scratch_buffer_grow_preserve and
|
||||
scratch_buffer_set_array_size eventually go away because all
|
||||
current users are moved to dynamic arrays.)
|
||||
|
||||
- Scratch buffers have a more aggressive growth policy because
|
||||
growing them typically means a retry of an operation (across an
|
||||
NSS service module boundary), which is expensive.
|
||||
|
||||
- For the same reason, scratch buffers have a much larger initial
|
||||
stack allocation. */
|
||||
|
||||
#ifndef _DYNARRAY_H
|
||||
#define _DYNARRAY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
struct dynarray_header
|
||||
{
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
void *array;
|
||||
};
|
||||
|
||||
/* Marker used in the allocated member to indicate that an error was
|
||||
encountered. */
|
||||
static inline size_t
|
||||
__dynarray_error_marker (void)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Internal function. See the has_failed function in
|
||||
dynarray-skeleton.c. */
|
||||
static inline bool
|
||||
__dynarray_error (struct dynarray_header *list)
|
||||
{
|
||||
return list->allocated == __dynarray_error_marker ();
|
||||
}
|
||||
|
||||
/* Internal function. Enlarge the dynamically allocated area of the
|
||||
array to make room for one more element. SCRATCH is a pointer to
|
||||
the scratch area (which is not heap-allocated and must not be
|
||||
freed). ELEMENT_SIZE is the size, in bytes, of one element.
|
||||
Return false on failure, true on success. */
|
||||
bool __libc_dynarray_emplace_enlarge (struct dynarray_header *,
|
||||
void *scratch, size_t element_size);
|
||||
libc_hidden_proto (__libc_dynarray_emplace_enlarge)
|
||||
|
||||
/* Internal function. Enlarge the dynamically allocated area of the
|
||||
array to make room for at least SIZE elements (which must be larger
|
||||
than the existing used part of the dynamic array). SCRATCH is a
|
||||
pointer to the scratch area (which is not heap-allocated and must
|
||||
not be freed). ELEMENT_SIZE is the size, in bytes, of one element.
|
||||
Return false on failure, true on success. */
|
||||
bool __libc_dynarray_resize (struct dynarray_header *, size_t size,
|
||||
void *scratch, size_t element_size);
|
||||
libc_hidden_proto (__libc_dynarray_resize)
|
||||
|
||||
/* Internal function. Like __libc_dynarray_resize, but clear the new
|
||||
part of the dynamic array. */
|
||||
bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size,
|
||||
void *scratch, size_t element_size);
|
||||
libc_hidden_proto (__libc_dynarray_resize_clear)
|
||||
|
||||
/* Internal type. */
|
||||
struct dynarray_finalize_result
|
||||
{
|
||||
void *array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
/* Internal function. Copy the dynamically-allocated area to an
|
||||
explicitly-sized heap allocation. SCRATCH is a pointer to the
|
||||
embedded scratch space. ELEMENT_SIZE is the size, in bytes, of the
|
||||
element type. On success, true is returned, and pointer and length
|
||||
are written to *RESULT. On failure, false is returned. The caller
|
||||
has to take care of some of the memory management; this function is
|
||||
expected to be called from dynarray-skeleton.c. */
|
||||
bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
|
||||
size_t element_size,
|
||||
struct dynarray_finalize_result *result);
|
||||
libc_hidden_proto (__libc_dynarray_finalize)
|
||||
|
||||
|
||||
/* Internal function. Terminate the process after an index error.
|
||||
SIZE is the number of elements of the dynamic array. INDEX is the
|
||||
lookup index which triggered the failure. */
|
||||
void __libc_dynarray_at_failure (size_t size, size_t index)
|
||||
__attribute__ ((noreturn));
|
||||
libc_hidden_proto (__libc_dynarray_at_failure)
|
||||
|
||||
#endif /* _DYNARRAY_H */
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Report an dynamic array index out of bounds condition.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dynarray.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
__libc_dynarray_at_failure (size_t size, size_t index)
|
||||
{
|
||||
char buf[200];
|
||||
__snprintf (buf, sizeof (buf), "Fatal glibc error: "
|
||||
"array index %zu not less than array length %zu\n",
|
||||
index, size);
|
||||
__libc_fatal (buf);
|
||||
}
|
||||
libc_hidden_def (__libc_dynarray_at_failure)
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Increase the size of a dynamic array in preparation of an emplace operation.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dynarray.h>
|
||||
#include <errno.h>
|
||||
#include <malloc-internal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool
|
||||
__libc_dynarray_emplace_enlarge (struct dynarray_header *list,
|
||||
void *scratch, size_t element_size)
|
||||
{
|
||||
size_t new_allocated;
|
||||
if (list->allocated == 0)
|
||||
{
|
||||
/* No scratch buffer provided. Choose a reasonable default
|
||||
size. */
|
||||
if (element_size < 4)
|
||||
new_allocated = 16;
|
||||
else if (element_size < 8)
|
||||
new_allocated = 8;
|
||||
else
|
||||
new_allocated = 4;
|
||||
}
|
||||
else
|
||||
/* Increase the allocated size, using an exponential growth
|
||||
policy. */
|
||||
{
|
||||
new_allocated = list->allocated + list->allocated / 2 + 1;
|
||||
if (new_allocated <= list->allocated)
|
||||
{
|
||||
/* Overflow. */
|
||||
__set_errno (ENOMEM);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t new_size;
|
||||
if (check_mul_overflow_size_t (new_allocated, element_size, &new_size))
|
||||
return false;
|
||||
void *new_array;
|
||||
if (list->array == scratch)
|
||||
{
|
||||
/* The previous array was not heap-allocated. */
|
||||
new_array = malloc (new_size);
|
||||
if (new_array != NULL && list->array != NULL)
|
||||
memcpy (new_array, list->array, list->used * element_size);
|
||||
}
|
||||
else
|
||||
new_array = realloc (list->array, new_size);
|
||||
if (new_array == NULL)
|
||||
return false;
|
||||
list->array = new_array;
|
||||
list->allocated = new_allocated;
|
||||
return true;
|
||||
}
|
||||
libc_hidden_def (__libc_dynarray_emplace_enlarge)
|
||||
@@ -0,0 +1,62 @@
|
||||
/* Copy the dynamically-allocated area to an explicitly-sized heap allocation.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dynarray.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool
|
||||
__libc_dynarray_finalize (struct dynarray_header *list,
|
||||
void *scratch, size_t element_size,
|
||||
struct dynarray_finalize_result *result)
|
||||
{
|
||||
if (__dynarray_error (list))
|
||||
/* The caller will reported the deferred error. */
|
||||
return false;
|
||||
|
||||
size_t used = list->used;
|
||||
|
||||
/* Empty list. */
|
||||
if (used == 0)
|
||||
{
|
||||
/* An empty list could still be backed by a heap-allocated
|
||||
array. Free it if necessary. */
|
||||
if (list->array != scratch)
|
||||
free (list->array);
|
||||
*result = (struct dynarray_finalize_result) { NULL, 0 };
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t allocation_size = used * element_size;
|
||||
void *heap_array = malloc (allocation_size);
|
||||
if (heap_array != NULL)
|
||||
{
|
||||
/* The new array takes ownership of the strings. */
|
||||
if (list->array != NULL)
|
||||
memcpy (heap_array, list->array, allocation_size);
|
||||
if (list->array != scratch)
|
||||
free (list->array);
|
||||
*result = (struct dynarray_finalize_result)
|
||||
{ .array = heap_array, .length = used };
|
||||
return true;
|
||||
}
|
||||
else
|
||||
/* The caller will perform the freeing operation. */
|
||||
return false;
|
||||
}
|
||||
libc_hidden_def (__libc_dynarray_finalize)
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Increase the size of a dynamic array.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dynarray.h>
|
||||
#include <errno.h>
|
||||
#include <malloc-internal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool
|
||||
__libc_dynarray_resize (struct dynarray_header *list, size_t size,
|
||||
void *scratch, size_t element_size)
|
||||
{
|
||||
/* The existing allocation provides sufficient room. */
|
||||
if (size <= list->allocated)
|
||||
{
|
||||
list->used = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Otherwise, use size as the new allocation size. The caller is
|
||||
expected to provide the final size of the array, so there is no
|
||||
over-allocation here. */
|
||||
|
||||
size_t new_size_bytes;
|
||||
if (check_mul_overflow_size_t (size, element_size, &new_size_bytes))
|
||||
{
|
||||
/* Overflow. */
|
||||
__set_errno (ENOMEM);
|
||||
return false;
|
||||
}
|
||||
void *new_array;
|
||||
if (list->array == scratch)
|
||||
{
|
||||
/* The previous array was not heap-allocated. */
|
||||
new_array = malloc (new_size_bytes);
|
||||
if (new_array != NULL && list->array != NULL)
|
||||
memcpy (new_array, list->array, list->used * element_size);
|
||||
}
|
||||
else
|
||||
new_array = realloc (list->array, new_size_bytes);
|
||||
if (new_array == NULL)
|
||||
return false;
|
||||
list->array = new_array;
|
||||
list->allocated = size;
|
||||
list->used = size;
|
||||
return true;
|
||||
}
|
||||
libc_hidden_def (__libc_dynarray_resize)
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Increase the size of a dynamic array and clear the new part.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dynarray.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool
|
||||
__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size,
|
||||
void *scratch, size_t element_size)
|
||||
{
|
||||
size_t old_size = list->used;
|
||||
if (!__libc_dynarray_resize (list, size, scratch, element_size))
|
||||
return false;
|
||||
/* __libc_dynarray_resize already checked for overflow. */
|
||||
memset (list->array + (old_size * element_size), 0,
|
||||
(size - old_size) * element_size);
|
||||
return true;
|
||||
}
|
||||
libc_hidden_def (__libc_dynarray_resize_clear)
|
||||
@@ -81,5 +81,24 @@ void __malloc_fork_unlock_parent (void) internal_function attribute_hidden;
|
||||
/* Called in the child process after a fork. */
|
||||
void __malloc_fork_unlock_child (void) internal_function attribute_hidden;
|
||||
|
||||
/* Set *RESULT to LEFT * RIGHT. Return true if the multiplication
|
||||
overflowed. */
|
||||
static inline bool
|
||||
check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
|
||||
{
|
||||
#if __GNUC__ >= 5
|
||||
return __builtin_mul_overflow (left, right, result);
|
||||
#else
|
||||
/* size_t is unsigned so the behavior on overflow is defined. */
|
||||
*result = left * right;
|
||||
size_t half_size_t = ((size_t) 1) << (8 * sizeof (size_t) / 2);
|
||||
if (__glibc_unlikely ((left | right) >= half_size_t))
|
||||
{
|
||||
if (__glibc_unlikely (right != 0 && *result / right != left))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _MALLOC_INTERNAL_H */
|
||||
|
||||
+24
-51
@@ -1202,14 +1202,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
MINSIZE : \
|
||||
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
|
||||
|
||||
/* Same, except also perform argument check */
|
||||
|
||||
#define checked_request2size(req, sz) \
|
||||
if (REQUEST_OUT_OF_RANGE (req)) { \
|
||||
__set_errno (ENOMEM); \
|
||||
return 0; \
|
||||
} \
|
||||
(sz) = request2size (req);
|
||||
/* Same, except also perform an argument and result check. First, we check
|
||||
that the padding done by request2size didn't result in an integer
|
||||
overflow. Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
|
||||
size isn't so large that a later alignment would lead to another integer
|
||||
overflow. */
|
||||
#define checked_request2size(req, sz) \
|
||||
({ \
|
||||
(sz) = request2size (req); \
|
||||
if (((sz) < (req)) \
|
||||
|| REQUEST_OUT_OF_RANGE (sz)) \
|
||||
{ \
|
||||
__set_errno (ENOMEM); \
|
||||
return 0; \
|
||||
} \
|
||||
})
|
||||
|
||||
/*
|
||||
--------------- Physical chunk operations ---------------
|
||||
@@ -4243,11 +4250,6 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
|
||||
mchunkptr bck; /* misc temp for linking */
|
||||
mchunkptr fwd; /* misc temp for linking */
|
||||
|
||||
unsigned long copysize; /* bytes to copy */
|
||||
unsigned int ncopies; /* INTERNAL_SIZE_T words to copy */
|
||||
INTERNAL_SIZE_T* s; /* copy source */
|
||||
INTERNAL_SIZE_T* d; /* copy destination */
|
||||
|
||||
const char *errstr = NULL;
|
||||
|
||||
/* oldmem size */
|
||||
@@ -4325,43 +4327,7 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Unroll copy of <= 36 bytes (72 if 8byte sizes)
|
||||
We know that contents have an odd number of
|
||||
INTERNAL_SIZE_T-sized words; minimally 3.
|
||||
*/
|
||||
|
||||
copysize = oldsize - SIZE_SZ;
|
||||
s = (INTERNAL_SIZE_T *) (chunk2mem (oldp));
|
||||
d = (INTERNAL_SIZE_T *) (newmem);
|
||||
ncopies = copysize / sizeof (INTERNAL_SIZE_T);
|
||||
assert (ncopies >= 3);
|
||||
|
||||
if (ncopies > 9)
|
||||
memcpy (d, s, copysize);
|
||||
|
||||
else
|
||||
{
|
||||
*(d + 0) = *(s + 0);
|
||||
*(d + 1) = *(s + 1);
|
||||
*(d + 2) = *(s + 2);
|
||||
if (ncopies > 4)
|
||||
{
|
||||
*(d + 3) = *(s + 3);
|
||||
*(d + 4) = *(s + 4);
|
||||
if (ncopies > 6)
|
||||
{
|
||||
*(d + 5) = *(s + 5);
|
||||
*(d + 6) = *(s + 6);
|
||||
if (ncopies > 8)
|
||||
{
|
||||
*(d + 7) = *(s + 7);
|
||||
*(d + 8) = *(s + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy (newmem, chunk2mem (oldp), oldsize - SIZE_SZ);
|
||||
_int_free (av, oldp, 1);
|
||||
check_inuse_chunk (av, newp);
|
||||
return chunk2mem (newp);
|
||||
@@ -4423,6 +4389,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
|
||||
*/
|
||||
|
||||
|
||||
/* Check for overflow. */
|
||||
if (nb > SIZE_MAX - alignment - MINSIZE)
|
||||
{
|
||||
__set_errno (ENOMEM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call malloc with worst case padding to hit alignment. */
|
||||
|
||||
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
|
||||
@@ -4902,7 +4875,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);
|
||||
|
||||
@@ -0,0 +1,665 @@
|
||||
/* Tests for struct alloc_buffer.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <alloc_buffer.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
/* Return true if PTR is sufficiently aligned for TYPE. */
|
||||
#define IS_ALIGNED(ptr, type) \
|
||||
((((uintptr_t) ptr) & (__alloc_buffer_assert_align (__alignof (type)) - 1)) \
|
||||
== 0)
|
||||
|
||||
/* Structure with non-power-of-two size. */
|
||||
struct twelve
|
||||
{
|
||||
uint32_t buffer[3] __attribute__ ((aligned (4)));
|
||||
};
|
||||
_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
|
||||
_Static_assert (__alignof__ (struct twelve) == 4, "struct twelve");
|
||||
|
||||
/* Check for success obtaining empty arrays. Does not assume the
|
||||
buffer is empty. */
|
||||
static void
|
||||
test_empty_array (struct alloc_buffer refbuf)
|
||||
{
|
||||
bool refbuf_failed = alloc_buffer_has_failed (&refbuf);
|
||||
if (test_verbose)
|
||||
printf ("info: %s: current=0x%llx end=0x%llx refbuf_failed=%d\n",
|
||||
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
|
||||
(unsigned long long) refbuf.__alloc_buffer_end, refbuf_failed);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY ((alloc_buffer_alloc_bytes (&buf, 0) == NULL)
|
||||
== refbuf_failed);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY ((alloc_buffer_alloc_array (&buf, char, 0) == NULL)
|
||||
== refbuf_failed);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
|
||||
}
|
||||
/* The following tests can fail due to the need for aligning the
|
||||
returned pointer. */
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
bool expect_failure = refbuf_failed
|
||||
|| !IS_ALIGNED (alloc_buffer_next (&buf, void), double);
|
||||
double *ptr = alloc_buffer_alloc_array (&buf, double, 0);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, double));
|
||||
TEST_VERIFY ((ptr == NULL) == expect_failure);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
bool expect_failure = refbuf_failed
|
||||
|| !IS_ALIGNED (alloc_buffer_next (&buf, void), struct twelve);
|
||||
struct twelve *ptr = alloc_buffer_alloc_array (&buf, struct twelve, 0);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
|
||||
TEST_VERIFY ((ptr == NULL) == expect_failure);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test allocation of impossibly large arrays. */
|
||||
static void
|
||||
test_impossible_array (struct alloc_buffer refbuf)
|
||||
{
|
||||
if (test_verbose)
|
||||
printf ("info: %s: current=0x%llx end=0x%llx\n",
|
||||
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
|
||||
(unsigned long long) refbuf.__alloc_buffer_end);
|
||||
static const size_t counts[] =
|
||||
{ SIZE_MAX, SIZE_MAX - 1, SIZE_MAX - 2, SIZE_MAX - 3, SIZE_MAX - 4,
|
||||
SIZE_MAX / 2, SIZE_MAX / 2 + 1, SIZE_MAX / 2 - 1, 0};
|
||||
|
||||
for (int i = 0; counts[i] != 0; ++i)
|
||||
{
|
||||
size_t count = counts[i];
|
||||
if (test_verbose)
|
||||
printf ("info: %s: count=%zu\n", __func__, count);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
|
||||
== NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for failure to obtain anything from a failed buffer. */
|
||||
static void
|
||||
test_after_failure (struct alloc_buffer refbuf)
|
||||
{
|
||||
if (test_verbose)
|
||||
printf ("info: %s: current=0x%llx end=0x%llx\n",
|
||||
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
|
||||
(unsigned long long) refbuf.__alloc_buffer_end);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&refbuf));
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
alloc_buffer_add_byte (&buf, 17);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
|
||||
test_impossible_array (refbuf);
|
||||
for (int count = 0; count <= 4; ++count)
|
||||
{
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
|
||||
== NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_empty (struct alloc_buffer refbuf)
|
||||
{
|
||||
TEST_VERIFY (alloc_buffer_size (&refbuf) == 0);
|
||||
if (alloc_buffer_next (&refbuf, void) != NULL)
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
|
||||
test_empty_array (refbuf);
|
||||
test_impossible_array (refbuf);
|
||||
|
||||
/* Failure to obtain non-empty objects. */
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
alloc_buffer_add_byte (&buf, 17);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_size_1 (struct alloc_buffer refbuf)
|
||||
{
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
|
||||
TEST_VERIFY (alloc_buffer_size (&refbuf) == 1);
|
||||
test_empty_array (refbuf);
|
||||
test_impossible_array (refbuf);
|
||||
|
||||
/* Success adding a single byte. */
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
alloc_buffer_add_byte (&buf, 17);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x11", 1) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
signed char *ptr = alloc_buffer_alloc (&buf, signed char);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = 126;
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\176", 1) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = (char) 253;
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\xfd", 1) == 0);
|
||||
|
||||
/* Failure with larger objects. */
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, short) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_size_2 (struct alloc_buffer refbuf)
|
||||
{
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
|
||||
TEST_VERIFY (alloc_buffer_size (&refbuf) == 2);
|
||||
TEST_VERIFY (IS_ALIGNED (alloc_buffer_next (&refbuf, void), short));
|
||||
test_empty_array (refbuf);
|
||||
test_impossible_array (refbuf);
|
||||
|
||||
/* Success adding two bytes. */
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
alloc_buffer_add_byte (&buf, '@');
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
test_size_1 (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "@\xfd", 2) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
signed char *ptr = alloc_buffer_alloc (&buf, signed char);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = 'A';
|
||||
test_size_1 (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "A\xfd", 2) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = 'B';
|
||||
test_size_1 (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "B\xfd", 2) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
unsigned short *ptr = alloc_buffer_alloc (&buf, unsigned short);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = htons (0x12f4);
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x12\xf4", 2) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
unsigned short *ptr = alloc_buffer_alloc_array (&buf, unsigned short, 1);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
*ptr = htons (0x13f5);
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x13\xf5", 2) == 0);
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
char *ptr = alloc_buffer_alloc_array (&buf, char, 2);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
memcpy (ptr, "12", 2);
|
||||
test_empty (buf);
|
||||
}
|
||||
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "12", 2) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_misaligned (char pad)
|
||||
{
|
||||
enum { SIZE = 23 };
|
||||
char *backing = xmalloc (SIZE + 2);
|
||||
backing[0] = ~pad;
|
||||
backing[SIZE + 1] = pad;
|
||||
struct alloc_buffer refbuf = alloc_buffer_create (backing + 1, SIZE);
|
||||
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
short *ptr = alloc_buffer_alloc_array (&buf, short, SIZE / sizeof (short));
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, short));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
for (int i = 0; i < SIZE / sizeof (short); ++i)
|
||||
ptr[i] = htons (0xff01 + i);
|
||||
TEST_VERIFY (memcmp (ptr,
|
||||
"\xff\x01\xff\x02\xff\x03\xff\x04"
|
||||
"\xff\x05\xff\x06\xff\x07\xff\x08"
|
||||
"\xff\x09\xff\x0a\xff\x0b", 22) == 0);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
uint32_t *ptr = alloc_buffer_alloc_array
|
||||
(&buf, uint32_t, SIZE / sizeof (uint32_t));
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
for (int i = 0; i < SIZE / sizeof (uint32_t); ++i)
|
||||
ptr[i] = htonl (0xf1e2d301 + i);
|
||||
TEST_VERIFY (memcmp (ptr,
|
||||
"\xf1\xe2\xd3\x01\xf1\xe2\xd3\x02"
|
||||
"\xf1\xe2\xd3\x03\xf1\xe2\xd3\x04"
|
||||
"\xf1\xe2\xd3\x05", 20) == 0);
|
||||
}
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
struct twelve *ptr = alloc_buffer_alloc (&buf, struct twelve);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
ptr->buffer[0] = htonl (0x11223344);
|
||||
ptr->buffer[1] = htonl (0x55667788);
|
||||
ptr->buffer[2] = htonl (0x99aabbcc);
|
||||
TEST_VERIFY (memcmp (ptr,
|
||||
"\x11\x22\x33\x44"
|
||||
"\x55\x66\x77\x88"
|
||||
"\x99\xaa\xbb\xcc", 12) == 0);
|
||||
}
|
||||
{
|
||||
static const double nums[] = { 1, 2 };
|
||||
struct alloc_buffer buf = refbuf;
|
||||
double *ptr = alloc_buffer_alloc_array (&buf, double, 2);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
TEST_VERIFY (IS_ALIGNED (ptr, double));
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
ptr[0] = nums[0];
|
||||
ptr[1] = nums[1];
|
||||
TEST_VERIFY (memcmp (ptr, nums, sizeof (nums)) == 0);
|
||||
}
|
||||
|
||||
/* Verify that padding was not overwritten. */
|
||||
TEST_VERIFY (backing[0] == ~pad);
|
||||
TEST_VERIFY (backing[SIZE + 1] == pad);
|
||||
free (backing);
|
||||
}
|
||||
|
||||
/* Check that overflow during alignment is handled properly. */
|
||||
static void
|
||||
test_large_misaligned (void)
|
||||
{
|
||||
uintptr_t minus1 = -1;
|
||||
uintptr_t start = minus1 & ~0xfe;
|
||||
struct alloc_buffer refbuf = alloc_buffer_create ((void *) start, 16);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
|
||||
|
||||
struct __attribute__ ((aligned (256))) align256
|
||||
{
|
||||
int dymmy;
|
||||
};
|
||||
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc (&buf, struct align256) == NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
for (int count = 0; count < 3; ++count)
|
||||
{
|
||||
struct alloc_buffer buf = refbuf;
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct align256, count)
|
||||
== NULL);
|
||||
test_after_failure (buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check behavior of large allocations. */
|
||||
static void
|
||||
test_large (void)
|
||||
{
|
||||
{
|
||||
/* Allocation which wraps around. */
|
||||
struct alloc_buffer buf = { 1, SIZE_MAX };
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, SIZE_MAX) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
|
||||
{
|
||||
/* Successful very large allocation. */
|
||||
struct alloc_buffer buf = { 1, SIZE_MAX };
|
||||
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
|
||||
(&buf, char, SIZE_MAX - 1);
|
||||
TEST_VERIFY (val == 1);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
test_empty (buf);
|
||||
}
|
||||
|
||||
{
|
||||
typedef char __attribute__ ((aligned (2))) char2;
|
||||
|
||||
/* Overflow in array size computation. */
|
||||
struct alloc_buffer buf = { 1, SIZE_MAX };
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, SIZE_MAX - 1) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
|
||||
/* Successful allocation after alignment. */
|
||||
buf = (struct alloc_buffer) { 1, SIZE_MAX };
|
||||
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
|
||||
(&buf, char2, SIZE_MAX - 2);
|
||||
TEST_VERIFY (val == 2);
|
||||
test_empty (buf);
|
||||
|
||||
/* Alignment behavior near the top of the address space. */
|
||||
buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
|
||||
TEST_VERIFY (alloc_buffer_next (&buf, char2) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, 0) == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
}
|
||||
|
||||
{
|
||||
typedef short __attribute__ ((aligned (2))) short2;
|
||||
|
||||
/* Test overflow in size computation. */
|
||||
struct alloc_buffer buf = { 1, SIZE_MAX };
|
||||
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short2, SIZE_MAX / 2)
|
||||
== NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
|
||||
/* A slightly smaller array fits within the allocation. */
|
||||
buf = (struct alloc_buffer) { 2, SIZE_MAX - 1 };
|
||||
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
|
||||
(&buf, short2, SIZE_MAX / 2 - 1);
|
||||
TEST_VERIFY (val == 2);
|
||||
test_empty (buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_copy_bytes (void)
|
||||
{
|
||||
char backing[4];
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
alloc_buffer_copy_bytes (&buf, "1", 1);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 3);
|
||||
TEST_VERIFY (memcmp (backing, "1@@@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
alloc_buffer_copy_bytes (&buf, "12", 3);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 1);
|
||||
TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
alloc_buffer_copy_bytes (&buf, "1234", 4);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 0);
|
||||
TEST_VERIFY (memcmp (backing, "1234", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
alloc_buffer_copy_bytes (&buf, "1234", 5);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
alloc_buffer_copy_bytes (&buf, "1234", -1);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_copy_string (void)
|
||||
{
|
||||
char backing[4];
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
const char *p = alloc_buffer_copy_string (&buf, "");
|
||||
TEST_VERIFY (p == backing);
|
||||
TEST_VERIFY (strcmp (p, "") == 0);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 3);
|
||||
TEST_VERIFY (memcmp (backing, "\0@@@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
const char *p = alloc_buffer_copy_string (&buf, "1");
|
||||
TEST_VERIFY (p == backing);
|
||||
TEST_VERIFY (strcmp (p, "1") == 0);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 2);
|
||||
TEST_VERIFY (memcmp (backing, "1\0@@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
const char *p = alloc_buffer_copy_string (&buf, "12");
|
||||
TEST_VERIFY (p == backing);
|
||||
TEST_VERIFY (strcmp (p, "12") == 0);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 1);
|
||||
TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
const char *p = alloc_buffer_copy_string (&buf, "123");
|
||||
TEST_VERIFY (p == backing);
|
||||
TEST_VERIFY (strcmp (p, "123") == 0);
|
||||
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (alloc_buffer_size (&buf) == 0);
|
||||
TEST_VERIFY (memcmp (backing, "123", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
TEST_VERIFY (alloc_buffer_copy_string (&buf, "1234") == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
|
||||
}
|
||||
{
|
||||
memset (backing, '@', sizeof (backing));
|
||||
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
|
||||
TEST_VERIFY (alloc_buffer_copy_string (&buf, "12345") == NULL);
|
||||
TEST_VERIFY (alloc_buffer_has_failed (&buf));
|
||||
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
test_empty (alloc_buffer_create (NULL, 0));
|
||||
test_empty (alloc_buffer_create ((char *) "", 0));
|
||||
test_empty (alloc_buffer_create ((void *) 1, 0));
|
||||
|
||||
{
|
||||
void *ptr = (void *) ""; /* Cannot be freed. */
|
||||
struct alloc_buffer buf = alloc_buffer_allocate (1, &ptr);
|
||||
test_size_1 (buf);
|
||||
free (ptr); /* Should have been overwritten. */
|
||||
}
|
||||
|
||||
{
|
||||
void *ptr= (void *) ""; /* Cannot be freed. */
|
||||
struct alloc_buffer buf = alloc_buffer_allocate (2, &ptr);
|
||||
test_size_2 (buf);
|
||||
free (ptr); /* Should have been overwritten. */
|
||||
}
|
||||
|
||||
test_misaligned (0);
|
||||
test_misaligned (0xc7);
|
||||
test_misaligned (0xff);
|
||||
|
||||
test_large_misaligned ();
|
||||
test_large ();
|
||||
test_copy_bytes ();
|
||||
test_copy_string ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,125 @@
|
||||
/* Test reporting of out-of-bounds access for dynamic arrays.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "tst-dynarray-shared.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* Run CALLBACK and check that the data on standard error equals
|
||||
EXPECTED. */
|
||||
static void
|
||||
check (const char *test, void (*callback) (void *), size_t index,
|
||||
const char *expected)
|
||||
{
|
||||
struct support_capture_subprocess result
|
||||
= support_capture_subprocess (callback, &index);
|
||||
if (strcmp (result.err.buffer, expected) != 0)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("error: test %s (%zu) unexpected standard error data\n"
|
||||
" expected: %s\n"
|
||||
" actual: %s\n",
|
||||
test, index, expected, result.err.buffer);
|
||||
}
|
||||
TEST_VERIFY (strlen (result.out.buffer) == 0);
|
||||
TEST_VERIFY (WIFSIGNALED (result.status));
|
||||
if (WIFSIGNALED (result.status))
|
||||
TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
|
||||
support_capture_subprocess_free (&result);
|
||||
}
|
||||
|
||||
/* Try indexing an empty array. */
|
||||
static void
|
||||
test_empty (void *closure)
|
||||
{
|
||||
size_t *pindex = closure;
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
dynarray_int_at (&dyn, *pindex);
|
||||
}
|
||||
|
||||
/* Try indexing a one-element array. */
|
||||
static void
|
||||
test_one (void *closure)
|
||||
{
|
||||
size_t *pindex = closure;
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
TEST_VERIFY (dynarray_int_resize (&dyn, 1));
|
||||
dynarray_int_at (&dyn, *pindex);
|
||||
}
|
||||
|
||||
/* Try indexing a longer array. */
|
||||
static void
|
||||
test_many (void *closure)
|
||||
{
|
||||
size_t *pindex = closure;
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
TEST_VERIFY (dynarray_int_resize (&dyn, 5371));
|
||||
dynarray_int_at (&dyn, *pindex);
|
||||
}
|
||||
|
||||
/* (size_t) -1 for use in string literals. */
|
||||
#if SIZE_WIDTH == 32
|
||||
# define MINUS_1 "4294967295"
|
||||
#elif SIZE_WIDTH == 64
|
||||
# define MINUS_1 "18446744073709551615"
|
||||
#else
|
||||
# error "unknown value for SIZE_WIDTH"
|
||||
#endif
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
TEST_VERIFY (setenv ("LIBC_FATAL_STDERR_", "1", 1) == 0);
|
||||
|
||||
check ("test_empty", test_empty, 0,
|
||||
"Fatal glibc error: array index 0 not less than array length 0\n");
|
||||
check ("test_empty", test_empty, 1,
|
||||
"Fatal glibc error: array index 1 not less than array length 0\n");
|
||||
check ("test_empty", test_empty, -1,
|
||||
"Fatal glibc error: array index " MINUS_1
|
||||
" not less than array length 0\n");
|
||||
|
||||
check ("test_one", test_one, 1,
|
||||
"Fatal glibc error: array index 1 not less than array length 1\n");
|
||||
check ("test_one", test_one, 2,
|
||||
"Fatal glibc error: array index 2 not less than array length 1\n");
|
||||
check ("test_one", test_one, -1,
|
||||
"Fatal glibc error: array index " MINUS_1
|
||||
" not less than array length 1\n");
|
||||
|
||||
check ("test_many", test_many, 5371,
|
||||
"Fatal glibc error: array index 5371"
|
||||
" not less than array length 5371\n");
|
||||
check ("test_many", test_many, 5372,
|
||||
"Fatal glibc error: array index 5372"
|
||||
" not less than array length 5371\n");
|
||||
check ("test_many", test_many, -1,
|
||||
"Fatal glibc error: array index " MINUS_1
|
||||
" not less than array length 5371\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,418 @@
|
||||
/* Test allocation failures with dynamic arrays.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test is separate from tst-dynarray because it cannot run under
|
||||
valgrind. */
|
||||
|
||||
#include "tst-dynarray-shared.h"
|
||||
|
||||
#include <mcheck.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Data structure to fill up the heap. */
|
||||
struct heap_filler
|
||||
{
|
||||
struct heap_filler *next;
|
||||
};
|
||||
|
||||
/* Allocate objects until the heap is full. */
|
||||
static struct heap_filler *
|
||||
fill_heap (void)
|
||||
{
|
||||
size_t pad = 4096;
|
||||
struct heap_filler *head = NULL;
|
||||
while (true)
|
||||
{
|
||||
struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
|
||||
if (new_head == NULL)
|
||||
{
|
||||
if (pad > 0)
|
||||
{
|
||||
/* Try again with smaller allocations. */
|
||||
pad = 0;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
new_head->next = head;
|
||||
head = new_head;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
/* Free the heap-filling allocations, so that we can continue testing
|
||||
and detect memory leaks elsewhere. */
|
||||
static void
|
||||
free_fill_heap (struct heap_filler *head)
|
||||
{
|
||||
while (head != NULL)
|
||||
{
|
||||
struct heap_filler *next = head->next;
|
||||
free (head);
|
||||
head = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check allocation failures for int arrays (without an element free
|
||||
function). */
|
||||
static void
|
||||
test_int_fail (void)
|
||||
{
|
||||
/* Exercise failure in add/emplace.
|
||||
|
||||
do_add: Use emplace (false) or add (true) to add elements.
|
||||
do_finalize: Perform finalization at the end (instead of free). */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
|
||||
{
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
size_t count = 0;
|
||||
while (true)
|
||||
{
|
||||
if (do_add)
|
||||
{
|
||||
dynarray_int_add (&dyn, 0);
|
||||
if (dynarray_int_has_failed (&dyn))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int *place = dynarray_int_emplace (&dyn);
|
||||
if (place == NULL)
|
||||
break;
|
||||
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
|
||||
*place = 0;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
printf ("info: %s: failure after %zu elements\n", __func__, count);
|
||||
TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
|
||||
if (do_finalize)
|
||||
{
|
||||
struct int_array result = { (int *) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT (result.length == (size_t) -1);
|
||||
}
|
||||
else
|
||||
dynarray_int_free (&dyn);
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
}
|
||||
|
||||
/* Exercise failure in finalize. */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
{
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
for (unsigned int i = 0; i < 10000; ++i)
|
||||
{
|
||||
if (do_add)
|
||||
{
|
||||
dynarray_int_add (&dyn, i);
|
||||
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
|
||||
}
|
||||
else
|
||||
{
|
||||
int *place = dynarray_int_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
*place = i;
|
||||
}
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
|
||||
struct heap_filler *heap_filler = fill_heap ();
|
||||
struct int_array result = { (int *) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT (result.length == (size_t) -1);
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
free_fill_heap (heap_filler);
|
||||
}
|
||||
|
||||
/* Exercise failure in resize. */
|
||||
{
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
struct heap_filler *heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
|
||||
TEST_VERIFY (dynarray_int_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
|
||||
dynarray_int_init (&dyn);
|
||||
TEST_VERIFY (dynarray_int_resize (&dyn, 1));
|
||||
heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
|
||||
TEST_VERIFY (dynarray_int_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
|
||||
dynarray_int_init (&dyn);
|
||||
TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
|
||||
heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
|
||||
TEST_VERIFY (dynarray_int_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check allocation failures for char * arrays (which automatically
|
||||
free the pointed-to strings). */
|
||||
static void
|
||||
test_str_fail (void)
|
||||
{
|
||||
/* Exercise failure in add/emplace.
|
||||
|
||||
do_add: Use emplace (false) or add (true) to add elements.
|
||||
do_finalize: Perform finalization at the end (instead of free). */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
|
||||
{
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
size_t count = 0;
|
||||
while (true)
|
||||
{
|
||||
char **place;
|
||||
if (do_add)
|
||||
{
|
||||
dynarray_str_add (&dyn, NULL);
|
||||
if (dynarray_str_has_failed (&dyn))
|
||||
break;
|
||||
else
|
||||
place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
place = dynarray_str_emplace (&dyn);
|
||||
if (place == NULL)
|
||||
break;
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT (*place == NULL);
|
||||
*place = strdup ("placeholder");
|
||||
if (*place == NULL)
|
||||
{
|
||||
/* Second loop to wait for failure of
|
||||
dynarray_str_emplace. */
|
||||
while (true)
|
||||
{
|
||||
if (do_add)
|
||||
{
|
||||
dynarray_str_add (&dyn, NULL);
|
||||
if (dynarray_str_has_failed (&dyn))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
char **place = dynarray_str_emplace (&dyn);
|
||||
if (place == NULL)
|
||||
break;
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
*place = NULL;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
printf ("info: %s: failure after %zu elements\n", __func__, count);
|
||||
TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
|
||||
if (do_finalize)
|
||||
{
|
||||
struct str_array result = { (char **) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT (result.length == (size_t) -1);
|
||||
}
|
||||
else
|
||||
dynarray_str_free (&dyn);
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
|
||||
}
|
||||
|
||||
/* Exercise failure in finalize. */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
{
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
for (unsigned int i = 0; i < 1000; ++i)
|
||||
{
|
||||
if (do_add)
|
||||
dynarray_str_add (&dyn, xstrdup ("placeholder"));
|
||||
else
|
||||
{
|
||||
char **place = dynarray_str_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
TEST_VERIFY_EXIT (*place == NULL);
|
||||
*place = xstrdup ("placeholder");
|
||||
}
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
struct heap_filler *heap_filler = fill_heap ();
|
||||
struct str_array result = { (char **) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT (result.length == (size_t) -1);
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
|
||||
free_fill_heap (heap_filler);
|
||||
}
|
||||
|
||||
/* Exercise failure in resize. */
|
||||
{
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
struct heap_filler *heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
|
||||
TEST_VERIFY (dynarray_str_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
|
||||
dynarray_str_init (&dyn);
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
|
||||
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
|
||||
heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
|
||||
TEST_VERIFY (dynarray_str_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
|
||||
dynarray_str_init (&dyn);
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
|
||||
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
|
||||
heap_filler = fill_heap ();
|
||||
TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
|
||||
TEST_VERIFY (dynarray_str_has_failed (&dyn));
|
||||
free_fill_heap (heap_filler);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test if mmap can allocate a page. This is necessary because
|
||||
setrlimit does not fail even if it reduces the RLIMIT_AS limit
|
||||
below what is currently needed by the process. */
|
||||
static bool
|
||||
mmap_works (void)
|
||||
{
|
||||
void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
return false;
|
||||
xmunmap (ptr, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Set the RLIMIT_AS limit to the value in *LIMIT. */
|
||||
static void
|
||||
xsetrlimit_as (const struct rlimit *limit)
|
||||
{
|
||||
if (setrlimit (RLIMIT_AS, limit) != 0)
|
||||
FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
|
||||
(unsigned long) limit->rlim_cur);
|
||||
}
|
||||
|
||||
/* Approximately this many bytes can be allocated after
|
||||
reduce_rlimit_as has run. */
|
||||
enum { as_limit_reserve = 2 * 1024 * 1024 };
|
||||
|
||||
/* Limit the size of the process, so that memory allocation in
|
||||
allocate_thread will eventually fail, without impacting the entire
|
||||
system. By default, a dynamic limit which leaves room for 2 MiB is
|
||||
activated. The TEST_RLIMIT_AS environment variable overrides
|
||||
it. */
|
||||
static void
|
||||
reduce_rlimit_as (void)
|
||||
{
|
||||
struct rlimit limit;
|
||||
if (getrlimit (RLIMIT_AS, &limit) != 0)
|
||||
FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
|
||||
|
||||
/* Use the TEST_RLIMIT_AS setting if available. */
|
||||
{
|
||||
long target = 0;
|
||||
const char *variable = "TEST_RLIMIT_AS";
|
||||
const char *target_str = getenv (variable);
|
||||
if (target_str != NULL)
|
||||
{
|
||||
target = atoi (target_str);
|
||||
if (target <= 0)
|
||||
FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
|
||||
printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
|
||||
target *= 1024 * 1024; /* Convert to megabytes. */
|
||||
limit.rlim_cur = target;
|
||||
xsetrlimit_as (&limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, try to find the limit with a binary search. */
|
||||
unsigned long low = 1 << 20;
|
||||
limit.rlim_cur = low;
|
||||
xsetrlimit_as (&limit);
|
||||
|
||||
/* Find working upper limit. */
|
||||
unsigned long high = 1 << 30;
|
||||
while (true)
|
||||
{
|
||||
limit.rlim_cur = high;
|
||||
xsetrlimit_as (&limit);
|
||||
if (mmap_works ())
|
||||
break;
|
||||
if (2 * high < high)
|
||||
FAIL_EXIT1 ("cannot find upper AS limit");
|
||||
high *= 2;
|
||||
}
|
||||
|
||||
/* Perform binary search. */
|
||||
while ((high - low) > 128 * 1024)
|
||||
{
|
||||
unsigned long middle = (low + high) / 2;
|
||||
limit.rlim_cur = middle;
|
||||
xsetrlimit_as (&limit);
|
||||
if (mmap_works ())
|
||||
high = middle;
|
||||
else
|
||||
low = middle;
|
||||
}
|
||||
|
||||
unsigned long target = high + as_limit_reserve;
|
||||
limit.rlim_cur = target;
|
||||
xsetrlimit_as (&limit);
|
||||
printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
reduce_rlimit_as ();
|
||||
test_int_fail ();
|
||||
test_str_fail ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT 90
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Shared definitions for dynarray tests.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct int_array
|
||||
{
|
||||
int *array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_int
|
||||
#define DYNARRAY_ELEMENT int
|
||||
#define DYNARRAY_PREFIX dynarray_int_
|
||||
#define DYNARRAY_FINAL_TYPE struct int_array
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
struct str_array
|
||||
{
|
||||
char **array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_str
|
||||
#define DYNARRAY_ELEMENT char *
|
||||
#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
|
||||
#define DYNARRAY_PREFIX dynarray_str_
|
||||
#define DYNARRAY_FINAL_TYPE struct str_array
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
/* Check that *DYN is equivalent to its initial state. */
|
||||
#define CHECK_INIT_STATE(type, dyn) \
|
||||
({ \
|
||||
TEST_VERIFY_EXIT (!dynarray_##type##_has_failed (dyn)); \
|
||||
TEST_VERIFY_EXIT (dynarray_##type##_size (dyn) == 0); \
|
||||
TEST_VERIFY_EXIT ((dyn)->dynarray_header.array \
|
||||
== (dyn)->scratch); \
|
||||
TEST_VERIFY_EXIT ((dyn)->dynarray_header.allocated > 0); \
|
||||
(void) 0; \
|
||||
})
|
||||
|
||||
/* Check that *DYN behaves as if it is in its initial state. */
|
||||
#define CHECK_EMPTY(type, dyn) \
|
||||
({ \
|
||||
CHECK_INIT_STATE (type, (dyn)); \
|
||||
dynarray_##type##_free (dyn); \
|
||||
CHECK_INIT_STATE (type, (dyn)); \
|
||||
dynarray_##type##_clear (dyn); \
|
||||
CHECK_INIT_STATE (type, (dyn)); \
|
||||
dynarray_##type##_remove_last (dyn); \
|
||||
CHECK_INIT_STATE (type, (dyn)); \
|
||||
dynarray_##type##_mark_failed (dyn); \
|
||||
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
|
||||
dynarray_##type##_clear (dyn); \
|
||||
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
|
||||
dynarray_##type##_remove_last (dyn); \
|
||||
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
|
||||
TEST_VERIFY_EXIT (dynarray_##type##_emplace (dyn) == NULL); \
|
||||
dynarray_##type##_free (dyn); \
|
||||
CHECK_INIT_STATE (type, (dyn)); \
|
||||
/* These functions should not assert. */ \
|
||||
dynarray_##type##_begin (dyn); \
|
||||
dynarray_##type##_end (dyn); \
|
||||
(void) 0; \
|
||||
})
|
||||
@@ -0,0 +1,574 @@
|
||||
/* Test for dynamic arrays.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "tst-dynarray-shared.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_long
|
||||
#define DYNARRAY_ELEMENT long
|
||||
#define DYNARRAY_PREFIX dynarray_long_
|
||||
#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 17)
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
struct long_array
|
||||
{
|
||||
long *array;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_long_noscratch
|
||||
#define DYNARRAY_ELEMENT long
|
||||
#define DYNARRAY_PREFIX dynarray_long_noscratch_
|
||||
#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 23)
|
||||
#define DYNARRAY_FINAL_TYPE struct long_array
|
||||
#define DYNARRAY_INITIAL_SIZE 0
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
#define DYNARRAY_STRUCT zstr
|
||||
#define DYNARRAY_ELEMENT char
|
||||
#define DYNARRAY_PREFIX zstr_
|
||||
#define DYNARRAY_INITIAL_SIZE 128
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <mcheck.h>
|
||||
#include <stdint.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
enum { max_count = 20 };
|
||||
|
||||
/* Test dynamic arrays with int elements (no automatic deallocation
|
||||
for elements). */
|
||||
static void
|
||||
test_int (void)
|
||||
{
|
||||
/* Empty array. */
|
||||
{
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
CHECK_EMPTY (int, &dyn);
|
||||
}
|
||||
|
||||
/* Empty array with finalization. */
|
||||
{
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
struct int_array result = { (int *) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
TEST_VERIFY_EXIT (result.array == NULL);
|
||||
TEST_VERIFY_EXIT (result.length == 0);
|
||||
}
|
||||
|
||||
/* Non-empty array tests.
|
||||
|
||||
do_add: Switch between emplace (false) and add (true).
|
||||
do_finalize: Perform finalize call at the end.
|
||||
do_clear: Perform clear call at the end.
|
||||
do_remove_last: Perform remove_last call after adding elements.
|
||||
count: Number of elements added to the array. */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
|
||||
for (int do_clear = 0; do_clear < 2; ++do_clear)
|
||||
for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
|
||||
for (unsigned int count = 0; count < max_count; ++count)
|
||||
{
|
||||
if (do_remove_last && count == 0)
|
||||
continue;
|
||||
unsigned int base = count * count;
|
||||
struct dynarray_int dyn;
|
||||
dynarray_int_init (&dyn);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
if (do_add)
|
||||
dynarray_int_add (&dyn, base + i);
|
||||
else
|
||||
{
|
||||
int *place = dynarray_int_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
*place = base + i;
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == i + 1);
|
||||
TEST_VERIFY_EXIT (dynarray_int_size (&dyn)
|
||||
<= dyn.dynarray_header.allocated);
|
||||
}
|
||||
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == count);
|
||||
TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
|
||||
if (count > 0)
|
||||
{
|
||||
TEST_VERIFY (dynarray_int_begin (&dyn)
|
||||
== dynarray_int_at (&dyn, 0));
|
||||
TEST_VERIFY (dynarray_int_end (&dyn)
|
||||
== dynarray_int_at (&dyn, count - 1) + 1);
|
||||
}
|
||||
unsigned final_count;
|
||||
bool heap_array = dyn.dynarray_header.array != dyn.scratch;
|
||||
if (do_remove_last)
|
||||
{
|
||||
dynarray_int_remove_last (&dyn);
|
||||
if (count == 0)
|
||||
final_count = 0;
|
||||
else
|
||||
final_count = count - 1;
|
||||
}
|
||||
else
|
||||
final_count = count;
|
||||
if (final_count > 0)
|
||||
{
|
||||
TEST_VERIFY (dynarray_int_begin (&dyn)
|
||||
== dynarray_int_at (&dyn, 0));
|
||||
TEST_VERIFY (dynarray_int_end (&dyn)
|
||||
== dynarray_int_at (&dyn, final_count - 1) + 1);
|
||||
}
|
||||
if (do_clear)
|
||||
{
|
||||
dynarray_int_clear (&dyn);
|
||||
final_count = 0;
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
|
||||
== heap_array);
|
||||
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == final_count);
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
|
||||
if (!do_clear)
|
||||
for (unsigned int i = 0; i < final_count; ++i)
|
||||
TEST_VERIFY_EXIT (*dynarray_int_at (&dyn, i) == base + i);
|
||||
if (do_finalize)
|
||||
{
|
||||
struct int_array result = { (int *) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
TEST_VERIFY_EXIT (result.length == final_count);
|
||||
if (final_count == 0)
|
||||
TEST_VERIFY_EXIT (result.array == NULL);
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (result.array != NULL);
|
||||
TEST_VERIFY_EXIT (result.array != (int *) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT
|
||||
(malloc_usable_size (result.array)
|
||||
>= final_count * sizeof (result.array[0]));
|
||||
for (unsigned int i = 0; i < final_count; ++i)
|
||||
TEST_VERIFY_EXIT (result.array[i] == base + i);
|
||||
free (result.array);
|
||||
}
|
||||
}
|
||||
else /* !do_finalize */
|
||||
{
|
||||
dynarray_int_free (&dyn);
|
||||
CHECK_INIT_STATE (int, &dyn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Test dynamic arrays with char * elements (with automatic
|
||||
deallocation of the pointed-to strings). */
|
||||
static void
|
||||
test_str (void)
|
||||
{
|
||||
/* Empty array. */
|
||||
{
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
CHECK_EMPTY (str, &dyn);
|
||||
}
|
||||
|
||||
/* Empty array with finalization. */
|
||||
{
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
struct str_array result = { (char **) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
|
||||
CHECK_INIT_STATE (str, &dyn);
|
||||
TEST_VERIFY_EXIT (result.array == NULL);
|
||||
TEST_VERIFY_EXIT (result.length == 0);
|
||||
}
|
||||
|
||||
/* Non-empty array tests.
|
||||
|
||||
do_add: Switch between emplace (false) and add (true).
|
||||
do_finalize: Perform finalize call at the end.
|
||||
do_clear: Perform clear call at the end.
|
||||
do_remove_last: Perform remove_last call after adding elements.
|
||||
count: Number of elements added to the array. */
|
||||
for (int do_add = 0; do_add < 2; ++do_add)
|
||||
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
|
||||
for (int do_clear = 0; do_clear < 2; ++do_clear)
|
||||
for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
|
||||
for (unsigned int count = 0; count < max_count; ++count)
|
||||
{
|
||||
if (do_remove_last && count == 0)
|
||||
continue;
|
||||
unsigned int base = count * count;
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
char *item = xasprintf ("%d", base + i);
|
||||
if (do_add)
|
||||
dynarray_str_add (&dyn, item);
|
||||
else
|
||||
{
|
||||
char **place = dynarray_str_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
TEST_VERIFY_EXIT (*place == NULL);
|
||||
*place = item;
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == i + 1);
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn)
|
||||
<= dyn.dynarray_header.allocated);
|
||||
}
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == count);
|
||||
TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
|
||||
if (count > 0)
|
||||
{
|
||||
TEST_VERIFY (dynarray_str_begin (&dyn)
|
||||
== dynarray_str_at (&dyn, 0));
|
||||
TEST_VERIFY (dynarray_str_end (&dyn)
|
||||
== dynarray_str_at (&dyn, count - 1) + 1);
|
||||
}
|
||||
unsigned final_count;
|
||||
bool heap_array = dyn.dynarray_header.array != dyn.scratch;
|
||||
if (do_remove_last)
|
||||
{
|
||||
dynarray_str_remove_last (&dyn);
|
||||
if (count == 0)
|
||||
final_count = 0;
|
||||
else
|
||||
final_count = count - 1;
|
||||
}
|
||||
else
|
||||
final_count = count;
|
||||
if (final_count > 0)
|
||||
{
|
||||
TEST_VERIFY (dynarray_str_begin (&dyn)
|
||||
== dynarray_str_at (&dyn, 0));
|
||||
TEST_VERIFY (dynarray_str_end (&dyn)
|
||||
== dynarray_str_at (&dyn, final_count - 1) + 1);
|
||||
}
|
||||
if (do_clear)
|
||||
{
|
||||
dynarray_str_clear (&dyn);
|
||||
final_count = 0;
|
||||
}
|
||||
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
|
||||
TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
|
||||
== heap_array);
|
||||
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == final_count);
|
||||
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
|
||||
if (!do_clear)
|
||||
for (unsigned int i = 0; i < count - do_remove_last; ++i)
|
||||
{
|
||||
char *expected = xasprintf ("%d", base + i);
|
||||
const char *actual = *dynarray_str_at (&dyn, i);
|
||||
TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
|
||||
free (expected);
|
||||
}
|
||||
if (do_finalize)
|
||||
{
|
||||
struct str_array result = { (char **) (uintptr_t) -1, -1 };
|
||||
TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
|
||||
CHECK_INIT_STATE (str, &dyn);
|
||||
TEST_VERIFY_EXIT (result.length == final_count);
|
||||
if (final_count == 0)
|
||||
TEST_VERIFY_EXIT (result.array == NULL);
|
||||
else
|
||||
{
|
||||
TEST_VERIFY_EXIT (result.array != NULL);
|
||||
TEST_VERIFY_EXIT (result.array
|
||||
!= (char **) (uintptr_t) -1);
|
||||
TEST_VERIFY_EXIT (result.length
|
||||
== count - do_remove_last);
|
||||
TEST_VERIFY_EXIT
|
||||
(malloc_usable_size (result.array)
|
||||
>= final_count * sizeof (result.array[0]));
|
||||
for (unsigned int i = 0; i < count - do_remove_last; ++i)
|
||||
{
|
||||
char *expected = xasprintf ("%d", base + i);
|
||||
char *actual = result.array[i];
|
||||
TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
|
||||
free (expected);
|
||||
free (actual);
|
||||
}
|
||||
free (result.array);
|
||||
}
|
||||
}
|
||||
else /* !do_finalize */
|
||||
{
|
||||
dynarray_str_free (&dyn);
|
||||
CHECK_INIT_STATE (str, &dyn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test resizing. */
|
||||
{
|
||||
enum { count = 2131 };
|
||||
struct dynarray_str dyn;
|
||||
dynarray_str_init (&dyn);
|
||||
|
||||
/* From length 0 to length 1. */
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_str_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
|
||||
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
|
||||
dynarray_str_free (&dyn);
|
||||
|
||||
/* From length 0 to length 1 and 2. */
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_str_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
|
||||
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated0");
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 2));
|
||||
TEST_VERIFY (dynarray_str_size (&dyn) == 2);
|
||||
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
|
||||
TEST_VERIFY (*dynarray_str_at (&dyn, 1) == NULL);
|
||||
*dynarray_str_at (&dyn, 1) = xstrdup ("allocated1");
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, count));
|
||||
TEST_VERIFY (dynarray_str_size (&dyn) == count);
|
||||
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
|
||||
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
|
||||
for (int i = 2; i < count; ++i)
|
||||
TEST_VERIFY (*dynarray_str_at (&dyn, i) == NULL);
|
||||
*dynarray_str_at (&dyn, count - 1) = xstrdup ("allocated2");
|
||||
TEST_VERIFY (dynarray_str_resize (&dyn, 3));
|
||||
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
|
||||
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
|
||||
TEST_VERIFY (*dynarray_str_at (&dyn, 2) == NULL);
|
||||
dynarray_str_free (&dyn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that DYNARRAY_ELEMENT_INIT has an effect. */
|
||||
static void
|
||||
test_long_init (void)
|
||||
{
|
||||
enum { count = 2131 };
|
||||
{
|
||||
struct dynarray_long dyn;
|
||||
dynarray_long_init (&dyn);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
long *place = dynarray_long_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
TEST_VERIFY (*place == 17);
|
||||
}
|
||||
TEST_VERIFY (dynarray_long_size (&dyn) == count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
|
||||
dynarray_long_free (&dyn);
|
||||
|
||||
TEST_VERIFY (dynarray_long_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_long_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
|
||||
*dynarray_long_at (&dyn, 0) = 18;
|
||||
dynarray_long_free (&dyn);
|
||||
TEST_VERIFY (dynarray_long_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_long_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
|
||||
TEST_VERIFY (dynarray_long_resize (&dyn, 2));
|
||||
TEST_VERIFY (dynarray_long_size (&dyn) == 2);
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, 1) == 17);
|
||||
*dynarray_long_at (&dyn, 0) = 18;
|
||||
TEST_VERIFY (dynarray_long_resize (&dyn, count));
|
||||
TEST_VERIFY (dynarray_long_size (&dyn) == count);
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 18);
|
||||
for (int i = 1; i < count; ++i)
|
||||
TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
|
||||
dynarray_long_free (&dyn);
|
||||
}
|
||||
|
||||
/* Similar, but without an on-stack scratch region
|
||||
(DYNARRAY_INITIAL_SIZE is 0). */
|
||||
{
|
||||
struct dynarray_long_noscratch dyn;
|
||||
dynarray_long_noscratch_init (&dyn);
|
||||
struct long_array result;
|
||||
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
|
||||
TEST_VERIFY (result.array == NULL);
|
||||
TEST_VERIFY (result.length == 0);
|
||||
|
||||
/* Test with one element. */
|
||||
{
|
||||
long *place = dynarray_long_noscratch_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
TEST_VERIFY (*place == 23);
|
||||
}
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
|
||||
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array != NULL);
|
||||
TEST_VERIFY (result.length == 1);
|
||||
TEST_VERIFY (result.array[0] == 23);
|
||||
free (result.array);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
long *place = dynarray_long_noscratch_emplace (&dyn);
|
||||
TEST_VERIFY_EXIT (place != NULL);
|
||||
TEST_VERIFY (*place == 23);
|
||||
if (i == 0)
|
||||
*place = 29;
|
||||
}
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 29);
|
||||
for (int i = 1; i < count; ++i)
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
|
||||
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
|
||||
TEST_VERIFY_EXIT (result.array != NULL);
|
||||
TEST_VERIFY (result.length == count);
|
||||
TEST_VERIFY (result.array[0] == 29);
|
||||
for (int i = 1; i < count; ++i)
|
||||
TEST_VERIFY (result.array[i] == 23);
|
||||
free (result.array);
|
||||
|
||||
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
|
||||
*dynarray_long_noscratch_at (&dyn, 0) = 24;
|
||||
dynarray_long_noscratch_free (&dyn);
|
||||
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
|
||||
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 2));
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 2);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 1) == 23);
|
||||
*dynarray_long_noscratch_at (&dyn, 0) = 24;
|
||||
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, count));
|
||||
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 24);
|
||||
for (int i = 1; i < count; ++i)
|
||||
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
|
||||
dynarray_long_noscratch_free (&dyn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test overflow in resize. */
|
||||
static void
|
||||
test_long_overflow (void)
|
||||
{
|
||||
{
|
||||
struct dynarray_long dyn;
|
||||
dynarray_long_init (&dyn);
|
||||
errno = EINVAL;
|
||||
TEST_VERIFY (!dynarray_long_resize
|
||||
(&dyn, (SIZE_MAX / sizeof (long)) + 1));
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
TEST_VERIFY (dynarray_long_has_failed (&dyn));
|
||||
}
|
||||
|
||||
{
|
||||
struct dynarray_long_noscratch dyn;
|
||||
dynarray_long_noscratch_init (&dyn);
|
||||
errno = EINVAL;
|
||||
TEST_VERIFY (!dynarray_long_noscratch_resize
|
||||
(&dyn, (SIZE_MAX / sizeof (long)) + 1));
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
TEST_VERIFY (dynarray_long_noscratch_has_failed (&dyn));
|
||||
}
|
||||
}
|
||||
|
||||
/* Test NUL-terminated string construction with the add function and
|
||||
the simple finalize function. */
|
||||
static void
|
||||
test_zstr (void)
|
||||
{
|
||||
/* Totally empty string (no NUL termination). */
|
||||
{
|
||||
struct zstr s;
|
||||
zstr_init (&s);
|
||||
char *result = zstr_finalize (&s, NULL);
|
||||
TEST_VERIFY (result == NULL);
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
size_t length = 1;
|
||||
result = zstr_finalize (&s, &length);
|
||||
TEST_VERIFY (result == NULL);
|
||||
TEST_VERIFY (length == 0);
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
}
|
||||
|
||||
/* Empty string. */
|
||||
{
|
||||
struct zstr s;
|
||||
zstr_init (&s);
|
||||
zstr_add (&s, '\0');
|
||||
char *result = zstr_finalize (&s, NULL);
|
||||
TEST_VERIFY_EXIT (result != NULL);
|
||||
TEST_VERIFY (*result == '\0');
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
free (result);
|
||||
|
||||
zstr_add (&s, '\0');
|
||||
size_t length = 1;
|
||||
result = zstr_finalize (&s, &length);
|
||||
TEST_VERIFY_EXIT (result != NULL);
|
||||
TEST_VERIFY (*result == '\0');
|
||||
TEST_VERIFY (length == 1);
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
free (result);
|
||||
}
|
||||
|
||||
/* A few characters. */
|
||||
{
|
||||
struct zstr s;
|
||||
zstr_init (&s);
|
||||
zstr_add (&s, 'A');
|
||||
zstr_add (&s, 'b');
|
||||
zstr_add (&s, 'c');
|
||||
zstr_add (&s, '\0');
|
||||
char *result = zstr_finalize (&s, NULL);
|
||||
TEST_VERIFY_EXIT (result != NULL);
|
||||
TEST_VERIFY (strcmp (result, "Abc") == 0);
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
free (result);
|
||||
|
||||
zstr_add (&s, 'X');
|
||||
zstr_add (&s, 'y');
|
||||
zstr_add (&s, 'z');
|
||||
zstr_add (&s, '\0');
|
||||
size_t length = 1;
|
||||
result = zstr_finalize (&s, &length);
|
||||
TEST_VERIFY_EXIT (result != NULL);
|
||||
TEST_VERIFY (strcmp (result, "Xyz") == 0);
|
||||
TEST_VERIFY (length == 4);
|
||||
TEST_VERIFY (zstr_size (&s) == 0);
|
||||
free (result);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
test_int ();
|
||||
test_str ();
|
||||
test_long_init ();
|
||||
test_long_overflow ();
|
||||
test_zstr ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,237 @@
|
||||
/* Test and verify that too-large memory allocations fail with ENOMEM.
|
||||
Copyright (C) 2018 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Bug 22375 reported a regression in malloc where if after malloc'ing then
|
||||
free'ing a small block of memory, malloc is then called with a really
|
||||
large size argument (close to SIZE_MAX): instead of returning NULL and
|
||||
setting errno to ENOMEM, malloc incorrectly returns the previously
|
||||
allocated block instead. Bug 22343 reported a similar case where
|
||||
posix_memalign incorrectly returns successfully when called with an with
|
||||
a really large size argument.
|
||||
|
||||
Both of these were caused by integer overflows in the allocator when it
|
||||
was trying to pad the requested size to allow for book-keeping or
|
||||
alignment. This test guards against such bugs by repeatedly allocating
|
||||
and freeing small blocks of memory then trying to allocate various block
|
||||
sizes larger than the memory bus width of 64-bit targets, or almost
|
||||
as large as SIZE_MAX on 32-bit targets supported by glibc. In each case,
|
||||
it verifies that such impossibly large allocations correctly fail. */
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/resource.h>
|
||||
#include <libc-internal.h>
|
||||
#include <support/check.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
|
||||
/* This function prepares for each 'too-large memory allocation' test by
|
||||
performing a small successful malloc/free and resetting errno prior to
|
||||
the actual test. */
|
||||
static void
|
||||
test_setup (void)
|
||||
{
|
||||
void *volatile ptr = malloc (16);
|
||||
TEST_VERIFY_EXIT (ptr != NULL);
|
||||
free (ptr);
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function tests each of:
|
||||
- malloc (SIZE)
|
||||
- realloc (PTR_FOR_REALLOC, SIZE)
|
||||
- for various values of NMEMB:
|
||||
- calloc (NMEMB, SIZE/NMEMB)
|
||||
- calloc (SIZE/NMEMB, NMEMB)
|
||||
and precedes each of these tests with a small malloc/free before it. */
|
||||
static void
|
||||
test_large_allocations (size_t size)
|
||||
{
|
||||
void * ptr_to_realloc;
|
||||
|
||||
test_setup ();
|
||||
TEST_VERIFY (malloc (size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
ptr_to_realloc = malloc (16);
|
||||
TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
|
||||
test_setup ();
|
||||
TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
free (ptr_to_realloc);
|
||||
|
||||
for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
|
||||
if ((size % nmemb) == 0)
|
||||
{
|
||||
test_setup ();
|
||||
TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
test_setup ();
|
||||
TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
static long pagesize;
|
||||
|
||||
/* This function tests the following aligned memory allocation functions
|
||||
using several valid alignments and precedes each allocation test with a
|
||||
small malloc/free before it:
|
||||
memalign, posix_memalign, aligned_alloc, valloc, pvalloc. */
|
||||
static void
|
||||
test_large_aligned_allocations (size_t size)
|
||||
{
|
||||
/* ptr stores the result of posix_memalign but since all those calls
|
||||
should fail, posix_memalign should never change ptr. We set it to
|
||||
NULL here and later on we check that it remains NULL after each
|
||||
posix_memalign call. */
|
||||
void * ptr = NULL;
|
||||
|
||||
size_t align;
|
||||
|
||||
/* All aligned memory allocation functions expect an alignment that is a
|
||||
power of 2. Given this, we test each of them with every valid
|
||||
alignment from 1 thru PAGESIZE. */
|
||||
for (align = 1; align <= pagesize; align *= 2)
|
||||
{
|
||||
test_setup ();
|
||||
TEST_VERIFY (memalign (align, size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
/* posix_memalign expects an alignment that is a power of 2 *and* a
|
||||
multiple of sizeof (void *). */
|
||||
if ((align % sizeof (void *)) == 0)
|
||||
{
|
||||
test_setup ();
|
||||
TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
|
||||
TEST_VERIFY (ptr == NULL);
|
||||
}
|
||||
|
||||
/* aligned_alloc expects a size that is a multiple of alignment. */
|
||||
if ((size % align) == 0)
|
||||
{
|
||||
test_setup ();
|
||||
TEST_VERIFY (aligned_alloc (align, size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
/* Both valloc and pvalloc return page-aligned memory. */
|
||||
|
||||
test_setup ();
|
||||
TEST_VERIFY (valloc (size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
test_setup ();
|
||||
TEST_VERIFY (pvalloc (size) == NULL);
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
}
|
||||
|
||||
|
||||
#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
|
||||
#define FIFTY_ON_BITS ((1UL << 50) - 1)
|
||||
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
|
||||
#if __WORDSIZE >= 64
|
||||
|
||||
/* This test assumes that none of the supported targets have an address
|
||||
bus wider than 50 bits, and that therefore allocations for sizes wider
|
||||
than 50 bits will fail. Here, we ensure that the assumption continues
|
||||
to be true in the future when we might have address buses wider than 50
|
||||
bits. */
|
||||
|
||||
struct rlimit alloc_size_limit
|
||||
= {
|
||||
.rlim_cur = FIFTY_ON_BITS,
|
||||
.rlim_max = FIFTY_ON_BITS
|
||||
};
|
||||
|
||||
setrlimit (RLIMIT_AS, &alloc_size_limit);
|
||||
|
||||
#endif /* __WORDSIZE >= 64 */
|
||||
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
/* GCC 7 warns about too-large allocations; here we want to test
|
||||
that they fail. */
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
|
||||
/* Aligned memory allocation functions need to be tested up to alignment
|
||||
size equivalent to page size, which should be a power of 2. */
|
||||
pagesize = sysconf (_SC_PAGESIZE);
|
||||
TEST_VERIFY_EXIT (powerof2 (pagesize));
|
||||
|
||||
/* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
|
||||
in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
|
||||
|
||||
We can expect that this range of allocation sizes will always lead to
|
||||
an allocation failure on both 64 and 32 bit targets, because:
|
||||
|
||||
1. no currently supported 64-bit target has an address bus wider than
|
||||
50 bits -- and (2^64 - 2^14) is much wider than that;
|
||||
|
||||
2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
|
||||
addressable, glibc itself is more than 2^14 bytes in size, and
|
||||
therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
|
||||
available. */
|
||||
|
||||
for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
|
||||
{
|
||||
test_large_allocations (SIZE_MAX - i);
|
||||
test_large_aligned_allocations (SIZE_MAX - i);
|
||||
}
|
||||
|
||||
#if __WORDSIZE >= 64
|
||||
/* On 64-bit targets, we need to test a much wider range of too-large
|
||||
sizes, so we test at intervals of (1 << 50) that allocation sizes
|
||||
ranging from SIZE_MAX down to (1 << 50) fail:
|
||||
The 14 MSBs are decremented starting from "all ON" going down to 1,
|
||||
the 50 LSBs are "all ON" and then "all OFF" during every iteration. */
|
||||
for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
|
||||
{
|
||||
size_t size = (msbs << 50) | FIFTY_ON_BITS;
|
||||
test_large_allocations (size);
|
||||
test_large_aligned_allocations (size);
|
||||
|
||||
size = msbs << 50;
|
||||
test_large_allocations (size);
|
||||
test_large_aligned_allocations (size);
|
||||
}
|
||||
#endif /* __WORDSIZE >= 64 */
|
||||
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -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
|
||||
|
||||
+5
-4
@@ -29,14 +29,15 @@
|
||||
|
||||
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_23)
|
||||
|
||||
/* Define the variables used for the interface. */
|
||||
char *loc1;
|
||||
char *loc2;
|
||||
/* Define the variables used for the interface. Avoid .symver on common
|
||||
symbol, which just creates a new common symbol, not an alias. */
|
||||
char *loc1 __attribute__ ((nocommon));
|
||||
char *loc2 __attribute__ ((nocommon));
|
||||
compat_symbol (libc, loc1, loc1, GLIBC_2_0);
|
||||
compat_symbol (libc, loc2, loc2, GLIBC_2_0);
|
||||
|
||||
/* Although we do not support the use we define this variable as well. */
|
||||
char *locs;
|
||||
char *locs __attribute__ ((nocommon));
|
||||
compat_symbol (libc, locs, locs, GLIBC_2_0);
|
||||
|
||||
|
||||
|
||||
+16
-5
@@ -219,11 +219,16 @@ CFLAGS-pt-system.c = -fexceptions
|
||||
LDLIBS-tst-once5 = -lstdc++
|
||||
CFLAGS-tst-thread_local1.o = -std=gnu++11
|
||||
LDLIBS-tst-thread_local1 = -lstdc++
|
||||
CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11
|
||||
LDLIBS-tst-thread-exit-clobber = -lstdc++
|
||||
CFLAGS-tst-minstack-throw.o = -std=gnu++11
|
||||
LDLIBS-tst-minstack-throw = -lstdc++
|
||||
|
||||
tests = tst-typesizes \
|
||||
tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
||||
tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \
|
||||
tst-mutex7 tst-mutex8 tst-mutex9 tst-mutex5a tst-mutex7a \
|
||||
tst-mutex7robust \
|
||||
tst-mutexpi1 tst-mutexpi2 tst-mutexpi3 tst-mutexpi4 tst-mutexpi5 \
|
||||
tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a tst-mutexpi8 \
|
||||
tst-mutexpi9 \
|
||||
@@ -241,7 +246,7 @@ tests = tst-typesizes \
|
||||
tst-rwlock4 tst-rwlock5 tst-rwlock6 tst-rwlock7 tst-rwlock8 \
|
||||
tst-rwlock9 tst-rwlock10 tst-rwlock11 tst-rwlock12 tst-rwlock13 \
|
||||
tst-rwlock14 tst-rwlock15 tst-rwlock16 tst-rwlock17 tst-rwlock18 \
|
||||
tst-rwlock19 \
|
||||
tst-rwlock19 tst-rwlock20 \
|
||||
tst-once1 tst-once2 tst-once3 tst-once4 tst-once5 \
|
||||
tst-key1 tst-key2 tst-key3 tst-key4 \
|
||||
tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 tst-sem6 tst-sem7 \
|
||||
@@ -296,7 +301,10 @@ tests = tst-typesizes \
|
||||
c89 gnu89 c99 gnu99 c11 gnu11) \
|
||||
tst-bad-schedattr \
|
||||
tst-thread_local1 tst-mutex-errorcheck tst-robust10 \
|
||||
tst-robust-fork tst-create-detached
|
||||
tst-robust-fork tst-create-detached \
|
||||
tst-thread-exit-clobber tst-minstack-cancel tst-minstack-exit \
|
||||
tst-minstack-throw
|
||||
|
||||
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
|
||||
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
|
||||
test-srcs = tst-oddstacklimit
|
||||
@@ -355,7 +363,7 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
|
||||
tst-oncex3 tst-oncex4
|
||||
ifeq ($(build-shared),yes)
|
||||
tests += tst-atfork2 tst-tls3 tst-tls3-malloc tst-tls4 tst-tls5 tst-_res1 \
|
||||
tst-fini1 tst-stackguard1
|
||||
tst-fini1 tst-stackguard1 tst-compat-forwarder
|
||||
tests-nolibpthread += tst-fini1
|
||||
ifeq ($(have-z-execstack),yes)
|
||||
tests += tst-execstack
|
||||
@@ -366,7 +374,7 @@ modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
|
||||
tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
|
||||
tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
|
||||
tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
|
||||
tst-join7mod
|
||||
tst-join7mod tst-compat-forwarder-mod
|
||||
extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
|
||||
tst-cleanup4aux.o tst-cleanupx4aux.o
|
||||
test-extras += $(modules-names) tst-cleanup4aux tst-cleanupx4aux
|
||||
@@ -440,7 +448,8 @@ endif
|
||||
|
||||
ifeq (,$(CXX))
|
||||
# These tests require a C++ compiler and runtime.
|
||||
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5
|
||||
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5 \
|
||||
tst-thread-exit-clobber tst-minstack-throw
|
||||
endif
|
||||
# These tests require a C++ compiler and runtime with thread_local support.
|
||||
ifneq ($(have-cxx-thread_local),yes)
|
||||
@@ -704,6 +713,8 @@ $(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1
|
||||
$(evaluate-test)
|
||||
endif
|
||||
|
||||
$(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so
|
||||
|
||||
# The tests here better do not run in parallel
|
||||
ifneq ($(filter %tests,$(MAKECMDGOALS)),)
|
||||
.NOTPARALLEL:
|
||||
|
||||
+12
-2
@@ -482,6 +482,10 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
/* Make sure the size of the stack is enough for the guard and
|
||||
eventually the thread descriptor. */
|
||||
guardsize = (attr->guardsize + pagesize_m1) & ~pagesize_m1;
|
||||
if (guardsize < attr->guardsize || size + guardsize < guardsize)
|
||||
/* Arithmetic overflow. */
|
||||
return EINVAL;
|
||||
size += guardsize;
|
||||
if (__builtin_expect (size < ((guardsize + __static_tls_size
|
||||
+ MINIMAL_REST_STACK + pagesize_m1)
|
||||
& ~pagesize_m1),
|
||||
@@ -683,8 +687,14 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
prot) != 0)
|
||||
goto mprot_error;
|
||||
#elif _STACK_GROWS_UP
|
||||
if (mprotect ((char *) pd - pd->guardsize,
|
||||
pd->guardsize - guardsize, prot) != 0)
|
||||
char *new_guard = (char *)(((uintptr_t) pd - guardsize)
|
||||
& ~pagesize_m1);
|
||||
char *old_guard = (char *)(((uintptr_t) pd - pd->guardsize)
|
||||
& ~pagesize_m1);
|
||||
/* The guard size difference might be > 0, but once rounded
|
||||
to the nearest page the size difference might be zero. */
|
||||
if (new_guard > old_guard
|
||||
&& mprotect (old_guard, new_guard - old_guard, prot) != 0)
|
||||
goto mprot_error;
|
||||
#endif
|
||||
|
||||
|
||||
+2
-2
@@ -382,9 +382,9 @@ struct pthread
|
||||
/* Machine-specific unwind info. */
|
||||
struct _Unwind_Exception exc;
|
||||
|
||||
/* If nonzero pointer to area allocated for the stack and its
|
||||
size. */
|
||||
/* If nonzero, pointer to the area allocated for the stack and guard. */
|
||||
void *stackblock;
|
||||
/* Size of the stackblock area including the guard. */
|
||||
size_t stackblock_size;
|
||||
/* Size of the included guard area. */
|
||||
size_t guardsize;
|
||||
|
||||
+1
-4
@@ -473,8 +473,5 @@ strong_alias (__pthread_initialize_minimal_internal,
|
||||
size_t
|
||||
__pthread_get_minstack (const pthread_attr_t *attr)
|
||||
{
|
||||
struct pthread_attr *iattr = (struct pthread_attr *) attr;
|
||||
|
||||
return (GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN
|
||||
+ iattr->guardsize);
|
||||
return GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN;
|
||||
}
|
||||
|
||||
+10
-21
@@ -25,36 +25,25 @@
|
||||
symbol in libpthread, but the historical ABI requires it. For static
|
||||
linking, there is no need to provide anything here--the libc version
|
||||
will be linked in. For shared library ABI compatibility, there must be
|
||||
longjmp and siglongjmp symbols in libpthread.so; so we define them using
|
||||
IFUNC to redirect to the libc function. */
|
||||
longjmp and siglongjmp symbols in libpthread.so.
|
||||
|
||||
With an IFUNC resolver, it would be possible to avoid the indirection,
|
||||
but the IFUNC resolver might run before the __libc_longjmp symbol has
|
||||
been relocated, in which case the IFUNC resolver would not be able to
|
||||
provide the correct address. */
|
||||
|
||||
#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22)
|
||||
|
||||
# if HAVE_IFUNC
|
||||
|
||||
# undef INIT_ARCH
|
||||
# define INIT_ARCH()
|
||||
# define DEFINE_LONGJMP(name) libc_ifunc (name, &__libc_longjmp)
|
||||
|
||||
extern __typeof(longjmp) longjmp_ifunc;
|
||||
extern __typeof(siglongjmp) siglongjmp_ifunc;
|
||||
|
||||
# else /* !HAVE_IFUNC */
|
||||
|
||||
static void __attribute__ ((noreturn, used))
|
||||
longjmp_compat (jmp_buf env, int val)
|
||||
{
|
||||
__libc_longjmp (env, val);
|
||||
}
|
||||
|
||||
# define DEFINE_LONGJMP(name) strong_alias (longjmp_compat, name)
|
||||
strong_alias (longjmp_compat, longjmp_alias)
|
||||
compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0);
|
||||
|
||||
# endif /* HAVE_IFUNC */
|
||||
|
||||
DEFINE_LONGJMP (longjmp_ifunc)
|
||||
compat_symbol (libpthread, longjmp_ifunc, longjmp, GLIBC_2_0);
|
||||
|
||||
strong_alias (longjmp_ifunc, siglongjmp_ifunc)
|
||||
compat_symbol (libpthread, siglongjmp_ifunc, siglongjmp, GLIBC_2_0);
|
||||
strong_alias (longjmp_alias, siglongjmp_alias)
|
||||
compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0);
|
||||
|
||||
#endif
|
||||
|
||||
+8
-16
@@ -25,29 +25,21 @@
|
||||
libpthread, but the historical ABI requires it. For static linking,
|
||||
there is no need to provide anything here--the libc version will be
|
||||
linked in. For shared library ABI compatibility, there must be a
|
||||
'system' symbol in libpthread.so; so we define it using IFUNC to
|
||||
redirect to the libc function. */
|
||||
'system' symbol in libpthread.so.
|
||||
|
||||
With an IFUNC resolver, it would be possible to avoid the indirection,
|
||||
but the IFUNC resolver might run before the __libc_system symbol has
|
||||
been relocated, in which case the IFUNC resolver would not be able to
|
||||
provide the correct address. */
|
||||
|
||||
#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22)
|
||||
|
||||
# if HAVE_IFUNC
|
||||
|
||||
extern __typeof(system) system_ifunc;
|
||||
# undef INIT_ARCH
|
||||
# define INIT_ARCH()
|
||||
libc_ifunc (system_ifunc, &__libc_system)
|
||||
|
||||
# else /* !HAVE_IFUNC */
|
||||
|
||||
static int __attribute__ ((used))
|
||||
system_compat (const char *line)
|
||||
{
|
||||
return __libc_system (line);
|
||||
}
|
||||
strong_alias (system_compat, system_ifunc)
|
||||
|
||||
# endif /* HAVE_IFUNC */
|
||||
|
||||
compat_symbol (libpthread, system_ifunc, system, GLIBC_2_0);
|
||||
strong_alias (system_compat, system_alias)
|
||||
compat_symbol (libpthread, system_alias, system, GLIBC_2_0);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -406,8 +406,12 @@ __condvar_quiesce_and_switch_g1 (pthread_cond_t *cond, uint64_t wseq,
|
||||
{
|
||||
/* There is still a waiter after spinning. Set the wake-request
|
||||
flag and block. Relaxed MO is fine because this is just about
|
||||
this futex word. */
|
||||
r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1);
|
||||
this futex word.
|
||||
|
||||
Update r to include the set wake-request flag so that the upcoming
|
||||
futex_wait only blocks if the flag is still set (otherwise, we'd
|
||||
violate the basic client-side futex protocol). */
|
||||
r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1) | 1;
|
||||
|
||||
if ((r >> 1) > 0)
|
||||
futex_wait_simple (cond->__data.__g_refs + g1, r, private);
|
||||
|
||||
@@ -57,9 +57,12 @@ pthread_getattr_np (pthread_t thread_id, pthread_attr_t *attr)
|
||||
/* The sizes are subject to alignment. */
|
||||
if (__glibc_likely (thread->stackblock != NULL))
|
||||
{
|
||||
iattr->stacksize = thread->stackblock_size;
|
||||
/* The stack size reported to the user should not include the
|
||||
guard size. */
|
||||
iattr->stacksize = thread->stackblock_size - thread->guardsize;
|
||||
#if _STACK_GROWS_DOWN
|
||||
iattr->stackaddr = (char *) thread->stackblock + iattr->stacksize;
|
||||
iattr->stackaddr = (char *) thread->stackblock
|
||||
+ thread->stackblock_size;
|
||||
#else
|
||||
iattr->stackaddr = (char *) thread->stackblock;
|
||||
#endif
|
||||
|
||||
@@ -197,11 +197,14 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
|
||||
{
|
||||
/* Try to acquire the lock through a CAS from 0 (not acquired) to
|
||||
our TID | assume_other_futex_waiters. */
|
||||
if (__glibc_likely ((oldval == 0)
|
||||
&& (atomic_compare_and_exchange_bool_acq
|
||||
(&mutex->__data.__lock,
|
||||
id | assume_other_futex_waiters, 0) == 0)))
|
||||
break;
|
||||
if (__glibc_likely (oldval == 0))
|
||||
{
|
||||
oldval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
id | assume_other_futex_waiters, 0);
|
||||
if (__glibc_likely (oldval == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
if ((oldval & FUTEX_OWNER_DIED) != 0)
|
||||
{
|
||||
|
||||
@@ -154,11 +154,14 @@ pthread_mutex_timedlock (pthread_mutex_t *mutex,
|
||||
{
|
||||
/* Try to acquire the lock through a CAS from 0 (not acquired) to
|
||||
our TID | assume_other_futex_waiters. */
|
||||
if (__glibc_likely ((oldval == 0)
|
||||
&& (atomic_compare_and_exchange_bool_acq
|
||||
(&mutex->__data.__lock,
|
||||
id | assume_other_futex_waiters, 0) == 0)))
|
||||
break;
|
||||
if (__glibc_likely (oldval == 0))
|
||||
{
|
||||
oldval
|
||||
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
|
||||
id | assume_other_futex_waiters, 0);
|
||||
if (__glibc_likely (oldval == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
if ((oldval & FUTEX_OWNER_DIED) != 0)
|
||||
{
|
||||
|
||||
@@ -92,6 +92,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
&mutex->__data.__list.__next);
|
||||
/* We need to set op_pending before starting the operation. Also
|
||||
see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
do
|
||||
@@ -117,7 +120,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exist here. If we fall
|
||||
@@ -133,6 +141,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
int kind = PTHREAD_MUTEX_TYPE (mutex);
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. Also see comments at ENQUEUE_MUTEX. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
return EDEADLK;
|
||||
@@ -140,6 +150,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
|
||||
@@ -158,6 +170,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
id, 0);
|
||||
if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
|
||||
{
|
||||
/* We haven't acquired the lock as it is already acquired by
|
||||
another owner. We do not need to ensure ordering wrt another
|
||||
memory access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -171,13 +186,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
if (oldval == id)
|
||||
lll_unlock (mutex->__data.__lock,
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
|
||||
/* FIXME This violates the mutex destruction requirements. See
|
||||
__pthread_mutex_unlock_full. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
}
|
||||
while ((oldval & FUTEX_OWNER_DIED) != 0);
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
mutex->__data.__owner = id;
|
||||
@@ -203,10 +225,15 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
|
||||
if (robust)
|
||||
/* Note: robust PI futexes are signaled by setting bit 0. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
(void *) (((uintptr_t) &mutex->__data.__list.__next)
|
||||
| 1));
|
||||
{
|
||||
/* Note: robust PI futexes are signaled by setting bit 0. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
(void *) (((uintptr_t) &mutex->__data.__list.__next)
|
||||
| 1));
|
||||
/* We need to set op_pending before starting the operation. Also
|
||||
see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
}
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
@@ -215,12 +242,16 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
{
|
||||
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Just bump the counter. */
|
||||
@@ -242,6 +273,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
{
|
||||
if ((oldval & FUTEX_OWNER_DIED) == 0)
|
||||
{
|
||||
/* We haven't acquired the lock as it is already acquired by
|
||||
another owner. We do not need to ensure ordering wrt another
|
||||
memory access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -262,6 +296,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
if (INTERNAL_SYSCALL_ERROR_P (e, __err)
|
||||
&& INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK)
|
||||
{
|
||||
/* The kernel has not yet finished the mutex owner death.
|
||||
We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -279,7 +316,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exit here. If we fall
|
||||
@@ -302,13 +344,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
|
||||
0, 0);
|
||||
|
||||
/* To the kernel, this will be visible after the kernel has
|
||||
acquired the mutex in the syscall. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
|
||||
if (robust)
|
||||
{
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX_PI (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
}
|
||||
|
||||
|
||||
+242
-240
@@ -55,7 +55,6 @@
|
||||
lock acquisition attempts, so that new incoming readers do not prolong a
|
||||
phase in which readers have acquired the lock.
|
||||
|
||||
|
||||
The main components of the rwlock are a writer-only lock that allows only
|
||||
one of the concurrent writers to be the primary writer, and a
|
||||
single-writer-multiple-readers lock that decides between read phases, in
|
||||
@@ -70,15 +69,16 @@
|
||||
---------------------------
|
||||
#1 0 0 0 0 Lock is idle (and in a read phase).
|
||||
#2 0 0 >0 0 Readers have acquired the lock.
|
||||
#3 0 1 0 0 Lock is not acquired; a writer is waiting for a write
|
||||
phase to start or will try to start one.
|
||||
#3 0 1 0 0 Lock is not acquired; a writer will try to start a
|
||||
write phase.
|
||||
#4 0 1 >0 0 Readers have acquired the lock; a writer is waiting
|
||||
and explicit hand-over to the writer is required.
|
||||
#4a 0 1 >0 1 Same as #4 except that there are further readers
|
||||
waiting because the writer is to be preferred.
|
||||
#5 1 0 0 0 Lock is idle (and in a write phase).
|
||||
#6 1 0 >0 0 Write phase; readers are waiting for a read phase to
|
||||
start or will try to start one.
|
||||
#6 1 0 >0 0 Write phase; readers will try to start a read phase
|
||||
(requires explicit hand-over to all readers that
|
||||
do not start the read phase).
|
||||
#7 1 1 0 0 Lock is acquired by a writer.
|
||||
#8 1 1 >0 0 Lock acquired by a writer and readers are waiting;
|
||||
explicit hand-over to the readers is required.
|
||||
@@ -375,9 +375,9 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
|
||||
complexity. */
|
||||
if (__glibc_likely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
|
||||
return 0;
|
||||
|
||||
/* If there is no primary writer but we are in a write phase, we can try
|
||||
to install a read phase ourself. */
|
||||
/* Otherwise, if we were in a write phase (states #6 or #8), we must wait
|
||||
for explicit hand-over of the read phase; the only exception is if we
|
||||
can start a read phase if there is no primary writer currently. */
|
||||
while (((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
&& ((r & PTHREAD_RWLOCK_WRLOCKED) == 0))
|
||||
{
|
||||
@@ -390,15 +390,18 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
|
||||
{
|
||||
/* We started the read phase, so we are also responsible for
|
||||
updating the write-phase futex. Relaxed MO is sufficient.
|
||||
Note that there can be no other reader that we have to wake
|
||||
because all other readers will see the read phase started by us
|
||||
(or they will try to start it themselves); if a writer started
|
||||
the read phase, we cannot have started it. Furthermore, we
|
||||
cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
|
||||
overwrite the value set by the most recent writer (or the readers
|
||||
before it in case of explicit hand-over) and we know that there
|
||||
are no waiting readers. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
We have to do the same steps as a writer would when handing
|
||||
over the read phase to us because other readers cannot
|
||||
distinguish between us and the writer; this includes
|
||||
explicit hand-over and potentially having to wake other readers
|
||||
(but we can pretend to do the setting and unsetting of WRLOCKED
|
||||
atomically, and thus can skip this step). */
|
||||
if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||
& PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -407,102 +410,98 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
|
||||
}
|
||||
}
|
||||
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
/* We were in a write phase but did not install the read phase. We cannot
|
||||
distinguish between a writer and another reader starting the read phase,
|
||||
so we must wait for explicit hand-over via __wrphase_futex.
|
||||
However, __wrphase_futex might not have been set to 1 yet (either
|
||||
because explicit hand-over to the writer is still ongoing, or because
|
||||
the writer has started the write phase but has not yet updated
|
||||
__wrphase_futex). The least recent value of __wrphase_futex we can
|
||||
read from here is the modification of the last read phase (because
|
||||
we synchronize with the last reader in this read phase through
|
||||
__readers; see the use of acquire MO on the fetch_add above).
|
||||
Therefore, if we observe a value of 0 for __wrphase_futex, we need
|
||||
to subsequently check that __readers now indicates a read phase; we
|
||||
need to use acquire MO for this so that if we observe a read phase,
|
||||
we will also see the modification of __wrphase_futex by the previous
|
||||
writer. We then need to load __wrphase_futex again and continue to
|
||||
wait if it is not 0, so that we do not skip explicit hand-over.
|
||||
Relaxed MO is sufficient for the load from __wrphase_futex because
|
||||
we just use it as an indicator for when we can proceed; we use
|
||||
__readers and the acquire MO accesses to it to eventually read from
|
||||
the proper stores to __wrphase_futex. */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
{
|
||||
/* We are in a write phase, and there must be a primary writer because
|
||||
of the previous loop. Block until the primary writer gives up the
|
||||
write phase. This case requires explicit hand-over using
|
||||
__wrphase_futex.
|
||||
However, __wrphase_futex might not have been set to 1 yet (either
|
||||
because explicit hand-over to the writer is still ongoing, or because
|
||||
the writer has started the write phase but does not yet have updated
|
||||
__wrphase_futex). The least recent value of __wrphase_futex we can
|
||||
read from here is the modification of the last read phase (because
|
||||
we synchronize with the last reader in this read phase through
|
||||
__readers; see the use of acquire MO on the fetch_add above).
|
||||
Therefore, if we observe a value of 0 for __wrphase_futex, we need
|
||||
to subsequently check that __readers now indicates a read phase; we
|
||||
need to use acquire MO for this so that if we observe a read phase,
|
||||
we will also see the modification of __wrphase_futex by the previous
|
||||
writer. We then need to load __wrphase_futex again and continue to
|
||||
wait if it is not 0, so that we do not skip explicit hand-over.
|
||||
Relaxed MO is sufficient for the load from __wrphase_futex because
|
||||
we just use it as an indicator for when we can proceed; we use
|
||||
__readers and the acquire MO accesses to it to eventually read from
|
||||
the proper stores to __wrphase_futex. */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
{
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex,
|
||||
&wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex,
|
||||
&wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
/* If we timed out, we need to unregister. If no read phase
|
||||
has been installed while we waited, we can just decrement
|
||||
the number of readers. Otherwise, we just acquire the
|
||||
lock, which is allowed because we give no precise timing
|
||||
guarantees, and because the timeout is only required to
|
||||
be in effect if we would have had to wait for other
|
||||
threads (e.g., if futex_wait would time-out immediately
|
||||
because the given absolute time is in the past). */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
{
|
||||
/* If we timed out, we need to unregister. If no read phase
|
||||
has been installed while we waited, we can just decrement
|
||||
the number of readers. Otherwise, we just acquire the
|
||||
lock, which is allowed because we give no precise timing
|
||||
guarantees, and because the timeout is only required to
|
||||
be in effect if we would have had to wait for other
|
||||
threads (e.g., if futex_wait would time-out immediately
|
||||
because the given absolute time is in the past). */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
{
|
||||
/* We don't need to make anything else visible to
|
||||
others besides unregistering, so relaxed MO is
|
||||
sufficient. */
|
||||
if (atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__readers, &r,
|
||||
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
|
||||
return ETIMEDOUT;
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore because we would just time out
|
||||
in this case and thus make the spin-waiting we need
|
||||
unnecessarily expensive. */
|
||||
while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
{
|
||||
/* TODO Back-off? */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
/* We don't need to make anything else visible to
|
||||
others besides unregistering, so relaxed MO is
|
||||
sufficient. */
|
||||
if (atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__readers, &r,
|
||||
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
|
||||
return ETIMEDOUT;
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* If we got interrupted (EINTR) or the futex word does not have the
|
||||
expected value (EAGAIN), retry. */
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore because we would just time out
|
||||
in this case and thus make the spin-waiting we need
|
||||
unnecessarily expensive. */
|
||||
while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
{
|
||||
/* TODO Back-off? */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
if (ready)
|
||||
/* See below. */
|
||||
break;
|
||||
/* We need acquire MO here so that we synchronize with the lock
|
||||
release of the writer, and so that we observe a recent value of
|
||||
__wrphase_futex (see below). */
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
/* We are in a read phase now, so the least recent modification of
|
||||
__wrphase_futex we can read from is the store by the writer
|
||||
with value 1. Thus, only now we can assume that if we observe
|
||||
a value of 0, explicit hand-over is finished. Retry the loop
|
||||
above one more time. */
|
||||
ready = true;
|
||||
/* If we got interrupted (EINTR) or the futex word does not have the
|
||||
expected value (EAGAIN), retry. */
|
||||
}
|
||||
if (ready)
|
||||
/* See below. */
|
||||
break;
|
||||
/* We need acquire MO here so that we synchronize with the lock
|
||||
release of the writer, and so that we observe a recent value of
|
||||
__wrphase_futex (see below). */
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
/* We are in a read phase now, so the least recent modification of
|
||||
__wrphase_futex we can read from is the store by the writer
|
||||
with value 1. Thus, only now we can assume that if we observe
|
||||
a value of 0, explicit hand-over is finished. Retry the loop
|
||||
above one more time. */
|
||||
ready = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -741,10 +740,23 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
}
|
||||
/* Our snapshot of __readers is up-to-date at this point because we
|
||||
either set WRLOCKED using a CAS or were handed over WRLOCKED from
|
||||
either set WRLOCKED using a CAS (and update r accordingly below,
|
||||
which was used as expected value for the CAS) or got WRLOCKED from
|
||||
another writer whose snapshot of __readers we inherit. */
|
||||
r |= PTHREAD_RWLOCK_WRLOCKED;
|
||||
}
|
||||
|
||||
/* We are the primary writer; enable blocking on __writers_futex. Relaxed
|
||||
MO is sufficient for futex words; acquire MO on the previous
|
||||
modifications of __readers ensures that this store happens after the
|
||||
store of value 0 by the previous primary writer. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex,
|
||||
1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
|
||||
|
||||
/* If we are in a write phase, we have acquired the lock. */
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
goto done;
|
||||
|
||||
/* If we are in a read phase and there are no readers, try to start a write
|
||||
phase. */
|
||||
while (((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
@@ -758,166 +770,156 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
|
||||
&r, r | PTHREAD_RWLOCK_WRPHASE))
|
||||
{
|
||||
/* We have started a write phase, so need to enable readers to wait.
|
||||
See the similar case in__pthread_rwlock_rdlock_full. */
|
||||
See the similar case in __pthread_rwlock_rdlock_full. Unlike in
|
||||
that similar case, we are the (only) primary writer and so do
|
||||
not need to wake another writer. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
/* Make sure we fall through to the end of the function. */
|
||||
r |= PTHREAD_RWLOCK_WRPHASE;
|
||||
break;
|
||||
|
||||
goto done;
|
||||
}
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
|
||||
/* We are the primary writer; enable blocking on __writers_futex. Relaxed
|
||||
MO is sufficient for futex words; acquire MO on the previous
|
||||
modifications of __readers ensures that this store happens after the
|
||||
store of value 0 by the previous primary writer. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex,
|
||||
1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
|
||||
|
||||
if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
|
||||
/* We became the primary writer in a read phase and there were readers when
|
||||
we did (because of the previous loop). Thus, we have to wait for
|
||||
explicit hand-over from one of these readers.
|
||||
We basically do the same steps as for the similar case in
|
||||
__pthread_rwlock_rdlock_full, except that we additionally might try
|
||||
to directly hand over to another writer and need to wake up
|
||||
other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
{
|
||||
/* We are not in a read phase and there are readers (because of the
|
||||
previous loop). Thus, we have to wait for explicit hand-over from
|
||||
one of these readers.
|
||||
We basically do the same steps as for the similar case in
|
||||
__pthread_rwlock_rdlock_full, except that we additionally might try
|
||||
to directly hand over to another writer and need to wake up
|
||||
other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */
|
||||
unsigned int wpf;
|
||||
bool ready = false;
|
||||
for (;;)
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
|
||||
{
|
||||
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
| PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex, &wpf,
|
||||
PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
&& !atomic_compare_exchange_weak_relaxed
|
||||
(&rwlock->__data.__wrphase_futex, &wpf,
|
||||
PTHREAD_RWLOCK_FUTEX_USED))
|
||||
continue;
|
||||
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
|
||||
if (err == ETIMEDOUT)
|
||||
if (rwlock->__data.__flags
|
||||
!= PTHREAD_RWLOCK_PREFER_READER_NP)
|
||||
{
|
||||
if (rwlock->__data.__flags
|
||||
!= PTHREAD_RWLOCK_PREFER_READER_NP)
|
||||
/* We try writer--writer hand-over. */
|
||||
unsigned int w = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers);
|
||||
if (w != 0)
|
||||
{
|
||||
/* We try writer--writer hand-over. */
|
||||
unsigned int w = atomic_load_relaxed
|
||||
(&rwlock->__data.__writers);
|
||||
if (w != 0)
|
||||
{
|
||||
/* We are about to hand over WRLOCKED, so we must
|
||||
release __writers_futex too; otherwise, we'd have
|
||||
a pending store, which could at least prevent
|
||||
other threads from waiting using the futex
|
||||
because it could interleave with the stores
|
||||
by subsequent writers. In turn, this means that
|
||||
we have to clean up when we do not hand over
|
||||
WRLOCKED.
|
||||
Release MO so that another writer that gets
|
||||
WRLOCKED from us can take over our view of
|
||||
__readers. */
|
||||
unsigned int wf = atomic_exchange_relaxed
|
||||
(&rwlock->__data.__writers_futex, 0);
|
||||
while (w != 0)
|
||||
{
|
||||
if (atomic_compare_exchange_weak_release
|
||||
(&rwlock->__data.__writers, &w,
|
||||
w | PTHREAD_RWLOCK_WRHANDOVER))
|
||||
{
|
||||
/* Wake other writers. */
|
||||
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
futex_wake
|
||||
(&rwlock->__data.__writers_futex, 1,
|
||||
private);
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* We still own WRLOCKED and someone else might set
|
||||
a write phase concurrently, so enable waiting
|
||||
again. Make sure we don't loose the flag that
|
||||
signals whether there are threads waiting on
|
||||
this futex. */
|
||||
atomic_store_relaxed
|
||||
(&rwlock->__data.__writers_futex, wf);
|
||||
}
|
||||
}
|
||||
/* If we timed out and we are not in a write phase, we can
|
||||
just stop being a primary writer. Otherwise, we just
|
||||
acquire the lock. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
{
|
||||
/* We are about to release WRLOCKED, so we must release
|
||||
__writers_futex too; see the handling of
|
||||
writer--writer hand-over above. */
|
||||
/* We are about to hand over WRLOCKED, so we must
|
||||
release __writers_futex too; otherwise, we'd have
|
||||
a pending store, which could at least prevent
|
||||
other threads from waiting using the futex
|
||||
because it could interleave with the stores
|
||||
by subsequent writers. In turn, this means that
|
||||
we have to clean up when we do not hand over
|
||||
WRLOCKED.
|
||||
Release MO so that another writer that gets
|
||||
WRLOCKED from us can take over our view of
|
||||
__readers. */
|
||||
unsigned int wf = atomic_exchange_relaxed
|
||||
(&rwlock->__data.__writers_futex, 0);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
while (w != 0)
|
||||
{
|
||||
/* While we don't need to make anything from a
|
||||
caller's critical section visible to other
|
||||
threads, we need to ensure that our changes to
|
||||
__writers_futex are properly ordered.
|
||||
Therefore, use release MO to synchronize with
|
||||
subsequent primary writers. Also wake up any
|
||||
waiting readers as they are waiting because of
|
||||
us. */
|
||||
if (atomic_compare_exchange_weak_release
|
||||
(&rwlock->__data.__readers, &r,
|
||||
(r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
& ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
|
||||
(&rwlock->__data.__writers, &w,
|
||||
w | PTHREAD_RWLOCK_WRHANDOVER))
|
||||
{
|
||||
/* Wake other writers. */
|
||||
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
futex_wake (&rwlock->__data.__writers_futex,
|
||||
1, private);
|
||||
/* Wake waiting readers. */
|
||||
if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
futex_wake (&rwlock->__data.__readers,
|
||||
INT_MAX, private);
|
||||
1, private);
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
/* We still own WRLOCKED and someone else might set a
|
||||
write phase concurrently, so enable waiting again.
|
||||
Make sure we don't loose the flag that signals
|
||||
whether there are threads waiting on this futex. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex,
|
||||
wf);
|
||||
/* We still own WRLOCKED and someone else might set
|
||||
a write phase concurrently, so enable waiting
|
||||
again. Make sure we don't loose the flag that
|
||||
signals whether there are threads waiting on
|
||||
this futex. */
|
||||
atomic_store_relaxed
|
||||
(&rwlock->__data.__writers_futex, wf);
|
||||
}
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore. */
|
||||
while ((atomic_load_relaxed
|
||||
(&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== PTHREAD_RWLOCK_FUTEX_USED)
|
||||
{
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
/* If we got interrupted (EINTR) or the futex word does not have
|
||||
the expected value (EAGAIN), retry. */
|
||||
/* If we timed out and we are not in a write phase, we can
|
||||
just stop being a primary writer. Otherwise, we just
|
||||
acquire the lock. */
|
||||
r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
{
|
||||
/* We are about to release WRLOCKED, so we must release
|
||||
__writers_futex too; see the handling of
|
||||
writer--writer hand-over above. */
|
||||
unsigned int wf = atomic_exchange_relaxed
|
||||
(&rwlock->__data.__writers_futex, 0);
|
||||
while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
{
|
||||
/* While we don't need to make anything from a
|
||||
caller's critical section visible to other
|
||||
threads, we need to ensure that our changes to
|
||||
__writers_futex are properly ordered.
|
||||
Therefore, use release MO to synchronize with
|
||||
subsequent primary writers. Also wake up any
|
||||
waiting readers as they are waiting because of
|
||||
us. */
|
||||
if (atomic_compare_exchange_weak_release
|
||||
(&rwlock->__data.__readers, &r,
|
||||
(r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
& ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
|
||||
{
|
||||
/* Wake other writers. */
|
||||
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
futex_wake (&rwlock->__data.__writers_futex,
|
||||
1, private);
|
||||
/* Wake waiting readers. */
|
||||
if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
futex_wake (&rwlock->__data.__readers,
|
||||
INT_MAX, private);
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
/* We still own WRLOCKED and someone else might set a
|
||||
write phase concurrently, so enable waiting again.
|
||||
Make sure we don't loose the flag that signals
|
||||
whether there are threads waiting on this futex. */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex, wf);
|
||||
}
|
||||
/* Use the acquire MO fence to mirror the steps taken in the
|
||||
non-timeout case. Note that the read can happen both
|
||||
in the atomic_load above as well as in the failure case
|
||||
of the CAS operation. */
|
||||
atomic_thread_fence_acquire ();
|
||||
/* We still need to wait for explicit hand-over, but we must
|
||||
not use futex_wait anymore. */
|
||||
while ((atomic_load_relaxed
|
||||
(&rwlock->__data.__wrphase_futex)
|
||||
| PTHREAD_RWLOCK_FUTEX_USED)
|
||||
== PTHREAD_RWLOCK_FUTEX_USED)
|
||||
{
|
||||
/* TODO Back-off. */
|
||||
}
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
/* See pthread_rwlock_rdlock_full. */
|
||||
if (ready)
|
||||
break;
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
ready = true;
|
||||
/* If we got interrupted (EINTR) or the futex word does not have
|
||||
the expected value (EAGAIN), retry. */
|
||||
}
|
||||
/* See pthread_rwlock_rdlock_full. */
|
||||
if (ready)
|
||||
break;
|
||||
if ((atomic_load_acquire (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
ready = true;
|
||||
}
|
||||
|
||||
done:
|
||||
atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
THREAD_GETMEM (THREAD_SELF, tid));
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Call the function system through a statically initialized pointer. */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int (*system_function) (const char *) = system;
|
||||
|
||||
void
|
||||
call_system (void)
|
||||
{
|
||||
system_function (NULL);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Test that the compat forwaders in libpthread work correctly. */
|
||||
|
||||
#include <support/test-driver.h>
|
||||
|
||||
extern void call_system (void);
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
/* Calling the system function from a shared library that is not linked
|
||||
against libpthread, when the main program is linked against
|
||||
libpthread, should not crash. */
|
||||
call_system ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Test cancellation with a minimal stack size.
|
||||
Copyright (C) 2018 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Note: This test is similar to tst-minstack-exit, but is separate to
|
||||
avoid spurious test passes due to warm-up effects. */
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
static void *
|
||||
threadfunc (void *closure)
|
||||
{
|
||||
while (1)
|
||||
pause ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
|
||||
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
|
||||
xpthread_cancel (thr);
|
||||
TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED);
|
||||
xpthread_attr_destroy (&attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Test that pthread_exit works with the minimum stack size.
|
||||
Copyright (C) 2018 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Note: This test is similar to tst-minstack-cancel, but is separate
|
||||
to avoid spurious test passes due to warm-up effects. */
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
static void *
|
||||
threadfunc (void *closure)
|
||||
{
|
||||
pthread_exit (threadfunc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
|
||||
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
|
||||
TEST_VERIFY (xpthread_join (thr) == threadfunc);
|
||||
xpthread_attr_destroy (&attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,87 @@
|
||||
/* Test that throwing C++ exceptions works with the minimum stack size.
|
||||
Copyright (C) 2018 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
/* Throw a std::runtime_exception. */
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
do_throw_exception ()
|
||||
{
|
||||
throw std::runtime_error ("test exception");
|
||||
}
|
||||
|
||||
/* Class with a destructor, to trigger unwind handling. */
|
||||
struct class_with_destructor
|
||||
{
|
||||
class_with_destructor ();
|
||||
~class_with_destructor ();
|
||||
};
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
class_with_destructor::class_with_destructor ()
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
class_with_destructor::~class_with_destructor ()
|
||||
{
|
||||
}
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
function_with_destructed_object ()
|
||||
{
|
||||
class_with_destructor obj;
|
||||
do_throw_exception ();
|
||||
}
|
||||
|
||||
static void *
|
||||
threadfunc (void *closure)
|
||||
{
|
||||
try
|
||||
{
|
||||
function_with_destructed_object ();
|
||||
FAIL_EXIT1 ("no exception thrown");
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
TEST_COMPARE (strcmp (e.what (), "test exception"), 0);
|
||||
return reinterpret_cast<void *> (threadfunc);
|
||||
}
|
||||
FAIL_EXIT1 ("no exception caught");
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
|
||||
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
|
||||
TEST_VERIFY (xpthread_join (thr) == threadfunc);
|
||||
xpthread_attr_destroy (&attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+35
-10
@@ -22,25 +22,41 @@
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* This test is a template for other tests to use. Other tests define
|
||||
the following macros to change the behaviour of the template test.
|
||||
The test is very simple, it configures N threads given the parameters
|
||||
below and then proceeds to go through mutex lock and unlock
|
||||
operations in each thread as described before for the thread
|
||||
function. */
|
||||
#ifndef TYPE
|
||||
# define TYPE PTHREAD_MUTEX_DEFAULT
|
||||
#endif
|
||||
|
||||
#ifndef ROBUST
|
||||
# define ROBUST PTHREAD_MUTEX_STALLED
|
||||
#endif
|
||||
#ifndef DELAY_NSEC
|
||||
# define DELAY_NSEC 11000
|
||||
#endif
|
||||
#ifndef ROUNDS
|
||||
# define ROUNDS 1000
|
||||
#endif
|
||||
#ifndef N
|
||||
# define N 100
|
||||
#endif
|
||||
|
||||
static pthread_mutex_t lock;
|
||||
|
||||
|
||||
#define ROUNDS 1000
|
||||
#define N 100
|
||||
|
||||
|
||||
/* Each thread locks and the subsequently unlocks the lock, yielding
|
||||
the smallest critical section possible. After the unlock the thread
|
||||
waits DELAY_NSEC nanoseconds before doing the lock and unlock again.
|
||||
Every thread does this ROUNDS times. The lock and unlock are
|
||||
checked for errors. */
|
||||
static void *
|
||||
tf (void *arg)
|
||||
{
|
||||
int nr = (long int) arg;
|
||||
int cnt;
|
||||
struct timespec ts = { .tv_sec = 0, .tv_nsec = 11000 };
|
||||
struct timespec ts = { .tv_sec = 0, .tv_nsec = DELAY_NSEC };
|
||||
|
||||
for (cnt = 0; cnt < ROUNDS; ++cnt)
|
||||
{
|
||||
@@ -56,13 +72,16 @@ tf (void *arg)
|
||||
return (void *) 1l;
|
||||
}
|
||||
|
||||
nanosleep (&ts, NULL);
|
||||
if ((ts.tv_sec > 0) || (ts.tv_nsec > 0))
|
||||
nanosleep (&ts, NULL);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Setup and run N threads, where each thread does as described
|
||||
in the above thread function. The threads are given a minimal 1MiB
|
||||
stack since they don't do anything between the lock and unlock. */
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
@@ -80,6 +99,12 @@ do_test (void)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (pthread_mutexattr_setrobust (&a, ROBUST) != 0)
|
||||
{
|
||||
puts ("mutexattr_setrobust failed");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_PI
|
||||
if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT) != 0)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/* Bug 21778: Fix oversight in robust mutex lock acquisition. */
|
||||
#define TYPE PTHREAD_MUTEX_NORMAL
|
||||
#define ROBUST PTHREAD_MUTEX_ROBUST
|
||||
#define DELAY_NSEC 0
|
||||
#define ROUNDS 1000
|
||||
#define N 32
|
||||
#include "tst-mutex7.c"
|
||||
@@ -0,0 +1,116 @@
|
||||
/* Test program for a read-phase / write-phase explicit hand-over.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||
not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <atomic.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
/* We realy want to set threads to 2 to reproduce this issue. The goal
|
||||
is to have one primary writer and a single reader, and to hit the
|
||||
bug that happens in the interleaving of those two phase transitions.
|
||||
However, on most hardware, adding a second writer seems to help the
|
||||
interleaving happen slightly more often, say 20% of the time. On a
|
||||
16 core ppc64 machine this fails 100% of the time with an unpatched
|
||||
glibc. On a 8 core x86_64 machine this fails ~93% of the time, but
|
||||
it doesn't fail at all on a 4 core system, so having available
|
||||
unloaded cores makes a big difference in reproducibility. On an 8
|
||||
core qemu/kvm guest the reproducer reliability drops to ~10%. */
|
||||
#define THREADS 3
|
||||
|
||||
#define KIND PTHREAD_RWLOCK_PREFER_READER_NP
|
||||
|
||||
static pthread_rwlock_t lock;
|
||||
static int done = 0;
|
||||
|
||||
static void*
|
||||
tf (void* arg)
|
||||
{
|
||||
while (atomic_load_relaxed (&done) == 0)
|
||||
{
|
||||
int rcnt = 0;
|
||||
int wcnt = 100;
|
||||
if ((uintptr_t) arg == 0)
|
||||
{
|
||||
rcnt = 1;
|
||||
wcnt = 1;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (wcnt)
|
||||
{
|
||||
xpthread_rwlock_wrlock (&lock);
|
||||
xpthread_rwlock_unlock (&lock);
|
||||
wcnt--;
|
||||
}
|
||||
if (rcnt)
|
||||
{
|
||||
xpthread_rwlock_rdlock (&lock);
|
||||
xpthread_rwlock_unlock (&lock);
|
||||
rcnt--;
|
||||
}
|
||||
}
|
||||
while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0));
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
pthread_t thr[THREADS];
|
||||
int n;
|
||||
pthread_rwlockattr_t attr;
|
||||
|
||||
xpthread_rwlockattr_init (&attr);
|
||||
xpthread_rwlockattr_setkind_np (&attr, KIND);
|
||||
|
||||
xpthread_rwlock_init (&lock, &attr);
|
||||
|
||||
/* Make standard error the same as standard output. */
|
||||
dup2 (1, 2);
|
||||
|
||||
/* Make sure we see all message, even those on stdout. */
|
||||
setvbuf (stdout, NULL, _IONBF, 0);
|
||||
|
||||
for (n = 0; n < THREADS; ++n)
|
||||
thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n);
|
||||
|
||||
struct timespec delay;
|
||||
delay.tv_sec = 10;
|
||||
delay.tv_nsec = 0;
|
||||
nanosleep (&delay, NULL);
|
||||
atomic_store_relaxed (&done, 1);
|
||||
|
||||
/* Wait for all the threads. */
|
||||
for (n = 0; n < THREADS; ++n)
|
||||
xpthread_join (thr[n]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,243 @@
|
||||
/* Test that pthread_exit does not clobber callee-saved registers.
|
||||
Copyright (C) 2018 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
/* This test attempts to check that callee-saved registers are
|
||||
restored to their original values when destructors are run after
|
||||
pthread_exit is called. GCC PR 83641 causes this test to fail.
|
||||
|
||||
The constants have been chosen randomly and are magic values which
|
||||
are used to detect whether registers have been clobbered. The idea
|
||||
is that these values are hidden behind a compiler barrier and only
|
||||
present in .rodata initially, so that it is less likely that they
|
||||
are in a register by accident.
|
||||
|
||||
The checker class can be stored in registers, and the magic values
|
||||
are directly loaded into these registers. The checker destructor
|
||||
is eventually invoked by pthread_exit and calls one of the
|
||||
check_magic functions to verify that the class contents (that is,
|
||||
register value) is correct.
|
||||
|
||||
These tests are performed both for unsigned int and double values,
|
||||
to cover different calling conventions. */
|
||||
|
||||
template <class T>
|
||||
struct values
|
||||
{
|
||||
T v0;
|
||||
T v1;
|
||||
T v2;
|
||||
T v3;
|
||||
T v4;
|
||||
};
|
||||
|
||||
static const values<unsigned int> magic_values =
|
||||
{
|
||||
0x57f7fc72,
|
||||
0xe582daba,
|
||||
0x5f6ac994,
|
||||
0x35efddb7,
|
||||
0x1fbf5a74,
|
||||
};
|
||||
|
||||
static const values<double> magic_values_double =
|
||||
{
|
||||
0.6764041905675465,
|
||||
0.9533336788140494,
|
||||
0.6091161359041452,
|
||||
0.7668653957125336,
|
||||
0.010374520235509666,
|
||||
};
|
||||
|
||||
/* Special index value which tells check_magic that no check should be
|
||||
performed. */
|
||||
enum { no_check = -1 };
|
||||
|
||||
/* Check that VALUE is the magic value for INDEX, behind a compiler
|
||||
barrier. */
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
check_magic (int index, unsigned int value)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
TEST_COMPARE (value, magic_values.v0);
|
||||
break;
|
||||
case 1:
|
||||
TEST_COMPARE (value, magic_values.v1);
|
||||
break;
|
||||
case 2:
|
||||
TEST_COMPARE (value, magic_values.v2);
|
||||
break;
|
||||
case 3:
|
||||
TEST_COMPARE (value, magic_values.v3);
|
||||
break;
|
||||
case 4:
|
||||
TEST_COMPARE (value, magic_values.v4);
|
||||
break;
|
||||
case no_check:
|
||||
break;
|
||||
default:
|
||||
FAIL_EXIT1 ("invalid magic value index %d", index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that VALUE is the magic value for INDEX, behind a compiler
|
||||
barrier. Double variant. */
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
check_magic (int index, double value)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
TEST_VERIFY (value == magic_values_double.v0);
|
||||
break;
|
||||
case 1:
|
||||
TEST_VERIFY (value == magic_values_double.v1);
|
||||
break;
|
||||
case 2:
|
||||
TEST_VERIFY (value == magic_values_double.v2);
|
||||
break;
|
||||
case 3:
|
||||
TEST_VERIFY (value == magic_values_double.v3);
|
||||
break;
|
||||
case 4:
|
||||
TEST_VERIFY (value == magic_values_double.v4);
|
||||
break;
|
||||
case no_check:
|
||||
break;
|
||||
default:
|
||||
FAIL_EXIT1 ("invalid magic value index %d", index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Store a magic value and check, via the destructor, that it has the
|
||||
expected value. */
|
||||
template <class T, int I>
|
||||
struct checker
|
||||
{
|
||||
T value;
|
||||
|
||||
checker (T v)
|
||||
: value (v)
|
||||
{
|
||||
}
|
||||
|
||||
~checker ()
|
||||
{
|
||||
check_magic (I, value);
|
||||
}
|
||||
};
|
||||
|
||||
/* The functions call_pthread_exit_0, call_pthread_exit_1,
|
||||
call_pthread_exit are used to call pthread_exit indirectly, with
|
||||
the intent of clobbering the register values. */
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
call_pthread_exit_0 (const values<unsigned int> *pvalues)
|
||||
{
|
||||
checker<unsigned int, no_check> c0 (pvalues->v0);
|
||||
checker<unsigned int, no_check> c1 (pvalues->v1);
|
||||
checker<unsigned int, no_check> c2 (pvalues->v2);
|
||||
checker<unsigned int, no_check> c3 (pvalues->v3);
|
||||
checker<unsigned int, no_check> c4 (pvalues->v4);
|
||||
|
||||
pthread_exit (NULL);
|
||||
}
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
call_pthread_exit_1 (const values<double> *pvalues)
|
||||
{
|
||||
checker<double, no_check> c0 (pvalues->v0);
|
||||
checker<double, no_check> c1 (pvalues->v1);
|
||||
checker<double, no_check> c2 (pvalues->v2);
|
||||
checker<double, no_check> c3 (pvalues->v3);
|
||||
checker<double, no_check> c4 (pvalues->v4);
|
||||
|
||||
values<unsigned int> other_values = { 0, };
|
||||
call_pthread_exit_0 (&other_values);
|
||||
}
|
||||
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void
|
||||
call_pthread_exit ()
|
||||
{
|
||||
values<double> other_values = { 0, };
|
||||
call_pthread_exit_1 (&other_values);
|
||||
}
|
||||
|
||||
/* Create on-stack objects and check that their values are restored by
|
||||
pthread_exit. If Nested is true, call pthread_exit indirectly via
|
||||
call_pthread_exit. */
|
||||
template <class T, bool Nested>
|
||||
__attribute__ ((noinline, noclone, weak))
|
||||
void *
|
||||
threadfunc (void *closure)
|
||||
{
|
||||
const values<T> *pvalues = static_cast<const values<T> *> (closure);
|
||||
|
||||
checker<T, 0> c0 (pvalues->v0);
|
||||
checker<T, 1> c1 (pvalues->v1);
|
||||
checker<T, 2> c2 (pvalues->v2);
|
||||
checker<T, 3> c3 (pvalues->v3);
|
||||
checker<T, 4> c4 (pvalues->v4);
|
||||
|
||||
if (Nested)
|
||||
call_pthread_exit ();
|
||||
else
|
||||
pthread_exit (NULL);
|
||||
|
||||
/* This should not be reached. */
|
||||
return const_cast<char *> ("");
|
||||
}
|
||||
|
||||
static int
|
||||
do_test ()
|
||||
{
|
||||
puts ("info: unsigned int, direct pthread_exit call");
|
||||
pthread_t thr
|
||||
= xpthread_create (NULL, &threadfunc<unsigned int, false>,
|
||||
const_cast<values<unsigned int> *> (&magic_values));
|
||||
TEST_VERIFY (xpthread_join (thr) == NULL);
|
||||
|
||||
puts ("info: double, direct pthread_exit call");
|
||||
thr = xpthread_create (NULL, &threadfunc<double, false>,
|
||||
const_cast<values<double> *> (&magic_values_double));
|
||||
TEST_VERIFY (xpthread_join (thr) == NULL);
|
||||
|
||||
puts ("info: unsigned int, indirect pthread_exit call");
|
||||
thr = xpthread_create (NULL, &threadfunc<unsigned int, true>,
|
||||
const_cast<values<unsigned int> *> (&magic_values));
|
||||
TEST_VERIFY (xpthread_join (thr) == NULL);
|
||||
|
||||
puts ("info: double, indirect pthread_exit call");
|
||||
thr = xpthread_create (NULL, &threadfunc<double, true>,
|
||||
const_cast<values<double> *> (&magic_values_double));
|
||||
TEST_VERIFY (xpthread_join (thr) == NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -54,6 +54,12 @@ tests = test-netdb tst-nss-test1 test-digits-dots \
|
||||
$(tests-static)
|
||||
xtests = bug-erange
|
||||
|
||||
# Tests which need libdl
|
||||
ifeq (yes,$(build-shared))
|
||||
tests += tst-nss-files-hosts-erange
|
||||
tests += tst-nss-files-hosts-multi
|
||||
endif
|
||||
|
||||
# If we have a thread library then we can test cancellation against
|
||||
# some routines like getpwuid_r.
|
||||
ifeq (yes,$(have-thread-library))
|
||||
@@ -135,3 +141,6 @@ $(objpfx)tst-nss-test1.out: $(objpfx)/libnss_test1.so$(libnss_test1.so-version)
|
||||
ifeq (yes,$(have-thread-library))
|
||||
$(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
|
||||
endif
|
||||
|
||||
$(objpfx)tst-nss-files-hosts-erange: $(libdl)
|
||||
$(objpfx)tst-nss-files-hosts-multi: $(libdl)
|
||||
|
||||
+217
-193
@@ -22,6 +22,8 @@
|
||||
#include <arpa/nameser.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv/resolv-internal.h>
|
||||
#include <scratch_buffer.h>
|
||||
#include <alloc_buffer.h>
|
||||
|
||||
|
||||
/* Get implementation for some internal functions. */
|
||||
@@ -115,6 +117,219 @@ DB_LOOKUP (hostbyaddr, ,,,
|
||||
}, const void *addr, socklen_t len, int af)
|
||||
#undef EXTRA_ARGS_VALUE
|
||||
|
||||
/* Type of the address and alias arrays. */
|
||||
#define DYNARRAY_STRUCT array
|
||||
#define DYNARRAY_ELEMENT char *
|
||||
#define DYNARRAY_PREFIX array_
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
static enum nss_status
|
||||
gethostbyname3_multi (FILE * stream, const char *name, int af,
|
||||
struct hostent *result, char *buffer, size_t buflen,
|
||||
int *errnop, int *herrnop, int flags)
|
||||
{
|
||||
assert (af == AF_INET || af == AF_INET6);
|
||||
|
||||
/* We have to get all host entries from the file. */
|
||||
struct scratch_buffer tmp_buffer;
|
||||
scratch_buffer_init (&tmp_buffer);
|
||||
struct hostent tmp_result_buf;
|
||||
struct array addresses;
|
||||
array_init (&addresses);
|
||||
struct array aliases;
|
||||
array_init (&aliases);
|
||||
enum nss_status status;
|
||||
|
||||
/* Preserve the addresses and aliases encountered so far. */
|
||||
for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)
|
||||
array_add (&addresses, result->h_addr_list[i]);
|
||||
for (size_t i = 0; result->h_aliases[i] != NULL; ++i)
|
||||
array_add (&aliases, result->h_aliases[i]);
|
||||
|
||||
/* The output buffer re-uses now-unused space at the end of the
|
||||
buffer, starting with the aliases array. It comes last in the
|
||||
data produced by internal_getent. (The alias names themselves
|
||||
are still located in the line read in internal_getent, which is
|
||||
stored at the beginning of the buffer.) */
|
||||
struct alloc_buffer outbuf;
|
||||
{
|
||||
char *bufferend = (char *) result->h_aliases;
|
||||
outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
|
||||
tmp_buffer.length, errnop, herrnop, af,
|
||||
flags);
|
||||
/* Enlarge the buffer if necessary. */
|
||||
if (status == NSS_STATUS_TRYAGAIN && *herrnop == NETDB_INTERNAL
|
||||
&& *errnop == ERANGE)
|
||||
{
|
||||
if (!scratch_buffer_grow (&tmp_buffer))
|
||||
{
|
||||
*errnop = ENOMEM;
|
||||
/* *herrnop and status already have the right value. */
|
||||
break;
|
||||
}
|
||||
/* Loop around and retry with a larger buffer. */
|
||||
}
|
||||
else if (status == NSS_STATUS_SUCCESS)
|
||||
{
|
||||
/* A line was read. Check that it matches the search
|
||||
criteria. */
|
||||
|
||||
int matches = 1;
|
||||
struct hostent *old_result = result;
|
||||
result = &tmp_result_buf;
|
||||
/* The following piece is a bit clumsy but we want to use
|
||||
the `LOOKUP_NAME_CASE' value. The optimizer should do
|
||||
its job. */
|
||||
do
|
||||
{
|
||||
LOOKUP_NAME_CASE (h_name, h_aliases)
|
||||
result = old_result;
|
||||
}
|
||||
while ((matches = 0));
|
||||
|
||||
/* If the line matches, we need to copy the addresses and
|
||||
aliases, so that we can reuse tmp_buffer for the next
|
||||
line. */
|
||||
if (matches)
|
||||
{
|
||||
/* Record the addresses. */
|
||||
for (size_t i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)
|
||||
{
|
||||
/* Allocate the target space in the output buffer,
|
||||
depending on the address family. */
|
||||
void *target;
|
||||
if (af == AF_INET)
|
||||
{
|
||||
assert (tmp_result_buf.h_length == 4);
|
||||
target = alloc_buffer_alloc (&outbuf, struct in_addr);
|
||||
}
|
||||
else if (af == AF_INET6)
|
||||
{
|
||||
assert (tmp_result_buf.h_length == 16);
|
||||
target = alloc_buffer_alloc (&outbuf, struct in6_addr);
|
||||
}
|
||||
else
|
||||
__builtin_unreachable ();
|
||||
|
||||
if (target == NULL)
|
||||
{
|
||||
/* Request a larger output buffer. */
|
||||
*errnop = ERANGE;
|
||||
*herrnop = NETDB_INTERNAL;
|
||||
status = NSS_STATUS_TRYAGAIN;
|
||||
break;
|
||||
}
|
||||
memcpy (target, tmp_result_buf.h_addr_list[i],
|
||||
tmp_result_buf.h_length);
|
||||
array_add (&addresses, target);
|
||||
}
|
||||
|
||||
/* Record the aliases. */
|
||||
for (size_t i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)
|
||||
{
|
||||
char *alias = tmp_result_buf.h_aliases[i];
|
||||
array_add (&aliases,
|
||||
alloc_buffer_copy_string (&outbuf, alias));
|
||||
}
|
||||
|
||||
/* If the real name is different add, it also to the
|
||||
aliases. This means that there is a duplication in
|
||||
the alias list but this is really the user's
|
||||
problem. */
|
||||
{
|
||||
char *new_name = tmp_result_buf.h_name;
|
||||
if (strcmp (old_result->h_name, new_name) != 0)
|
||||
array_add (&aliases,
|
||||
alloc_buffer_copy_string (&outbuf, new_name));
|
||||
}
|
||||
|
||||
/* Report memory allocation failures during the
|
||||
expansion of the temporary arrays. */
|
||||
if (array_has_failed (&addresses) || array_has_failed (&aliases))
|
||||
{
|
||||
*errnop = ENOMEM;
|
||||
*herrnop = NETDB_INTERNAL;
|
||||
status = NSS_STATUS_UNAVAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Request a larger output buffer if we ran out of room. */
|
||||
if (alloc_buffer_has_failed (&outbuf))
|
||||
{
|
||||
*errnop = ERANGE;
|
||||
*herrnop = NETDB_INTERNAL;
|
||||
status = NSS_STATUS_TRYAGAIN;
|
||||
break;
|
||||
}
|
||||
|
||||
result = old_result;
|
||||
} /* If match was found. */
|
||||
|
||||
/* If no match is found, loop around and fetch another
|
||||
line. */
|
||||
|
||||
} /* status == NSS_STATUS_SUCCESS. */
|
||||
else
|
||||
/* internal_getent returned an error. */
|
||||
break;
|
||||
} /* while (true) */
|
||||
|
||||
/* Propagate the NSS_STATUS_TRYAGAIN error to the caller. It means
|
||||
that we may not have loaded the complete result.
|
||||
NSS_STATUS_NOTFOUND, however, means that we reached the end of
|
||||
the file successfully. */
|
||||
if (status != NSS_STATUS_TRYAGAIN)
|
||||
status = NSS_STATUS_SUCCESS;
|
||||
|
||||
if (status == NSS_STATUS_SUCCESS)
|
||||
{
|
||||
/* Copy the address and alias arrays into the output buffer and
|
||||
add NULL terminators. The pointed-to elements were directly
|
||||
written into the output buffer above and do not need to be
|
||||
copied again. */
|
||||
size_t addresses_count = array_size (&addresses);
|
||||
size_t aliases_count = array_size (&aliases);
|
||||
char **out_addresses = alloc_buffer_alloc_array
|
||||
(&outbuf, char *, addresses_count + 1);
|
||||
char **out_aliases = alloc_buffer_alloc_array
|
||||
(&outbuf, char *, aliases_count + 1);
|
||||
if (out_addresses == NULL || out_aliases == NULL)
|
||||
{
|
||||
/* The output buffer is not large enough. */
|
||||
*errnop = ERANGE;
|
||||
*herrnop = NETDB_INTERNAL;
|
||||
status = NSS_STATUS_TRYAGAIN;
|
||||
/* Fall through to function exit. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Everything is allocated in place. Make the copies and
|
||||
adjust the array pointers. */
|
||||
memcpy (out_addresses, array_begin (&addresses),
|
||||
addresses_count * sizeof (char *));
|
||||
out_addresses[addresses_count] = NULL;
|
||||
memcpy (out_aliases, array_begin (&aliases),
|
||||
aliases_count * sizeof (char *));
|
||||
out_aliases[aliases_count] = NULL;
|
||||
|
||||
result->h_addr_list = out_addresses;
|
||||
result->h_aliases = out_aliases;
|
||||
|
||||
status = NSS_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
scratch_buffer_free (&tmp_buffer);
|
||||
array_free (&addresses);
|
||||
array_free (&aliases);
|
||||
return status;
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
|
||||
char *buffer, size_t buflen, int *errnop,
|
||||
@@ -143,199 +358,8 @@ _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
|
||||
|
||||
if (status == NSS_STATUS_SUCCESS
|
||||
&& _res_hconf.flags & HCONF_FLAG_MULTI)
|
||||
{
|
||||
/* We have to get all host entries from the file. */
|
||||
size_t tmp_buflen = MIN (buflen, 4096);
|
||||
char tmp_buffer_stack[tmp_buflen]
|
||||
__attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
|
||||
char *tmp_buffer = tmp_buffer_stack;
|
||||
struct hostent tmp_result_buf;
|
||||
int naddrs = 1;
|
||||
int naliases = 0;
|
||||
char *bufferend;
|
||||
bool tmp_buffer_malloced = false;
|
||||
|
||||
while (result->h_aliases[naliases] != NULL)
|
||||
++naliases;
|
||||
|
||||
bufferend = (char *) &result->h_aliases[naliases + 1];
|
||||
|
||||
again:
|
||||
while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
|
||||
tmp_buflen, errnop, herrnop, af,
|
||||
flags))
|
||||
== NSS_STATUS_SUCCESS)
|
||||
{
|
||||
int matches = 1;
|
||||
struct hostent *old_result = result;
|
||||
result = &tmp_result_buf;
|
||||
/* The following piece is a bit clumsy but we want to use the
|
||||
`LOOKUP_NAME_CASE' value. The optimizer should do its
|
||||
job. */
|
||||
do
|
||||
{
|
||||
LOOKUP_NAME_CASE (h_name, h_aliases)
|
||||
result = old_result;
|
||||
}
|
||||
while ((matches = 0));
|
||||
|
||||
if (matches)
|
||||
{
|
||||
/* We could be very clever and try to recycle a few bytes
|
||||
in the buffer instead of generating new arrays. But
|
||||
we are not doing this here since it's more work than
|
||||
it's worth. Simply let the user provide a bit bigger
|
||||
buffer. */
|
||||
char **new_h_addr_list;
|
||||
char **new_h_aliases;
|
||||
int newaliases = 0;
|
||||
size_t newstrlen = 0;
|
||||
int cnt;
|
||||
|
||||
/* Count the new aliases and the length of the strings. */
|
||||
while (tmp_result_buf.h_aliases[newaliases] != NULL)
|
||||
{
|
||||
char *cp = tmp_result_buf.h_aliases[newaliases];
|
||||
++newaliases;
|
||||
newstrlen += strlen (cp) + 1;
|
||||
}
|
||||
/* If the real name is different add it also to the
|
||||
aliases. This means that there is a duplication
|
||||
in the alias list but this is really the user's
|
||||
problem. */
|
||||
if (strcmp (old_result->h_name,
|
||||
tmp_result_buf.h_name) != 0)
|
||||
{
|
||||
++newaliases;
|
||||
newstrlen += strlen (tmp_result_buf.h_name) + 1;
|
||||
}
|
||||
|
||||
/* Make sure bufferend is aligned. */
|
||||
assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
|
||||
|
||||
/* Now we can check whether the buffer is large enough.
|
||||
16 is the maximal size of the IP address. */
|
||||
if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
|
||||
+ roundup (newstrlen, sizeof (char *))
|
||||
+ (naliases + newaliases + 1) * sizeof (char *)
|
||||
>= buffer + buflen)
|
||||
{
|
||||
*errnop = ERANGE;
|
||||
*herrnop = NETDB_INTERNAL;
|
||||
status = NSS_STATUS_TRYAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_h_addr_list =
|
||||
(char **) (bufferend
|
||||
+ roundup (newstrlen, sizeof (char *))
|
||||
+ 16);
|
||||
new_h_aliases =
|
||||
(char **) ((char *) new_h_addr_list
|
||||
+ (naddrs + 2) * sizeof (char *));
|
||||
|
||||
/* Copy the old data in the new arrays. */
|
||||
for (cnt = 0; cnt < naddrs; ++cnt)
|
||||
new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
|
||||
|
||||
for (cnt = 0; cnt < naliases; ++cnt)
|
||||
new_h_aliases[cnt] = old_result->h_aliases[cnt];
|
||||
|
||||
/* Store the new strings. */
|
||||
cnt = 0;
|
||||
while (tmp_result_buf.h_aliases[cnt] != NULL)
|
||||
{
|
||||
new_h_aliases[naliases++] = bufferend;
|
||||
bufferend = (__stpcpy (bufferend,
|
||||
tmp_result_buf.h_aliases[cnt])
|
||||
+ 1);
|
||||
++cnt;
|
||||
}
|
||||
|
||||
if (cnt < newaliases)
|
||||
{
|
||||
new_h_aliases[naliases++] = bufferend;
|
||||
bufferend = __stpcpy (bufferend,
|
||||
tmp_result_buf.h_name) + 1;
|
||||
}
|
||||
|
||||
/* Final NULL pointer. */
|
||||
new_h_aliases[naliases] = NULL;
|
||||
|
||||
/* Round up the buffer end address. */
|
||||
bufferend += (sizeof (char *)
|
||||
- ((bufferend - (char *) 0)
|
||||
% sizeof (char *))) % sizeof (char *);
|
||||
|
||||
/* Now the new address. */
|
||||
new_h_addr_list[naddrs++] =
|
||||
memcpy (bufferend, tmp_result_buf.h_addr,
|
||||
tmp_result_buf.h_length);
|
||||
|
||||
/* Also here a final NULL pointer. */
|
||||
new_h_addr_list[naddrs] = NULL;
|
||||
|
||||
/* Store the new array pointers. */
|
||||
old_result->h_aliases = new_h_aliases;
|
||||
old_result->h_addr_list = new_h_addr_list;
|
||||
|
||||
/* Compute the new buffer end. */
|
||||
bufferend = (char *) &new_h_aliases[naliases + 1];
|
||||
assert (bufferend <= buffer + buflen);
|
||||
|
||||
result = old_result;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == NSS_STATUS_TRYAGAIN)
|
||||
{
|
||||
size_t newsize = 2 * tmp_buflen;
|
||||
if (tmp_buffer_malloced)
|
||||
{
|
||||
char *newp = realloc (tmp_buffer, newsize);
|
||||
if (newp != NULL)
|
||||
{
|
||||
assert ((((uintptr_t) newp)
|
||||
& (__alignof__ (struct hostent_data) - 1))
|
||||
== 0);
|
||||
tmp_buffer = newp;
|
||||
tmp_buflen = newsize;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
else if (!__libc_use_alloca (buflen + newsize))
|
||||
{
|
||||
tmp_buffer = malloc (newsize);
|
||||
if (tmp_buffer != NULL)
|
||||
{
|
||||
assert ((((uintptr_t) tmp_buffer)
|
||||
& (__alignof__ (struct hostent_data) - 1))
|
||||
== 0);
|
||||
tmp_buffer_malloced = true;
|
||||
tmp_buflen = newsize;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp_buffer
|
||||
= extend_alloca (tmp_buffer, tmp_buflen,
|
||||
newsize
|
||||
+ __alignof__ (struct hostent_data));
|
||||
tmp_buffer = (char *) (((uintptr_t) tmp_buffer
|
||||
+ __alignof__ (struct hostent_data)
|
||||
- 1)
|
||||
& ~(__alignof__ (struct hostent_data)
|
||||
- 1));
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
else
|
||||
status = NSS_STATUS_SUCCESS;
|
||||
out:
|
||||
if (tmp_buffer_malloced)
|
||||
free (tmp_buffer);
|
||||
}
|
||||
status = gethostbyname3_multi
|
||||
(stream, name, af, result, buffer, buflen, errnop, herrnop, flags);
|
||||
|
||||
internal_endent (&stream);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/* Parse /etc/hosts in multi mode with a trailing long line (bug 21915).
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <netdb.h>
|
||||
#include <nss.h>
|
||||
#include <support/check.h>
|
||||
#include <support/check_nss.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
struct support_chroot *chroot_env;
|
||||
|
||||
#define X10 "XXXXXXXXXX"
|
||||
#define X100 X10 X10 X10 X10 X10 X10 X10 X10 X10 X10
|
||||
#define X1000 X100 X100 X100 X100 X100 X100 X100 X100 X100 X100
|
||||
|
||||
static void
|
||||
prepare (int argc, char **argv)
|
||||
{
|
||||
chroot_env = support_chroot_create
|
||||
((struct support_chroot_configuration)
|
||||
{
|
||||
.resolv_conf = "",
|
||||
.hosts =
|
||||
"127.0.0.1 localhost localhost.localdomain\n"
|
||||
"::1 localhost localhost.localdomain\n"
|
||||
"192.0.2.1 example.com\n"
|
||||
"#" X1000 X100 "\n",
|
||||
.host_conf = "multi on\n",
|
||||
});
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
support_become_root ();
|
||||
if (!support_can_chroot ())
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
__nss_configure_lookup ("hosts", "files");
|
||||
if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
|
||||
FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
|
||||
|
||||
xchroot (chroot_env->path_chroot);
|
||||
|
||||
errno = ERANGE;
|
||||
h_errno = NETDB_INTERNAL;
|
||||
check_hostent ("gethostbyname example.com",
|
||||
gethostbyname ("example.com"),
|
||||
"name: example.com\n"
|
||||
"address: 192.0.2.1\n");
|
||||
errno = ERANGE;
|
||||
h_errno = NETDB_INTERNAL;
|
||||
check_hostent ("gethostbyname2 AF_INET example.com",
|
||||
gethostbyname2 ("example.com", AF_INET),
|
||||
"name: example.com\n"
|
||||
"address: 192.0.2.1\n");
|
||||
{
|
||||
struct addrinfo hints =
|
||||
{
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
.ai_protocol = IPPROTO_TCP,
|
||||
};
|
||||
errno = ERANGE;
|
||||
h_errno = NETDB_INTERNAL;
|
||||
struct addrinfo *ai;
|
||||
int ret = getaddrinfo ("example.com", "80", &hints, &ai);
|
||||
check_addrinfo ("example.com AF_UNSPEC", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n");
|
||||
if (ret == 0)
|
||||
freeaddrinfo (ai);
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
errno = ERANGE;
|
||||
h_errno = NETDB_INTERNAL;
|
||||
ret = getaddrinfo ("example.com", "80", &hints, &ai);
|
||||
check_addrinfo ("example.com AF_INET", ai, ret,
|
||||
"address: STREAM/TCP 192.0.2.1 80\n");
|
||||
if (ret == 0)
|
||||
freeaddrinfo (ai);
|
||||
}
|
||||
|
||||
support_chroot_free (chroot_env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PREPARE prepare
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,331 @@
|
||||
/* Parse /etc/hosts in multi mode with many addresses/aliases.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <netdb.h>
|
||||
#include <nss.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/check_nss.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xmemstream.h>
|
||||
#include <support/xstdio.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
struct support_chroot *chroot_env;
|
||||
|
||||
static void
|
||||
prepare (int argc, char **argv)
|
||||
{
|
||||
chroot_env = support_chroot_create
|
||||
((struct support_chroot_configuration)
|
||||
{
|
||||
.resolv_conf = "",
|
||||
.hosts = "", /* See write_hosts below. */
|
||||
.host_conf = "multi on\n",
|
||||
});
|
||||
}
|
||||
|
||||
/* Create the /etc/hosts file from outside the chroot. */
|
||||
static void
|
||||
write_hosts (int count)
|
||||
{
|
||||
TEST_VERIFY (count > 0 && count <= 65535);
|
||||
FILE *fp = xfopen (chroot_env->path_hosts, "w");
|
||||
fputs ("127.0.0.1 localhost localhost.localdomain\n"
|
||||
"::1 localhost localhost.localdomain\n",
|
||||
fp);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
fprintf (fp, "10.4.%d.%d www4.example.com\n",
|
||||
(i / 256) & 0xff, i & 0xff);
|
||||
fprintf (fp, "10.46.%d.%d www.example.com\n",
|
||||
(i / 256) & 0xff, i & 0xff);
|
||||
fprintf (fp, "192.0.2.1 alias.example.com v4-%d.example.com\n", i);
|
||||
fprintf (fp, "2001:db8::6:%x www6.example.com\n", i);
|
||||
fprintf (fp, "2001:db8::46:%x www.example.com\n", i);
|
||||
fprintf (fp, "2001:db8::1 alias.example.com v6-%d.example.com\n", i);
|
||||
}
|
||||
xfclose (fp);
|
||||
}
|
||||
|
||||
/* Parameters of a single test. */
|
||||
struct test_params
|
||||
{
|
||||
const char *name; /* Name to query. */
|
||||
const char *marker; /* Address marker for the name. */
|
||||
int count; /* Number of addresses/aliases. */
|
||||
int family; /* AF_INET, AF_INET_6 or AF_UNSPEC. */
|
||||
bool canonname; /* True if AI_CANONNAME should be enabled. */
|
||||
};
|
||||
|
||||
/* Expected result of gethostbyname/gethostbyname2. */
|
||||
static char *
|
||||
expected_ghbn (const struct test_params *params)
|
||||
{
|
||||
TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);
|
||||
|
||||
struct xmemstream expected;
|
||||
xopen_memstream (&expected);
|
||||
if (strcmp (params->name, "alias.example.com") == 0)
|
||||
{
|
||||
fprintf (expected.out, "name: %s\n", params->name);
|
||||
char af;
|
||||
if (params->family == AF_INET)
|
||||
af = '4';
|
||||
else
|
||||
af = '6';
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fprintf (expected.out, "alias: v%c-%d.example.com\n", af, i);
|
||||
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
if (params->family == AF_INET)
|
||||
fputs ("address: 192.0.2.1\n", expected.out);
|
||||
else
|
||||
fputs ("address: 2001:db8::1\n", expected.out);
|
||||
}
|
||||
else /* www/www4/www6 name. */
|
||||
{
|
||||
bool do_ipv4 = params->family == AF_INET
|
||||
&& strncmp (params->name, "www6", 4) != 0;
|
||||
bool do_ipv6 = params->family == AF_INET6
|
||||
&& strncmp (params->name, "www4", 4) != 0;
|
||||
if (do_ipv4 || do_ipv6)
|
||||
{
|
||||
fprintf (expected.out, "name: %s\n", params->name);
|
||||
if (do_ipv4)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fprintf (expected.out, "address: 10.%s.%d.%d\n",
|
||||
params->marker, i / 256, i % 256);
|
||||
if (do_ipv6)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fprintf (expected.out, "address: 2001:db8::%s:%x\n",
|
||||
params->marker, i);
|
||||
}
|
||||
else
|
||||
fputs ("error: HOST_NOT_FOUND\n", expected.out);
|
||||
}
|
||||
xfclose_memstream (&expected);
|
||||
return expected.buffer;
|
||||
}
|
||||
|
||||
/* Expected result of getaddrinfo. */
|
||||
static char *
|
||||
expected_gai (const struct test_params *params)
|
||||
{
|
||||
bool do_ipv4 = false;
|
||||
bool do_ipv6 = false;
|
||||
if (params->family == AF_UNSPEC)
|
||||
do_ipv4 = do_ipv6 = true;
|
||||
else if (params->family == AF_INET)
|
||||
do_ipv4 = true;
|
||||
else if (params->family == AF_INET6)
|
||||
do_ipv6 = true;
|
||||
|
||||
struct xmemstream expected;
|
||||
xopen_memstream (&expected);
|
||||
if (strcmp (params->name, "alias.example.com") == 0)
|
||||
{
|
||||
if (params->canonname)
|
||||
fprintf (expected.out,
|
||||
"flags: AI_CANONNAME\n"
|
||||
"canonname: %s\n",
|
||||
params->name);
|
||||
|
||||
if (do_ipv4)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fputs ("address: STREAM/TCP 192.0.2.1 80\n", expected.out);
|
||||
if (do_ipv6)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fputs ("address: STREAM/TCP 2001:db8::1 80\n", expected.out);
|
||||
}
|
||||
else /* www/www4/www6 name. */
|
||||
{
|
||||
if (strncmp (params->name, "www4", 4) == 0)
|
||||
do_ipv6 = false;
|
||||
else if (strncmp (params->name, "www6", 4) == 0)
|
||||
do_ipv4 = false;
|
||||
/* Otherwise, we have www as the name, so we do both. */
|
||||
|
||||
if (do_ipv4 || do_ipv6)
|
||||
{
|
||||
if (params->canonname)
|
||||
fprintf (expected.out,
|
||||
"flags: AI_CANONNAME\n"
|
||||
"canonname: %s\n",
|
||||
params->name);
|
||||
|
||||
if (do_ipv4)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fprintf (expected.out, "address: STREAM/TCP 10.%s.%d.%d 80\n",
|
||||
params->marker, i / 256, i % 256);
|
||||
if (do_ipv6)
|
||||
for (int i = 0; i < params->count; ++i)
|
||||
fprintf (expected.out,
|
||||
"address: STREAM/TCP 2001:db8::%s:%x 80\n",
|
||||
params->marker, i);
|
||||
}
|
||||
else
|
||||
fputs ("error: Name or service not known\n", expected.out);
|
||||
}
|
||||
xfclose_memstream (&expected);
|
||||
return expected.buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
run_gbhn_gai (struct test_params *params)
|
||||
{
|
||||
char *ctx = xasprintf ("name=%s marker=%s count=%d family=%d",
|
||||
params->name, params->marker, params->count,
|
||||
params->family);
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s\n", ctx);
|
||||
|
||||
/* Check gethostbyname, gethostbyname2. */
|
||||
if (params->family == AF_INET)
|
||||
{
|
||||
char *expected = expected_ghbn (params);
|
||||
check_hostent (ctx, gethostbyname (params->name), expected);
|
||||
free (expected);
|
||||
}
|
||||
if (params->family != AF_UNSPEC)
|
||||
{
|
||||
char *expected = expected_ghbn (params);
|
||||
check_hostent (ctx, gethostbyname2 (params->name, params->family),
|
||||
expected);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
/* Check getaddrinfo. */
|
||||
for (int do_canonical = 0; do_canonical < 2; ++do_canonical)
|
||||
{
|
||||
params->canonname = do_canonical;
|
||||
char *expected = expected_gai (params);
|
||||
struct addrinfo hints =
|
||||
{
|
||||
.ai_family = params->family,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
.ai_protocol = IPPROTO_TCP,
|
||||
};
|
||||
if (do_canonical)
|
||||
hints.ai_flags |= AI_CANONNAME;
|
||||
struct addrinfo *ai;
|
||||
int ret = getaddrinfo (params->name, "80", &hints, &ai);
|
||||
check_addrinfo (ctx, ai, ret, expected);
|
||||
if (ret == 0)
|
||||
freeaddrinfo (ai);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
free (ctx);
|
||||
}
|
||||
|
||||
/* Callback for the subprocess which runs the test in a chroot. */
|
||||
static void
|
||||
subprocess (void *closure)
|
||||
{
|
||||
struct test_params *params = closure;
|
||||
|
||||
xchroot (chroot_env->path_chroot);
|
||||
|
||||
static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };
|
||||
static const char *const names[] =
|
||||
{
|
||||
"www.example.com", "www4.example.com", "www6.example.com",
|
||||
"alias.example.com",
|
||||
NULL
|
||||
};
|
||||
static const char *const names_marker[] = { "46", "4", "6", "" };
|
||||
|
||||
for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)
|
||||
{
|
||||
params->family = families[family_idx];
|
||||
for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)
|
||||
{
|
||||
params->name = names[names_idx];
|
||||
params->marker = names_marker[names_idx];
|
||||
run_gbhn_gai (params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the test for a specific number of addresses/aliases. */
|
||||
static void
|
||||
run_test (int count)
|
||||
{
|
||||
write_hosts (count);
|
||||
|
||||
struct test_params params =
|
||||
{
|
||||
.count = count,
|
||||
};
|
||||
|
||||
support_isolate_in_subprocess (subprocess, ¶ms);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
support_become_root ();
|
||||
if (!support_can_chroot ())
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
/* This test should not use gigabytes of memory. */
|
||||
{
|
||||
struct rlimit limit;
|
||||
if (getrlimit (RLIMIT_AS, &limit) != 0)
|
||||
{
|
||||
printf ("getrlimit (RLIMIT_AS) failed: %m\n");
|
||||
return 1;
|
||||
}
|
||||
long target = 200 * 1024 * 1024;
|
||||
if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
|
||||
{
|
||||
limit.rlim_cur = target;
|
||||
if (setrlimit (RLIMIT_AS, &limit) != 0)
|
||||
{
|
||||
printf ("setrlimit (RLIMIT_AS) failed: %m\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__nss_configure_lookup ("hosts", "files");
|
||||
if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
|
||||
FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
|
||||
|
||||
/* Run the tests with a few different address/alias counts. */
|
||||
for (int count = 1; count <= 111; ++count)
|
||||
run_test (count);
|
||||
run_test (1111);
|
||||
run_test (22222);
|
||||
|
||||
support_chroot_free (chroot_env);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PREPARE prepare
|
||||
#include <support/test-driver.c>
|
||||
+11
-3
@@ -43,7 +43,7 @@ routines := \
|
||||
getpgid setpgid getpgrp bsd-getpgrp setpgrp getsid setsid \
|
||||
getresuid getresgid setresuid setresgid \
|
||||
pathconf sysconf fpathconf \
|
||||
glob glob64 fnmatch regex \
|
||||
glob glob64 globfree globfree64 glob_pattern_p fnmatch regex \
|
||||
confstr \
|
||||
getopt getopt1 getopt_init \
|
||||
sched_setp sched_getp sched_sets sched_gets sched_yield sched_primax \
|
||||
@@ -91,7 +91,8 @@ tests := tstgetopt testfnm runtests runptests \
|
||||
tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \
|
||||
tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \
|
||||
tst-posix_spawn-fd \
|
||||
tst-posix_fadvise tst-posix_fadvise64
|
||||
tst-posix_fadvise tst-posix_fadvise64 \
|
||||
tst-glob-tilde
|
||||
xtests := bug-ga2
|
||||
ifeq (yes,$(build-shared))
|
||||
test-srcs := globtest
|
||||
@@ -134,7 +135,8 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \
|
||||
$(objpfx)tst-rxspencer-no-utf8-mem.out $(objpfx)tst-pcre-mem.out \
|
||||
$(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \
|
||||
$(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \
|
||||
$(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out
|
||||
$(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \
|
||||
$(objpfx)tst-glob-tilde-mem.out
|
||||
xtests-special += $(objpfx)bug-ga2-mem.out
|
||||
endif
|
||||
|
||||
@@ -341,6 +343,12 @@ $(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace
|
||||
|
||||
$(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(inst_libexecdir)/getconf: $(inst_bindir)/getconf \
|
||||
$(objpfx)getconf.speclist FORCE
|
||||
$(addprefix $(..)./scripts/mkinstalldirs ,\
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Sizes of structs with flexible array members.
|
||||
|
||||
Copyright 2016-2017 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
Written by Paul Eggert. */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Nonzero multiple of alignment of TYPE, suitable for FLEXSIZEOF below.
|
||||
On older platforms without _Alignof, use a pessimistic bound that is
|
||||
safe in practice even if FLEXIBLE_ARRAY_MEMBER is 1.
|
||||
On newer platforms, use _Alignof to get a tighter bound. */
|
||||
|
||||
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
|
||||
# define FLEXALIGNOF(type) (sizeof (type) & ~ (sizeof (type) - 1))
|
||||
#else
|
||||
# define FLEXALIGNOF(type) _Alignof (type)
|
||||
#endif
|
||||
|
||||
/* Upper bound on the size of a struct of type TYPE with a flexible
|
||||
array member named MEMBER that is followed by N bytes of other data.
|
||||
This is not simply sizeof (TYPE) + N, since it may require
|
||||
alignment on unusually picky C11 platforms, and
|
||||
FLEXIBLE_ARRAY_MEMBER may be 1 on pre-C11 platforms.
|
||||
Yield a value less than N if and only if arithmetic overflow occurs. */
|
||||
|
||||
#define FLEXSIZEOF(type, member, n) \
|
||||
((offsetof (type, member) + FLEXALIGNOF (type) - 1 + (n)) \
|
||||
& ~ (FLEXALIGNOF (type) - 1))
|
||||
+373
-407
File diff suppressed because it is too large
Load Diff
@@ -43,10 +43,4 @@ glob64 (const char *pattern, int flags,
|
||||
}
|
||||
libc_hidden_def (glob64)
|
||||
|
||||
void
|
||||
globfree64 (glob64_t *pglob)
|
||||
{
|
||||
}
|
||||
libc_hidden_def (globfree64)
|
||||
|
||||
stub_warning (glob64)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Shared definition for glob and glob_pattern_p.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GLOB_INTERNAL_H
|
||||
# define GLOB_INTERNAL_H
|
||||
|
||||
static inline int
|
||||
__glob_pattern_type (const char *pattern, int quote)
|
||||
{
|
||||
const char *p;
|
||||
int ret = 0;
|
||||
|
||||
for (p = pattern; *p != '\0'; ++p)
|
||||
switch (*p)
|
||||
{
|
||||
case '?':
|
||||
case '*':
|
||||
return 1;
|
||||
|
||||
case '\\':
|
||||
if (quote)
|
||||
{
|
||||
if (p[1] != '\0')
|
||||
++p;
|
||||
ret |= 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case '[':
|
||||
ret |= 4;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if (ret & 4)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* GLOB_INTERNAL_H */
|
||||
@@ -0,0 +1,33 @@
|
||||
/* Return nonzero if PATTERN contains any metacharacters.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _LIBC
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glob.h>
|
||||
#include "glob_internal.h"
|
||||
|
||||
/* Return nonzero if PATTERN contains any metacharacters.
|
||||
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
|
||||
int
|
||||
__glob_pattern_p (const char *pattern, int quote)
|
||||
{
|
||||
return __glob_pattern_type (pattern, quote) == 1;
|
||||
}
|
||||
weak_alias (__glob_pattern_p, glob_pattern_p)
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Frees the dynamically allocated storage from an earlier call to glob.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _LIBC
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glob.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Free storage allocated in PGLOB by a previous `glob' call. */
|
||||
void
|
||||
globfree (glob_t *pglob)
|
||||
{
|
||||
if (pglob->gl_pathv != NULL)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < pglob->gl_pathc; ++i)
|
||||
free (pglob->gl_pathv[pglob->gl_offs + i]);
|
||||
free (pglob->gl_pathv);
|
||||
pglob->gl_pathv = NULL;
|
||||
}
|
||||
}
|
||||
#ifndef globfree
|
||||
libc_hidden_def (globfree)
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Frees the dynamically allocated storage from an earlier call to glob.
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _LIBC
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glob.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Free storage allocated in PGLOB by a previous `glob' call. */
|
||||
void
|
||||
globfree64 (glob64_t *pglob)
|
||||
{
|
||||
}
|
||||
libc_hidden_def (globfree64)
|
||||
+6
-3
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/* Check for GLOB_TIDLE heap allocation issues (bugs 22320, 22325, 22332).
|
||||
Copyright (C) 2017 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <glob.h>
|
||||
#include <mcheck.h>
|
||||
#include <nss.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* Flag which indicates whether to pass the GLOB_ONLYDIR flag. */
|
||||
static int do_onlydir;
|
||||
|
||||
/* Flag which indicates whether to pass the GLOB_NOCHECK flag. */
|
||||
static int do_nocheck;
|
||||
|
||||
/* Flag which indicates whether to pass the GLOB_MARK flag. */
|
||||
static int do_mark;
|
||||
|
||||
/* Flag which indicates whether to pass the GLOB_NOESCAPE flag. */
|
||||
static int do_noescape;
|
||||
|
||||
static void
|
||||
one_test (const char *prefix, const char *middle, const char *suffix)
|
||||
{
|
||||
char *pattern = xasprintf ("%s%s%s", prefix, middle, suffix);
|
||||
int flags = GLOB_TILDE;
|
||||
if (do_onlydir)
|
||||
flags |= GLOB_ONLYDIR;
|
||||
if (do_nocheck)
|
||||
flags |= GLOB_NOCHECK;
|
||||
if (do_mark)
|
||||
flags |= GLOB_MARK;
|
||||
if (do_noescape)
|
||||
flags |= GLOB_NOESCAPE;
|
||||
glob_t gl;
|
||||
/* This glob call might result in crashes or memory leaks. */
|
||||
if (glob (pattern, flags, NULL, &gl) == 0)
|
||||
globfree (&gl);
|
||||
free (pattern);
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
/* The largest base being tested. */
|
||||
largest_base_size = 500000,
|
||||
|
||||
/* The actual size is the base size plus a variable whose absolute
|
||||
value is not greater than this. This helps malloc to trigger
|
||||
overflows. */
|
||||
max_size_skew = 16,
|
||||
|
||||
/* The maximum string length supported by repeating_string
|
||||
below. */
|
||||
repeat_size = largest_base_size + max_size_skew,
|
||||
};
|
||||
|
||||
/* Used to construct strings which repeat a single character 'x'. */
|
||||
static char *repeat;
|
||||
|
||||
/* Return a string of SIZE characters. */
|
||||
const char *
|
||||
repeating_string (int size)
|
||||
{
|
||||
TEST_VERIFY (size >= 0);
|
||||
TEST_VERIFY (size <= repeat_size);
|
||||
const char *repeated_shifted = repeat + repeat_size - size;
|
||||
TEST_VERIFY (strlen (repeated_shifted) == size);
|
||||
return repeated_shifted;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Avoid network-based NSS modules and initialize nss_files with a
|
||||
dummy lookup. This has to come before mtrace because NSS does
|
||||
not free all memory. */
|
||||
__nss_configure_lookup ("passwd", "files");
|
||||
(void) getpwnam ("root");
|
||||
|
||||
mtrace ();
|
||||
|
||||
repeat = xmalloc (repeat_size + 1);
|
||||
memset (repeat, 'x', repeat_size);
|
||||
repeat[repeat_size] = '\0';
|
||||
|
||||
/* These numbers control the size of the user name. The values
|
||||
cover the minimum (0), a typical size (8), a large
|
||||
stack-allocated size (100000), and a somewhat large
|
||||
heap-allocated size (largest_base_size). */
|
||||
static const int base_sizes[] = { 0, 8, 100, 100000, largest_base_size, -1 };
|
||||
|
||||
for (do_onlydir = 0; do_onlydir < 2; ++do_onlydir)
|
||||
for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck)
|
||||
for (do_mark = 0; do_mark < 2; ++do_mark)
|
||||
for (do_noescape = 0; do_noescape < 2; ++do_noescape)
|
||||
for (int base_idx = 0; base_sizes[base_idx] >= 0; ++base_idx)
|
||||
{
|
||||
for (int size_skew = -max_size_skew; size_skew <= max_size_skew;
|
||||
++size_skew)
|
||||
{
|
||||
int size = base_sizes[base_idx] + size_skew;
|
||||
if (size < 0)
|
||||
continue;
|
||||
|
||||
const char *user_name = repeating_string (size);
|
||||
one_test ("~", user_name, "/a/b");
|
||||
one_test ("~", user_name, "x\\x\\x////x\\a");
|
||||
}
|
||||
|
||||
const char *user_name = repeating_string (base_sizes[base_idx]);
|
||||
one_test ("~", user_name, "");
|
||||
one_test ("~", user_name, "/");
|
||||
one_test ("~", user_name, "/a");
|
||||
one_test ("~", user_name, "/*/*");
|
||||
one_test ("~", user_name, "\\/");
|
||||
one_test ("/~", user_name, "");
|
||||
one_test ("*/~", user_name, "/a/b");
|
||||
}
|
||||
|
||||
free (repeat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user