mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-10 05:59:39 +08:00
Compare commits
159
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cde88e7398 | ||
|
|
e9db77683e | ||
|
|
44fd8887d0 | ||
|
|
4a68828e37 | ||
|
|
56fbd0ba21 | ||
|
|
d81114e0aa | ||
|
|
e5dac996b9 | ||
|
|
98979f62b8 | ||
|
|
fe09348c4e | ||
|
|
722c93572e | ||
|
|
b915da29da | ||
|
|
4bd28df0b0 | ||
|
|
118fbee7a0 | ||
|
|
148200bda3 | ||
|
|
f82072183a | ||
|
|
23db5555eb | ||
|
|
e51e3044e1 | ||
|
|
21344a3d62 | ||
|
|
0478174d1e | ||
|
|
9ff61e9159 | ||
|
|
6578d89c17 | ||
|
|
ef0051070f | ||
|
|
f71df694b1 | ||
|
|
9f917f7b41 | ||
|
|
f55e312bcd | ||
|
|
b32c275714 | ||
|
|
7966ce07e8 | ||
|
|
cedb3e47c6 | ||
|
|
fe7c01cb14 | ||
|
|
59991bf48a | ||
|
|
cef4c840a8 | ||
|
|
0d3905b110 | ||
|
|
f144981490 | ||
|
|
91d5989356 | ||
|
|
6d750b1899 | ||
|
|
5dab5eafb3 | ||
|
|
cb89ba9c72 | ||
|
|
a645c48756 | ||
|
|
f88c59f465 | ||
|
|
3640758943 | ||
|
|
b58bace7ce | ||
|
|
354f52e984 | ||
|
|
d6ccf2f45c | ||
|
|
1def6a34ae | ||
|
|
1533274d5f | ||
|
|
57922433fa | ||
|
|
298e659172 | ||
|
|
b95c8a3150 | ||
|
|
5f7fa9ac30 | ||
|
|
22f3c11df1 | ||
|
|
91b02c5b4d | ||
|
|
58d2672f64 | ||
|
|
059d6750f9 | ||
|
|
d948478bc5 | ||
|
|
4a5e58827f | ||
|
|
38e8981833 | ||
|
|
b9054d6602 | ||
|
|
4d24775292 | ||
|
|
5cbb73004b | ||
|
|
9b3aac5869 | ||
|
|
630d7201ce | ||
|
|
7423da211d | ||
|
|
4aeff335ca | ||
|
|
54e725e39d | ||
|
|
2aee101ff6 | ||
|
|
4bf5ab3196 | ||
|
|
0d1d7fb843 | ||
|
|
e5366c12d0 | ||
|
|
384113d1c0 | ||
|
|
e8c13d5f7a | ||
|
|
60f8062425 | ||
|
|
a9f60b1571 | ||
|
|
85224b0290 | ||
|
|
c533244b8e | ||
|
|
2373941bd7 | ||
|
|
37edf1d3f8 | ||
|
|
8e92ca5dd7 | ||
|
|
9aaa083387 | ||
|
|
d09b11cbe5 | ||
|
|
07a42c0eff | ||
|
|
c678b80233 | ||
|
|
17fc7debcc | ||
|
|
eee0a3d04b | ||
|
|
781403407b | ||
|
|
f57666aa30 | ||
|
|
492524a691 | ||
|
|
b297581acb | ||
|
|
198bfee808 | ||
|
|
1e5c5303a5 | ||
|
|
2a8612b278 | ||
|
|
be62bbb621 | ||
|
|
bee4baf6d7 | ||
|
|
8f83d095fa | ||
|
|
69b914a99e | ||
|
|
27e039455d | ||
|
|
b4ce4476fc | ||
|
|
1a8db070fa | ||
|
|
4d7af7815a | ||
|
|
852620258d | ||
|
|
ea16a6938d | ||
|
|
b5a9a19b82 | ||
|
|
4c2dada507 | ||
|
|
874c28c9f5 | ||
|
|
9dd07a91db | ||
|
|
4718b053df | ||
|
|
5f1ae50a78 | ||
|
|
8c89e6b02d | ||
|
|
b8dd0f4278 | ||
|
|
ce6ba630db | ||
|
|
f5cc21eaee | ||
|
|
481a6cf0c2 | ||
|
|
2c7078bfb9 | ||
|
|
10f1519f6a | ||
|
|
f44c2ca5ea | ||
|
|
e7388e5134 | ||
|
|
9071be6b3f | ||
|
|
7d174f5353 | ||
|
|
53a7e59405 | ||
|
|
7e40c3f804 | ||
|
|
510a25f2d2 | ||
|
|
168035056e | ||
|
|
b21abc069f | ||
|
|
3e8d8dd5af | ||
|
|
fc0e3393ff | ||
|
|
65010329f2 | ||
|
|
e1af1df694 | ||
|
|
6c2b6e9e27 | ||
|
|
69dcd992a0 | ||
|
|
dcd52b94bf | ||
|
|
d0b6db4acf | ||
|
|
df11de9193 | ||
|
|
5256ffc51e | ||
|
|
aff9b37712 | ||
|
|
69d1e73d99 | ||
|
|
314e181dc9 | ||
|
|
5a74abda20 | ||
|
|
5473739a7b | ||
|
|
044c96f0d5 | ||
|
|
3a67c72c15 | ||
|
|
2339d6a55e | ||
|
|
0ef2f4400c | ||
|
|
a55e109709 | ||
|
|
307d04334d | ||
|
|
e7d22db29c | ||
|
|
1fe2b9ca8a | ||
|
|
0b79004569 | ||
|
|
58559f1443 | ||
|
|
aa8a3e4cde | ||
|
|
fd644b6fd9 | ||
|
|
c87b5bab24 | ||
|
|
66fdfd57fe | ||
|
|
b0aa03dfff | ||
|
|
2f498f3d14 | ||
|
|
bfcfa22589 | ||
|
|
d05b05d157 | ||
|
|
4b25485f03 | ||
|
|
726e1554ce | ||
|
|
7f11842e74 | ||
|
|
32e03a5b64 |
+4
-2
@@ -831,8 +831,10 @@ endif
|
||||
# disable any optimization that assume default rounding mode.
|
||||
+math-flags = -frounding-math
|
||||
|
||||
# Build libc/libm using -fno-math-errno, but run testsuite with -fmath-errno.
|
||||
+extra-math-flags = $(if $(filter libnldbl nonlib testsuite,$(in-module)),-fmath-errno,-fno-math-errno)
|
||||
# Logically only "libnldbl", "nonlib" and "testsuite" should be using
|
||||
# -fno-math-errno. However due to GCC bug #88576, only "libm" can use
|
||||
# -fno-math-errno.
|
||||
+extra-math-flags = $(if $(filter libm,$(in-module)),-fno-math-errno,-fmath-errno)
|
||||
|
||||
# We might want to compile with some stack-protection flag.
|
||||
ifneq ($(stack-protector),)
|
||||
|
||||
@@ -4,6 +4,119 @@ See the end for copying conditions.
|
||||
|
||||
Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
|
||||
using `glibc' in the "product" field.
|
||||
|
||||
Version 2.28.1
|
||||
|
||||
Major new features:
|
||||
|
||||
* The entry for the new Japanese era has been added for ja_JP locale.
|
||||
|
||||
Deprecated and removed features, and other changes affecting compatibility:
|
||||
|
||||
* For powercp64le ABI, Transactional Lock Elision is now enabled iff kernel
|
||||
indicates that it will abort the transaction prior to entering the kernel
|
||||
(PPC_FEATURE2_HTM_NOSC on hwcap2). On older kernels the transaction is
|
||||
suspended, and this caused some undefined side-effects issues by aborting
|
||||
transactions manually. Glibc avoided it by abort transactions manually on
|
||||
each syscall, but it lead to performance issues on newer kernels where the
|
||||
HTM state is saved and restore lazily (the state being saved even when the
|
||||
process actually does not use HTM).
|
||||
|
||||
* The copy_file_range function fails with ENOSYS if the kernel does not
|
||||
support the system call of the same name. Previously, user space
|
||||
emulation was performed, but its behavior did not match the kernel
|
||||
behavior, which was deemed too confusing. Applications which use the
|
||||
copy_file_range function will have to be run on kernels which implement
|
||||
the copy_file_range system call. Support for most architectures was added
|
||||
in version 4.5 of the mainline Linux kernel.
|
||||
|
||||
The following bugs are resolved with this release:
|
||||
|
||||
[18035] Fix pldd hang
|
||||
[19444] build failures with -O1 due to -Wmaybe-uninitialized
|
||||
[20018] getaddrinfo should reject IP addresses with trailing characters
|
||||
[20019] NULL pointer dereference in libc.so.6 IFUNC due to uninitialized GOT
|
||||
[20209] localedata: Spelling mistake for Sunday in Greenlandic kl_GL
|
||||
[20568] Fix crash in _IO_wfile_sync
|
||||
[22927] libanl: properly cleanup if first helper thread creation failed
|
||||
[23400] stdlib/test-bz22786.c creates temporary files in glibc source tree
|
||||
[23497] readdir64@GLIBC_2.1 cannot parse the kernel directory stream
|
||||
[23509] CET enabled glibc is incompatible with the older linker
|
||||
[23521] nss_files aliases database file stream leak
|
||||
[23538] pthread_cond_broadcast: Fix waiters-after-spinning case
|
||||
[23562] signal: Use correct type for si_band in siginfo_t
|
||||
[23578] regex: Fix memory overread in re_compile_pattern
|
||||
[23579] libc: Errors misreported in preadv2
|
||||
[23606] Missing ENDBR32 in sysdeps/i386/start.S
|
||||
[23614] powerpc: missing CFI register information in __mpn_* functions
|
||||
[23679] gethostid: Missing NULL check for gethostbyname_r result
|
||||
[23709] Fix CPU string flags for Haswell-type CPUs
|
||||
[23717] Fix stack overflow in stdlib/tst-setcontext9
|
||||
[23821] si_band in siginfo_t has wrong type long int on sparc64
|
||||
[23822] ia64 static libm.a is missing exp2f, log2f and powf symbols
|
||||
[23864] libc: [riscv] missing kernel-features.h undefines
|
||||
[23844] pthread_rwlock_trywrlock results in hang
|
||||
[23927] Linux if_nametoindex() does not close descriptor (CVE-2018-19591)
|
||||
[23972] __old_getdents64 uses wrong d_off value on overflow
|
||||
[24018] gettext may return NULL
|
||||
[24022] riscv may lack <asm/syscalls.h>
|
||||
[24024] strerror() might set errno to ENOMEM due to -fno-math-error
|
||||
[24027] malloc: Integer overflow in realloc
|
||||
[24034] tst-cancel21-static fails with SIGBUS on pre-ARMv7 when using GCC 8
|
||||
[24040] riscv64: unterminated call chain in __thread_start
|
||||
[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)
|
||||
[24161] __run_fork_handlers self-deadlocks in malloc/tst-mallocfork2
|
||||
[24228] old x86 applications that use legacy libio crash on exit
|
||||
[24476] dlfcn: Guard __dlerror_main_freeres with __libc_once_get (once)
|
||||
[24744] io: Remove the copy_file_range emulation.
|
||||
[25203] libio: Disable vtable validation for pre-2.1 interposed handles
|
||||
[25204] Ignore LD_PREFER_MAP_32BIT_EXEC for SUID programs
|
||||
[25225] ld.so fails to link on x86 if GCC defaults to -fcf-protection
|
||||
[25232] No const correctness for strchr et al. for Clang++
|
||||
[25414] 'glob' use-after-free bug (CVE-2020-1752)
|
||||
[25423] Array overflow in backtrace on powerpc
|
||||
[25933] Off by one error in __strncmp_avx2
|
||||
[27130] "rep movsb" performance issue
|
||||
[27177] GLIBC_TUNABLES=glibc.cpu.x86_ibt=on:glibc.cpu.x86_shstk=on doesn't work
|
||||
|
||||
Security related changes:
|
||||
|
||||
CVE-2018-19591: A file descriptor leak in if_nametoindex can lead to a
|
||||
denial of service due to resource exhaustion when processing getaddrinfo
|
||||
calls with crafted host names. Reported by Guido Vranken.
|
||||
|
||||
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-2016-10739: The getaddrinfo function could successfully parse IPv4
|
||||
addresses with arbitrary trailing characters, potentially leading to data
|
||||
or command injection issues in applications.
|
||||
|
||||
CVE-2019-9169: Attempted case-insensitive regular-expression match
|
||||
via proceed_next_node in posix/regexec.c leads to heap-based buffer
|
||||
over-read. Reported by Hongxu Chen.
|
||||
|
||||
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.
|
||||
|
||||
CVE-2020-1752: A use-after-free vulnerability in the glob function when
|
||||
expanding ~user has been fixed.
|
||||
|
||||
|
||||
Version 2.28
|
||||
|
||||
@@ -422,6 +535,8 @@ The following bugs are resolved with this release:
|
||||
[23459] libc: COMMON_CPUID_INDEX_80000001 isn't populated for Intel
|
||||
processors
|
||||
[23467] dynamic-link: x86/CET: A property note parser bug
|
||||
[24112] network: Do not send DNS queries for non-host names (where all
|
||||
answers will be rejected)
|
||||
|
||||
|
||||
Version 2.27
|
||||
|
||||
@@ -3762,11 +3762,32 @@ else
|
||||
fi
|
||||
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
#ifndef __CET__
|
||||
#error no CET compiler support
|
||||
#endif
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
libc_cv_compiler_default_cet=yes
|
||||
else
|
||||
libc_cv_compiler_default_cet=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
|
||||
# Check whether --enable-cet was given.
|
||||
if test "${enable_cet+set}" = set; then :
|
||||
enableval=$enable_cet; enable_cet=$enableval
|
||||
else
|
||||
enable_cet=no
|
||||
enable_cet=$libc_cv_compiler_default_cet
|
||||
fi
|
||||
|
||||
|
||||
|
||||
+8
-1
@@ -464,11 +464,18 @@ AC_ARG_ENABLE([mathvec],
|
||||
[build_mathvec=$enableval],
|
||||
[build_mathvec=notset])
|
||||
|
||||
AC_TRY_COMPILE([], [
|
||||
#ifndef __CET__
|
||||
# error no CET compiler support
|
||||
#endif],
|
||||
[libc_cv_compiler_default_cet=yes],
|
||||
[libc_cv_compiler_default_cet=no])
|
||||
|
||||
AC_ARG_ENABLE([cet],
|
||||
AC_HELP_STRING([--enable-cet],
|
||||
[enable Intel Control-flow Enforcement Technology (CET), x86 only]),
|
||||
[enable_cet=$enableval],
|
||||
[enable_cet=no])
|
||||
[enable_cet=$libc_cv_compiler_default_cet])
|
||||
|
||||
# We keep the original values in `$config_*' and never modify them, so we
|
||||
# can write them unchanged into config.make. Everything else uses
|
||||
|
||||
@@ -172,7 +172,8 @@ element siginfo_t pid_t si_pid
|
||||
element siginfo_t uid_t si_uid
|
||||
element siginfo_t {void*} si_addr
|
||||
element siginfo_t int si_status
|
||||
element siginfo_t long si_band
|
||||
// Bug 23821: si_band has type int on sparc64.
|
||||
xfail[sparc64-linux]-element siginfo_t long si_band
|
||||
# endif
|
||||
# ifndef XPG42
|
||||
element siginfo_t {union sigval} si_value
|
||||
|
||||
@@ -46,7 +46,8 @@ element siginfo_t pid_t si_pid
|
||||
element siginfo_t uid_t si_uid
|
||||
element siginfo_t {void*} si_addr
|
||||
element siginfo_t int si_status
|
||||
element siginfo_t long si_band
|
||||
// Bug 23821: si_band has type int on sparc64.
|
||||
xfail[sparc64-linux]-element siginfo_t long si_band
|
||||
# ifndef XPG42
|
||||
element siginfo_t {union sigval} si_value
|
||||
# endif
|
||||
|
||||
@@ -88,6 +88,18 @@ handle_signal (int signum)
|
||||
}
|
||||
/* Symbol names are not available for static functions, so we do not
|
||||
check do_test. */
|
||||
|
||||
/* Check that backtrace does not return more than what fits in the array
|
||||
(bug 25423). */
|
||||
for (int j = 0; j < NUM_FUNCTIONS; j++)
|
||||
{
|
||||
n = backtrace (addresses, j);
|
||||
if (n > j)
|
||||
{
|
||||
FAIL ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NO_INLINE int
|
||||
|
||||
+25
-9
@@ -72,9 +72,16 @@ __dlerror (void)
|
||||
__libc_once (once, init);
|
||||
|
||||
/* Get error string. */
|
||||
result = (struct dl_action_result *) __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
result = &last_result;
|
||||
if (static_buf != NULL)
|
||||
result = static_buf;
|
||||
else
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
result = (struct dl_action_result *) __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
result = &last_result;
|
||||
}
|
||||
|
||||
/* Test whether we already returned the string. */
|
||||
if (result->returned != 0)
|
||||
@@ -198,7 +205,10 @@ check_free (struct dl_action_result *rec)
|
||||
Dl_info info;
|
||||
if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0)
|
||||
#endif
|
||||
free ((char *) rec->errstring);
|
||||
{
|
||||
free ((char *) rec->errstring);
|
||||
rec->errstring = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +237,19 @@ free_key_mem (void *mem)
|
||||
void
|
||||
__dlerror_main_freeres (void)
|
||||
{
|
||||
void *mem;
|
||||
/* Free the global memory if used. */
|
||||
check_free (&last_result);
|
||||
/* Free the TSD memory if used. */
|
||||
mem = __libc_getspecific (key);
|
||||
if (mem != NULL)
|
||||
free_key_mem (mem);
|
||||
|
||||
if (__libc_once_get (once) && static_buf == NULL)
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
void *mem;
|
||||
/* Free the TSD memory if used. */
|
||||
mem = __libc_getspecific (key);
|
||||
if (mem != NULL)
|
||||
free_key_mem (mem);
|
||||
}
|
||||
}
|
||||
|
||||
struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
|
||||
|
||||
+6
-1
@@ -186,7 +186,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
|
||||
tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \
|
||||
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
|
||||
tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
|
||||
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note
|
||||
tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
|
||||
tst-unwind-main
|
||||
# reldep9
|
||||
tests-internal += loadtest unload unload2 circleload1 \
|
||||
neededtest neededtest2 neededtest3 neededtest4 \
|
||||
@@ -1266,6 +1267,8 @@ CFLAGS-ifuncmain5pie.c += $(pie-ccflag)
|
||||
CFLAGS-ifuncmain6pie.c += $(pie-ccflag)
|
||||
CFLAGS-ifuncmain7pie.c += $(pie-ccflag)
|
||||
|
||||
LDFLAGS-ifuncmain6pie = -Wl,-z,lazy
|
||||
|
||||
$(objpfx)ifuncmain1pie: $(objpfx)ifuncmod1.so
|
||||
$(objpfx)ifuncmain1staticpie: $(objpfx)ifuncdep1pic.o
|
||||
$(objpfx)ifuncmain1vispie: $(objpfx)ifuncmod1.so
|
||||
@@ -1484,3 +1487,5 @@ tst-libc_dlvsym-static-ENV = \
|
||||
$(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so
|
||||
|
||||
$(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
|
||||
|
||||
CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0
|
||||
|
||||
+14
-4
@@ -1173,6 +1173,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
goto call_lose;
|
||||
}
|
||||
|
||||
/* dlopen of an executable is not valid because it is not possible
|
||||
to perform proper relocations, handle static TLS, or run the
|
||||
ELF constructors. For PIE, the check needs the dynamic
|
||||
section, so there is another check below. */
|
||||
if (__glibc_unlikely (type != ET_DYN)
|
||||
&& __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0))
|
||||
{
|
||||
@@ -1209,9 +1213,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
elf_get_dynamic_info (l, NULL);
|
||||
|
||||
/* Make sure we are not dlopen'ing an object that has the
|
||||
DF_1_NOOPEN flag set. */
|
||||
if (__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
|
||||
&& (mode & __RTLD_DLOPEN))
|
||||
DF_1_NOOPEN flag set, or a PIE object. */
|
||||
if ((__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
|
||||
&& (mode & __RTLD_DLOPEN))
|
||||
|| (__glibc_unlikely (l->l_flags_1 & DF_1_PIE)
|
||||
&& __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0)))
|
||||
{
|
||||
/* We are not supposed to load this object. Free all resources. */
|
||||
_dl_unmap_segments (l);
|
||||
@@ -1222,7 +1228,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
if (l->l_phdr_allocated)
|
||||
free ((void *) l->l_phdr);
|
||||
|
||||
errstring = N_("shared object cannot be dlopen()ed");
|
||||
if (l->l_flags_1 & DF_1_PIE)
|
||||
errstring
|
||||
= N_("cannot dynamically load position-independent executable");
|
||||
else
|
||||
errstring = N_("shared object cannot be dlopen()ed");
|
||||
goto call_lose;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -292,8 +292,6 @@ dl_open_worker (void *a)
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
|
||||
|
||||
_dl_open_check (new);
|
||||
|
||||
/* Print scope information. */
|
||||
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
|
||||
_dl_show_scope (new, 0);
|
||||
@@ -366,6 +364,12 @@ dl_open_worker (void *a)
|
||||
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
|
||||
}
|
||||
|
||||
/* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
|
||||
object when _dl_open_check throws an exception. Move it after
|
||||
relocation to avoid leaving the NODELETE object mapped without
|
||||
relocation. */
|
||||
_dl_open_check (new);
|
||||
|
||||
/* If the file is not loaded now as a dependency, add the search
|
||||
list of the newly loaded object to the scope. */
|
||||
bool any_tls = false;
|
||||
|
||||
+43
-5
@@ -183,10 +183,36 @@ _dl_profile_fixup (
|
||||
/* This is the address in the array where we store the result of previous
|
||||
relocations. */
|
||||
struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
|
||||
DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr;
|
||||
|
||||
DL_FIXUP_VALUE_TYPE value = *resultp;
|
||||
if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0)
|
||||
/* CONCURRENCY NOTES:
|
||||
|
||||
Multiple threads may be calling the same PLT sequence and with
|
||||
LD_AUDIT enabled they will be calling into _dl_profile_fixup to
|
||||
update the reloc_result with the result of the lazy resolution.
|
||||
The reloc_result guard variable is reloc_init, and we use
|
||||
acquire/release loads and store to it to ensure that the results of
|
||||
the structure are consistent with the loaded value of the guard.
|
||||
This does not fix all of the data races that occur when two or more
|
||||
threads read reloc_result->reloc_init with a value of zero and read
|
||||
and write to that reloc_result concurrently. The expectation is
|
||||
generally that while this is a data race it works because the
|
||||
threads write the same values. Until the data races are fixed
|
||||
there is a potential for problems to arise from these data races.
|
||||
The reloc result updates should happen in parallel but there should
|
||||
be an atomic RMW which does the final update to the real result
|
||||
entry (see bug 23790).
|
||||
|
||||
The following code uses reloc_result->init set to 0 to indicate if it is
|
||||
the first time this object is being relocated, otherwise 1 which
|
||||
indicates the object has already been relocated.
|
||||
|
||||
Reading/Writing from/to reloc_result->reloc_init must not happen
|
||||
before previous writes to reloc_result complete as they could
|
||||
end-up with an incomplete struct. */
|
||||
DL_FIXUP_VALUE_TYPE value;
|
||||
unsigned int init = atomic_load_acquire (&reloc_result->init);
|
||||
|
||||
if (init == 0)
|
||||
{
|
||||
/* This is the first time we have to relocate this object. */
|
||||
const ElfW(Sym) *const symtab
|
||||
@@ -346,19 +372,31 @@ _dl_profile_fixup (
|
||||
|
||||
/* Store the result for later runs. */
|
||||
if (__glibc_likely (! GLRO(dl_bind_not)))
|
||||
*resultp = value;
|
||||
{
|
||||
reloc_result->addr = value;
|
||||
/* Guarantee all previous writes complete before
|
||||
init is updated. See CONCURRENCY NOTES earlier */
|
||||
atomic_store_release (&reloc_result->init, 1);
|
||||
}
|
||||
init = 1;
|
||||
}
|
||||
else
|
||||
value = reloc_result->addr;
|
||||
|
||||
/* By default we do not call the pltexit function. */
|
||||
long int framesize = -1;
|
||||
|
||||
|
||||
#ifdef SHARED
|
||||
/* Auditing checkpoint: report the PLT entering and allow the
|
||||
auditors to change the value. */
|
||||
if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0
|
||||
if (GLRO(dl_naudit) > 0
|
||||
/* Don't do anything if no auditor wants to intercept this call. */
|
||||
&& (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0)
|
||||
{
|
||||
/* Sanity check: DL_FIXUP_VALUE_CODE_ADDR (value) should have been
|
||||
initialized earlier in this function or in another thread. */
|
||||
assert (DL_FIXUP_VALUE_CODE_ADDR (value) != 0);
|
||||
ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
|
||||
l_info[DT_SYMTAB])
|
||||
+ reloc_result->boundndx);
|
||||
|
||||
@@ -85,6 +85,11 @@ glibc {
|
||||
tcache_unsorted_limit {
|
||||
type: SIZE_T
|
||||
}
|
||||
mxfast {
|
||||
type: SIZE_T
|
||||
minval: 0
|
||||
security_level: SXID_IGNORE
|
||||
}
|
||||
}
|
||||
tune {
|
||||
hwcap_mask {
|
||||
|
||||
@@ -2847,6 +2847,13 @@ enum
|
||||
#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
|
||||
#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
|
||||
|
||||
/* AArch64 specific values for the Dyn d_tag field. */
|
||||
#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5)
|
||||
#define DT_AARCH64_NUM 6
|
||||
|
||||
/* AArch64 specific values for the st_other field. */
|
||||
#define STO_AARCH64_VARIANT_PCS 0x80
|
||||
|
||||
/* ARM relocs. */
|
||||
|
||||
#define R_ARM_NONE 0 /* No reloc */
|
||||
|
||||
@@ -142,10 +142,7 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
|
||||
assert (info[DT_FLAGS] == NULL
|
||||
|| (info[DT_FLAGS]->d_un.d_val & ~DF_BIND_NOW) == 0);
|
||||
#endif
|
||||
#if defined RTLD_BOOTSTRAP || defined STATIC_PIE_BOOTSTRAP
|
||||
assert (info[DT_RUNPATH] == NULL);
|
||||
assert (info[DT_RPATH] == NULL);
|
||||
#else
|
||||
#if ! defined RTLD_BOOTSTRAP && ! defined STATIC_PIE_BOOTSTRAP
|
||||
if (info[DT_FLAGS] != NULL)
|
||||
{
|
||||
/* Flags are used. Translate to the old form where available.
|
||||
|
||||
+3
-11
@@ -9,7 +9,6 @@
|
||||
#include "ifunc-sel.h"
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
static int
|
||||
one (void)
|
||||
@@ -28,20 +27,17 @@ foo_ifunc (void)
|
||||
}
|
||||
|
||||
extern int foo (void);
|
||||
extern foo_p get_foo (void);
|
||||
extern int call_foo (void);
|
||||
extern foo_p get_foo_p (void);
|
||||
|
||||
foo_p my_foo_ptr = foo;
|
||||
foo_p foo_ptr = foo;
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
foo_p p;
|
||||
|
||||
p = get_foo ();
|
||||
if (p != foo)
|
||||
abort ();
|
||||
if ((*p) () != -30)
|
||||
if (call_foo () != -30)
|
||||
abort ();
|
||||
|
||||
p = get_foo_p ();
|
||||
@@ -52,12 +48,8 @@ main (void)
|
||||
|
||||
if (foo_ptr != foo)
|
||||
abort ();
|
||||
if (my_foo_ptr != foo)
|
||||
abort ();
|
||||
if ((*foo_ptr) () != -30)
|
||||
abort ();
|
||||
if ((*my_foo_ptr) () != -30)
|
||||
abort ();
|
||||
if (foo () != -30)
|
||||
abort ();
|
||||
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ extern int foo (void);
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
|
||||
foo_p foo_ptr = foo;
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
foo_p
|
||||
get_foo_p (void)
|
||||
@@ -12,8 +12,8 @@ get_foo_p (void)
|
||||
return foo_ptr;
|
||||
}
|
||||
|
||||
foo_p
|
||||
get_foo (void)
|
||||
int
|
||||
call_foo (void)
|
||||
{
|
||||
return foo;
|
||||
return foo ();
|
||||
}
|
||||
|
||||
+40
-74
@@ -23,10 +23,6 @@
|
||||
#define EW_(e, w, t) EW__(e, w, _##t)
|
||||
#define EW__(e, w, t) e##w##t
|
||||
|
||||
#define pldd_assert(name, exp) \
|
||||
typedef int __assert_##name[((exp) != 0) - 1]
|
||||
|
||||
|
||||
struct E(link_map)
|
||||
{
|
||||
EW(Addr) l_addr;
|
||||
@@ -39,12 +35,12 @@ struct E(link_map)
|
||||
EW(Addr) l_libname;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (l_addr, (offsetof (struct link_map, l_addr)
|
||||
== offsetof (struct E(link_map), l_addr)));
|
||||
pldd_assert (l_name, (offsetof (struct link_map, l_name)
|
||||
== offsetof (struct E(link_map), l_name)));
|
||||
pldd_assert (l_next, (offsetof (struct link_map, l_next)
|
||||
== offsetof (struct E(link_map), l_next)));
|
||||
_Static_assert (offsetof (struct link_map, l_addr)
|
||||
== offsetof (struct E(link_map), l_addr), "l_addr");
|
||||
_Static_assert (offsetof (struct link_map, l_name)
|
||||
== offsetof (struct E(link_map), l_name), "l_name");
|
||||
_Static_assert (offsetof (struct link_map, l_next)
|
||||
== offsetof (struct E(link_map), l_next), "l_next");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -54,10 +50,10 @@ struct E(libname_list)
|
||||
EW(Addr) next;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (name, (offsetof (struct libname_list, name)
|
||||
== offsetof (struct E(libname_list), name)));
|
||||
pldd_assert (next, (offsetof (struct libname_list, next)
|
||||
== offsetof (struct E(libname_list), next)));
|
||||
_Static_assert (offsetof (struct libname_list, name)
|
||||
== offsetof (struct E(libname_list), name), "name");
|
||||
_Static_assert (offsetof (struct libname_list, next)
|
||||
== offsetof (struct E(libname_list), next), "next");
|
||||
#endif
|
||||
|
||||
struct E(r_debug)
|
||||
@@ -69,16 +65,17 @@ struct E(r_debug)
|
||||
EW(Addr) r_map;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (r_version, (offsetof (struct r_debug, r_version)
|
||||
== offsetof (struct E(r_debug), r_version)));
|
||||
pldd_assert (r_map, (offsetof (struct r_debug, r_map)
|
||||
== offsetof (struct E(r_debug), r_map)));
|
||||
_Static_assert (offsetof (struct r_debug, r_version)
|
||||
== offsetof (struct E(r_debug), r_version), "r_version");
|
||||
_Static_assert (offsetof (struct r_debug, r_map)
|
||||
== offsetof (struct E(r_debug), r_map), "r_map");
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
|
||||
E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv,
|
||||
size_t auxv_size)
|
||||
{
|
||||
EW(Addr) phdr = 0;
|
||||
unsigned int phnum = 0;
|
||||
@@ -104,12 +101,9 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (phdr == 0 || phnum == 0 || phent == 0)
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot find program header of process"));
|
||||
|
||||
EW(Phdr) *p = alloca (phnum * phent);
|
||||
if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read program header"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EW(Phdr) *p = xmalloc (phnum * phent);
|
||||
if (pread (memfd, p, phnum * phent, phdr) != phnum * phent)
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read program header"));
|
||||
|
||||
/* Determine the load offset. We need this for interpreting the
|
||||
other program header entries so we do this in a separate loop.
|
||||
@@ -129,24 +123,18 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (p[i].p_type == PT_DYNAMIC)
|
||||
{
|
||||
EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
|
||||
if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
!= p[i].p_filesz)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read dynamic section"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read dynamic section"));
|
||||
|
||||
/* Search for the DT_DEBUG entry. */
|
||||
for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j)
|
||||
if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0)
|
||||
{
|
||||
struct E(r_debug) r;
|
||||
if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
|
||||
if (pread (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
|
||||
!= sizeof (r))
|
||||
{
|
||||
error (0, 0, gettext ("cannot read r_debug"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read r_debug"));
|
||||
|
||||
if (r.r_map != 0)
|
||||
{
|
||||
@@ -160,13 +148,10 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
}
|
||||
else if (p[i].p_type == PT_INTERP)
|
||||
{
|
||||
interp = alloca (p[i].p_filesz);
|
||||
if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
interp = xmalloc (p[i].p_filesz);
|
||||
if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
!= p[i].p_filesz)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read program interpreter"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read program interpreter"));
|
||||
}
|
||||
|
||||
if (list == 0)
|
||||
@@ -174,14 +159,16 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (interp == NULL)
|
||||
{
|
||||
// XXX check whether the executable itself is the loader
|
||||
return EXIT_FAILURE;
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// XXX perhaps try finding ld.so and _r_debug in it
|
||||
|
||||
return EXIT_FAILURE;
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free (p);
|
||||
free (interp);
|
||||
|
||||
/* Print the PID and program name first. */
|
||||
printf ("%lu:\t%s\n", (unsigned long int) pid, exe);
|
||||
|
||||
@@ -192,47 +179,27 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
do
|
||||
{
|
||||
struct E(link_map) m;
|
||||
if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m))
|
||||
{
|
||||
error (0, 0, gettext ("cannot read link map"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
if (pread (memfd, &m, sizeof (m), list) != sizeof (m))
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read link map"));
|
||||
|
||||
EW(Addr) name_offset = m.l_name;
|
||||
again:
|
||||
while (1)
|
||||
{
|
||||
ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset);
|
||||
ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length, name_offset);
|
||||
if (n == -1)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read object name"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read object name"));
|
||||
|
||||
if (memchr (tmpbuf.data, '\0', n) != NULL)
|
||||
break;
|
||||
|
||||
if (!scratch_buffer_grow (&tmpbuf))
|
||||
{
|
||||
error (0, 0, gettext ("cannot allocate buffer for object name"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
error (EXIT_FAILURE, 0,
|
||||
gettext ("cannot allocate buffer for object name"));
|
||||
}
|
||||
|
||||
if (((char *)tmpbuf.data)[0] == '\0' && name_offset == m.l_name
|
||||
&& m.l_libname != 0)
|
||||
{
|
||||
/* Try the l_libname element. */
|
||||
struct E(libname_list) ln;
|
||||
if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln))
|
||||
{
|
||||
name_offset = ln.name;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
/* The m.l_name and m.l_libname.name for loader linkmap points to same
|
||||
values (since BZ#387 fix). Trying to use l_libname name as the
|
||||
shared object name might lead to an infinite loop (BZ#18035). */
|
||||
|
||||
/* Skip over the executable. */
|
||||
if (((char *)tmpbuf.data)[0] != '\0')
|
||||
@@ -242,7 +209,6 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
}
|
||||
while (list != 0);
|
||||
|
||||
out:
|
||||
scratch_buffer_free (&tmpbuf);
|
||||
return status;
|
||||
}
|
||||
|
||||
+30
-34
@@ -17,23 +17,17 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloca.h>
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <argp.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libintl.h>
|
||||
#include <link.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <scratch_buffer.h>
|
||||
|
||||
@@ -76,14 +70,9 @@ static struct argp argp =
|
||||
options, parse_opt, args_doc, doc, NULL, more_help, NULL
|
||||
};
|
||||
|
||||
// File descriptor of /proc/*/mem file.
|
||||
static int memfd;
|
||||
|
||||
/* Name of the executable */
|
||||
static char *exe;
|
||||
|
||||
/* Local functions. */
|
||||
static int get_process_info (int dfd, long int pid);
|
||||
static int get_process_info (const char *exe, int dfd, long int pid);
|
||||
static void wait_for_ptrace_stop (long int pid);
|
||||
|
||||
|
||||
@@ -102,8 +91,10 @@ main (int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert (sizeof (pid_t) == sizeof (int)
|
||||
|| sizeof (pid_t) == sizeof (long int));
|
||||
_Static_assert (sizeof (pid_t) == sizeof (int)
|
||||
|| sizeof (pid_t) == sizeof (long int),
|
||||
"sizeof (pid_t) != sizeof (int) or sizeof (long int)");
|
||||
|
||||
char *endp;
|
||||
errno = 0;
|
||||
long int pid = strtol (argv[remaining], &endp, 10);
|
||||
@@ -119,25 +110,24 @@ main (int argc, char *argv[])
|
||||
if (dfd == -1)
|
||||
error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf);
|
||||
|
||||
struct scratch_buffer exebuf;
|
||||
scratch_buffer_init (&exebuf);
|
||||
/* Name of the executable */
|
||||
struct scratch_buffer exe;
|
||||
scratch_buffer_init (&exe);
|
||||
ssize_t nexe;
|
||||
while ((nexe = readlinkat (dfd, "exe",
|
||||
exebuf.data, exebuf.length)) == exebuf.length)
|
||||
exe.data, exe.length)) == exe.length)
|
||||
{
|
||||
if (!scratch_buffer_grow (&exebuf))
|
||||
if (!scratch_buffer_grow (&exe))
|
||||
{
|
||||
nexe = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nexe == -1)
|
||||
exe = (char *) "<program name undetermined>";
|
||||
/* Default stack allocation is at least 1024. */
|
||||
snprintf (exe.data, exe.length, "<program name undetermined>");
|
||||
else
|
||||
{
|
||||
exe = exebuf.data;
|
||||
exe[nexe] = '\0';
|
||||
}
|
||||
((char*)exe.data)[nexe] = '\0';
|
||||
|
||||
/* Stop all threads since otherwise the list of loaded modules might
|
||||
change while we are reading it. */
|
||||
@@ -155,8 +145,8 @@ main (int argc, char *argv[])
|
||||
error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"),
|
||||
buf);
|
||||
|
||||
struct dirent64 *d;
|
||||
while ((d = readdir64 (dir)) != NULL)
|
||||
struct dirent *d;
|
||||
while ((d = readdir (dir)) != NULL)
|
||||
{
|
||||
if (! isdigit (d->d_name[0]))
|
||||
continue;
|
||||
@@ -182,7 +172,7 @@ main (int argc, char *argv[])
|
||||
|
||||
wait_for_ptrace_stop (tid);
|
||||
|
||||
struct thread_list *newp = alloca (sizeof (*newp));
|
||||
struct thread_list *newp = xmalloc (sizeof (*newp));
|
||||
newp->tid = tid;
|
||||
newp->next = thread_list;
|
||||
thread_list = newp;
|
||||
@@ -190,17 +180,22 @@ main (int argc, char *argv[])
|
||||
|
||||
closedir (dir);
|
||||
|
||||
int status = get_process_info (dfd, pid);
|
||||
if (thread_list == NULL)
|
||||
error (EXIT_FAILURE, 0, gettext ("no valid %s/task entries"), buf);
|
||||
|
||||
int status = get_process_info (exe.data, dfd, pid);
|
||||
|
||||
assert (thread_list != NULL);
|
||||
do
|
||||
{
|
||||
ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL);
|
||||
struct thread_list *prev = thread_list;
|
||||
thread_list = thread_list->next;
|
||||
free (prev);
|
||||
}
|
||||
while (thread_list != NULL);
|
||||
|
||||
close (dfd);
|
||||
scratch_buffer_free (&exe);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -281,9 +276,10 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
|
||||
|
||||
|
||||
static int
|
||||
get_process_info (int dfd, long int pid)
|
||||
get_process_info (const char *exe, int dfd, long int pid)
|
||||
{
|
||||
memfd = openat (dfd, "mem", O_RDONLY);
|
||||
/* File descriptor of /proc/<pid>/mem file. */
|
||||
int memfd = openat (dfd, "mem", O_RDONLY);
|
||||
if (memfd == -1)
|
||||
goto no_info;
|
||||
|
||||
@@ -333,9 +329,9 @@ get_process_info (int dfd, long int pid)
|
||||
|
||||
int retval;
|
||||
if (e_ident[EI_CLASS] == ELFCLASS32)
|
||||
retval = find_maps32 (pid, auxv, auxv_size);
|
||||
retval = find_maps32 (exe, memfd, pid, auxv, auxv_size);
|
||||
else
|
||||
retval = find_maps64 (pid, auxv, auxv_size);
|
||||
retval = find_maps64 (exe, memfd, pid, auxv, auxv_size);
|
||||
|
||||
free (auxv);
|
||||
close (memfd);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Test unwinding through main.
|
||||
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 <unwind.h>
|
||||
#include <unistd.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
#if USE_PTHREADS
|
||||
# include <pthread.h>
|
||||
# include <error.h>
|
||||
#endif
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
callback (struct _Unwind_Context *ctx, void *arg)
|
||||
{
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
static void *
|
||||
func (void *a)
|
||||
{
|
||||
/* Arrange for this test to be killed if _Unwind_Backtrace runs into an
|
||||
endless loop. We cannot use the test driver because the complete
|
||||
call chain needs to be compiled with -funwind-tables so that
|
||||
_Unwind_Backtrace is able to reach the start routine. */
|
||||
alarm (DEFAULT_TIMEOUT);
|
||||
_Unwind_Backtrace (callback, 0);
|
||||
return a;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
#if USE_PTHREADS
|
||||
pthread_t thr;
|
||||
int rc = pthread_create (&thr, NULL, &func, NULL);
|
||||
if (rc)
|
||||
error (1, rc, "pthread_create");
|
||||
rc = pthread_join (thr, NULL);
|
||||
if (rc)
|
||||
error (1, rc, "pthread_join");
|
||||
#else
|
||||
func (NULL);
|
||||
#endif
|
||||
}
|
||||
+1
-1
@@ -128,7 +128,7 @@ internal_getgrouplist (const char *user, gid_t group, long int *size,
|
||||
|
||||
/* This is really only for debugging. */
|
||||
if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
|
||||
__libc_fatal ("illegal status in internal_getgrouplist");
|
||||
__libc_fatal ("Illegal status in internal_getgrouplist.\n");
|
||||
|
||||
/* For compatibility reason we will continue to look for more
|
||||
entries using the next service even though data has already
|
||||
|
||||
@@ -150,6 +150,8 @@ libpthread {
|
||||
__cthread_keycreate;
|
||||
__cthread_getspecific;
|
||||
__cthread_setspecific;
|
||||
__pthread_getspecific;
|
||||
__pthread_setspecific;
|
||||
__pthread_getattr_np;
|
||||
__pthread_attr_getstack;
|
||||
}
|
||||
|
||||
+3
-1
@@ -73,7 +73,7 @@ modules.so := $(addsuffix .so, $(modules))
|
||||
ifeq (yes,$(build-shared))
|
||||
tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
|
||||
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
|
||||
bug-iconv10 bug-iconv11 bug-iconv12
|
||||
bug-iconv10 bug-iconv11 bug-iconv12 bug-iconv14
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -316,6 +316,8 @@ $(objpfx)bug-iconv10.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv12.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv14.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
|
||||
$(objpfx)iconv-test.out: run-iconv-test.sh $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so)) \
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Assertion in ISO-2022-JP-3 due to two-character sequence (bug 27256).
|
||||
Copyright (C) 2021 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 <iconv.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* Use an escape sequence to return to the initial state. */
|
||||
static void
|
||||
with_escape_sequence (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D\e(B";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 3);
|
||||
TEST_COMPARE (inbuf - in, strlen (in) - 3);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Return to the initial shift state, producing the pending
|
||||
character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
/* Use an explicit flush to return to the initial state. */
|
||||
static void
|
||||
with_flush (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Flush the pending character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
with_escape_sequence ();
|
||||
with_flush ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+48
-19
@@ -67,23 +67,34 @@ enum
|
||||
CURRENT_SEL_MASK = 7 << 3
|
||||
};
|
||||
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the state
|
||||
also contains the last two bytes to be output, shifted by 6 bits, and a
|
||||
one-bit indicator whether they must be preceded by the shift sequence,
|
||||
in bit 22. */
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the
|
||||
state also contains the last two bytes to be output, shifted by 6
|
||||
bits, and a one-bit indicator whether they must be preceded by the
|
||||
shift sequence, in bit 22. During ISO-2022-JP-3 to UCS-4
|
||||
conversion, COUNT may also contain a non-zero pending wide
|
||||
character, shifted by six bits. This happens for certain inputs in
|
||||
JISX0213_1_2004_set and JISX0213_2_set if the second wide character
|
||||
in a combining sequence cannot be written because the buffer is
|
||||
full. */
|
||||
|
||||
/* Since this is a stateful encoding we have to provide code which resets
|
||||
the output state to the initial state. This has to be done during the
|
||||
flushing. */
|
||||
#define EMIT_SHIFT_TO_INIT \
|
||||
if ((data->__statep->__count & ~7) != ASCII_set) \
|
||||
if (data->__statep->__count != ASCII_set) \
|
||||
{ \
|
||||
if (FROM_DIRECTION) \
|
||||
{ \
|
||||
/* It's easy, we don't have to emit anything, we just reset the \
|
||||
state for the input. */ \
|
||||
data->__statep->__count &= 7; \
|
||||
data->__statep->__count |= ASCII_set; \
|
||||
if (__glibc_likely (outbuf + 4 <= outend)) \
|
||||
{ \
|
||||
/* Write out the last character. */ \
|
||||
*((uint32_t *) outbuf) = data->__statep->__count >> 6; \
|
||||
outbuf += sizeof (uint32_t); \
|
||||
data->__statep->__count = ASCII_set; \
|
||||
} \
|
||||
else \
|
||||
/* We don't have enough room in the output buffer. */ \
|
||||
status = __GCONV_FULL_OUTPUT; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -151,7 +162,21 @@ enum
|
||||
#define LOOPFCT FROM_LOOP
|
||||
#define BODY \
|
||||
{ \
|
||||
uint32_t ch = *inptr; \
|
||||
uint32_t ch; \
|
||||
\
|
||||
/* Output any pending character. */ \
|
||||
ch = set >> 6; \
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
/* Remove the pending character, but preserve state bits. */ \
|
||||
set &= (1 << 6) - 1; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise read the next input byte. */ \
|
||||
ch = *inptr; \
|
||||
\
|
||||
/* Recognize escape sequences. */ \
|
||||
if (__glibc_unlikely (ch == ESC)) \
|
||||
@@ -297,21 +322,25 @@ enum
|
||||
uint32_t u1 = __jisx0213_to_ucs_combining[ch - 1][0]; \
|
||||
uint32_t u2 = __jisx0213_to_ucs_combining[ch - 1][1]; \
|
||||
\
|
||||
inptr += 2; \
|
||||
\
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
\
|
||||
/* See whether we have room for two characters. */ \
|
||||
if (outptr + 8 <= outend) \
|
||||
if (outptr + 4 <= outend) \
|
||||
{ \
|
||||
inptr += 2; \
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
put32 (outptr, u2); \
|
||||
outptr += 4; \
|
||||
continue; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise store only the first character now, and \
|
||||
put the second one into the queue. */ \
|
||||
set |= u2 << 6; \
|
||||
/* Tell the caller why we terminate the loop. */ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
inptr += 2; \
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
#include <inet/arpa/inet.h>
|
||||
|
||||
#ifndef _ISOMAC
|
||||
extern int __inet_aton (const char *__cp, struct in_addr *__inp);
|
||||
libc_hidden_proto (__inet_aton)
|
||||
/* Variant of inet_aton which rejects trailing garbage. */
|
||||
extern int __inet_aton_exact (const char *__cp, struct in_addr *__inp);
|
||||
libc_hidden_proto (__inet_aton_exact)
|
||||
|
||||
libc_hidden_proto (inet_aton)
|
||||
libc_hidden_proto (inet_ntop)
|
||||
libc_hidden_proto (inet_pton)
|
||||
extern __typeof (inet_pton) __inet_pton;
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
# endif
|
||||
# define DT_1_SUPPORTED_MASK \
|
||||
(DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
|
||||
| DF_1_ORIGIN | DF_1_NODEFLIB)
|
||||
| DF_1_ORIGIN | DF_1_NODEFLIB | DF_1_PIE)
|
||||
|
||||
#endif /* !_ISOMAC */
|
||||
#endif /* elf.h */
|
||||
|
||||
@@ -216,6 +216,10 @@ struct link_map
|
||||
unsigned int boundndx;
|
||||
uint32_t enterexit;
|
||||
unsigned int flags;
|
||||
/* CONCURRENCY NOTE: This is used to guard the concurrent initialization
|
||||
of the relocation result across multiple threads. See the more
|
||||
detailed notes in elf/dl-runtime.c. */
|
||||
unsigned int init;
|
||||
} *l_reloc_result;
|
||||
|
||||
/* Pointer to the version information if available. */
|
||||
|
||||
+2
-1
@@ -98,7 +98,8 @@ enum __libc_message_action
|
||||
do_backtrace = 1 << 1 /* Backtrace. */
|
||||
};
|
||||
|
||||
/* Print out MESSAGE on the error output and abort. */
|
||||
/* Print out MESSAGE (which should end with a newline) on the error output
|
||||
and abort. */
|
||||
extern void __libc_fatal (const char *__message)
|
||||
__attribute__ ((__noreturn__));
|
||||
extern void __libc_message (enum __libc_message_action action,
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ 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 test-hnto-types
|
||||
tst-sockaddr test-hnto-types tst-if_index-long
|
||||
|
||||
# tst-deadline must be linked statically so that we can access
|
||||
# internal functions.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Check for descriptor leak in if_nametoindex with a long interface name.
|
||||
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/>. */
|
||||
|
||||
/* This test checks for a descriptor leak in case of a long interface
|
||||
name (CVE-2018-19591, bug 23927). */
|
||||
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/descriptors.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct support_descriptors *descrs = support_descriptors_list ();
|
||||
|
||||
/* Prepare a name which is just as long as required for trigging the
|
||||
bug. */
|
||||
char name[IFNAMSIZ + 1];
|
||||
memset (name, 'A', IFNAMSIZ);
|
||||
name[IFNAMSIZ] = '\0';
|
||||
TEST_COMPARE (strlen (name), IFNAMSIZ);
|
||||
struct ifreq ifr;
|
||||
TEST_COMPARE (strlen (name), sizeof (ifr.ifr_name));
|
||||
|
||||
/* Test directly via if_nametoindex. */
|
||||
TEST_COMPARE (if_nametoindex (name), 0);
|
||||
TEST_COMPARE (errno, ENODEV);
|
||||
support_descriptors_check (descrs);
|
||||
|
||||
/* Same test via getaddrinfo. */
|
||||
char *host = xasprintf ("fea0::%%%s", name);
|
||||
struct addrinfo hints = { .ai_flags = AI_NUMERICHOST, };
|
||||
struct addrinfo *ai;
|
||||
TEST_COMPARE (getaddrinfo (host, NULL, &hints, &ai), EAI_NONAME);
|
||||
support_descriptors_check (descrs);
|
||||
|
||||
support_descriptors_free (descrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+1
-1
@@ -631,7 +631,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
|
||||
int ret = __asprintf (&xdirname, "%s/%s", cwd, dirname);
|
||||
free (cwd);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
goto return_untranslated;
|
||||
dirname = xdirname;
|
||||
}
|
||||
#ifndef IN_LIBGLOCALE
|
||||
|
||||
@@ -73,11 +73,6 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
|
||||
tst-fts tst-fts-lfs tst-open-tmpfile \
|
||||
tst-copy_file_range tst-getcwd-abspath \
|
||||
|
||||
# This test includes the compat implementation of copy_file_range,
|
||||
# which uses internal, unexported libc functions.
|
||||
tests-static += tst-copy_file_range-compat
|
||||
tests-internal += tst-copy_file_range-compat
|
||||
|
||||
# Likewise for statx, but we do not need static linking here.
|
||||
tests-internal += tst-statx
|
||||
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/* Emulation of copy_file_range.
|
||||
Copyright (C) 2017-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/>. */
|
||||
|
||||
/* The following macros should be defined before including this
|
||||
file:
|
||||
|
||||
COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
|
||||
COPY_FILE_RANGE Name of the function to define. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
COPY_FILE_RANGE_DECL
|
||||
ssize_t
|
||||
COPY_FILE_RANGE (int infd, __off64_t *pinoff,
|
||||
int outfd, __off64_t *poutoff,
|
||||
size_t length, unsigned int flags)
|
||||
{
|
||||
if (flags != 0)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
struct stat64 instat;
|
||||
struct stat64 outstat;
|
||||
if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
|
||||
return -1;
|
||||
if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
|
||||
{
|
||||
__set_errno (EISDIR);
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
|
||||
{
|
||||
/* We need a regular input file so that the we can seek
|
||||
backwards in case of a write failure. */
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (instat.st_dev != outstat.st_dev)
|
||||
{
|
||||
/* Cross-device copies are not supported. */
|
||||
__set_errno (EXDEV);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The output descriptor must not have O_APPEND set. */
|
||||
{
|
||||
int flags = __fcntl (outfd, F_GETFL);
|
||||
if (flags & O_APPEND)
|
||||
{
|
||||
__set_errno (EBADF);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid an overflow in the result. */
|
||||
if (length > SSIZE_MAX)
|
||||
length = SSIZE_MAX;
|
||||
|
||||
/* Main copying loop. The buffer size is arbitrary and is a
|
||||
trade-off between stack size consumption, cache usage, and
|
||||
amortization of system call overhead. */
|
||||
size_t copied = 0;
|
||||
char buf[8192];
|
||||
while (length > 0)
|
||||
{
|
||||
size_t to_read = length;
|
||||
if (to_read > sizeof (buf))
|
||||
to_read = sizeof (buf);
|
||||
|
||||
/* Fill the buffer. */
|
||||
ssize_t read_count;
|
||||
if (pinoff == NULL)
|
||||
read_count = read (infd, buf, to_read);
|
||||
else
|
||||
read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
|
||||
if (read_count == 0)
|
||||
/* End of file reached prematurely. */
|
||||
return copied;
|
||||
if (read_count < 0)
|
||||
{
|
||||
if (copied > 0)
|
||||
/* Report the number of bytes copied so far. */
|
||||
return copied;
|
||||
return -1;
|
||||
}
|
||||
if (pinoff != NULL)
|
||||
*pinoff += read_count;
|
||||
|
||||
/* Write the buffer part which was read to the destination. */
|
||||
char *end = buf + read_count;
|
||||
for (char *p = buf; p < end; )
|
||||
{
|
||||
ssize_t write_count;
|
||||
if (poutoff == NULL)
|
||||
write_count = write (outfd, p, end - p);
|
||||
else
|
||||
write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
|
||||
if (write_count < 0)
|
||||
{
|
||||
/* Adjust the input read position to match what we have
|
||||
written, so that the caller can pick up after the
|
||||
error. */
|
||||
size_t written = p - buf;
|
||||
/* NB: This needs to be signed so that we can form the
|
||||
negative value below. */
|
||||
ssize_t overread = read_count - written;
|
||||
if (pinoff == NULL)
|
||||
{
|
||||
if (overread > 0)
|
||||
{
|
||||
/* We are on an error recovery path, so we
|
||||
cannot deal with failure here. */
|
||||
int save_errno = errno;
|
||||
(void) __libc_lseek64 (infd, -overread, SEEK_CUR);
|
||||
__set_errno (save_errno);
|
||||
}
|
||||
}
|
||||
else /* pinoff != NULL */
|
||||
*pinoff -= overread;
|
||||
|
||||
if (copied + written > 0)
|
||||
/* Report the number of bytes copied so far. */
|
||||
return copied + written;
|
||||
return -1;
|
||||
}
|
||||
p += write_count;
|
||||
if (poutoff != NULL)
|
||||
*poutoff += write_count;
|
||||
} /* Write loop. */
|
||||
|
||||
copied += read_count;
|
||||
length -= read_count;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
+13
-5
@@ -1,5 +1,5 @@
|
||||
/* Generic implementation of copy_file_range.
|
||||
Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
||||
/* Stub implementation of copy_file_range.
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
@@ -16,7 +16,15 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define COPY_FILE_RANGE_DECL
|
||||
#define COPY_FILE_RANGE copy_file_range
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <io/copy_file_range-compat.c>
|
||||
ssize_t
|
||||
copy_file_range (int infd, __off64_t *pinoff,
|
||||
int outfd, __off64_t *poutoff,
|
||||
size_t length, unsigned int flags)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (copy_file_range)
|
||||
|
||||
+10
-547
@@ -1,5 +1,5 @@
|
||||
/* Tests for copy_file_range.
|
||||
Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
@@ -20,22 +20,15 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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>
|
||||
#ifdef CLONE_NEWNS
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
/* Boolean flags which indicate whether to use pointers with explicit
|
||||
output flags. */
|
||||
@@ -49,10 +42,6 @@ static int infd;
|
||||
static char *outfile;
|
||||
static int outfd;
|
||||
|
||||
/* Like the above, but on a different file system. xdevfile can be
|
||||
NULL if no suitable file system has been found. */
|
||||
static char *xdevfile;
|
||||
|
||||
/* Input and output offsets. Set according to do_inoff and do_outoff
|
||||
before the test. The offsets themselves are always set to
|
||||
zero. */
|
||||
@@ -61,13 +50,10 @@ static off64_t *pinoff;
|
||||
static off64_t outoff;
|
||||
static off64_t *poutoff;
|
||||
|
||||
/* These are a collection of copy sizes used in tests. The selection
|
||||
takes into account that the fallback implementation uses an
|
||||
internal buffer of 8192 bytes. */
|
||||
/* These are a collection of copy sizes used in tests. */
|
||||
enum { maximum_size = 99999 };
|
||||
static const int typical_sizes[] =
|
||||
{ 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
|
||||
maximum_size };
|
||||
{ 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, maximum_size };
|
||||
|
||||
/* The random contents of this array can be used as a pattern to check
|
||||
for correct write operations. */
|
||||
@@ -76,101 +62,6 @@ static unsigned char random_data[maximum_size];
|
||||
/* The size chosen by the test harness. */
|
||||
static int current_size;
|
||||
|
||||
/* Maximum writable file offset. Updated by find_maximum_offset
|
||||
below. */
|
||||
static off64_t maximum_offset;
|
||||
|
||||
/* Error code when crossing the offset. */
|
||||
static int maximum_offset_errno;
|
||||
|
||||
/* If true: Writes which cross the limit will fail. If false: Writes
|
||||
which cross the limit will result in a partial write. */
|
||||
static bool maximum_offset_hard_limit;
|
||||
|
||||
/* Fills maximum_offset etc. above. Truncates outfd as a side
|
||||
effect. */
|
||||
static void
|
||||
find_maximum_offset (void)
|
||||
{
|
||||
xftruncate (outfd, 0);
|
||||
if (maximum_offset != 0)
|
||||
return;
|
||||
|
||||
uint64_t upper = -1;
|
||||
upper >>= 1; /* Maximum of off64_t. */
|
||||
TEST_VERIFY ((off64_t) upper > 0);
|
||||
TEST_VERIFY ((off64_t) (upper + 1) < 0);
|
||||
if (lseek64 (outfd, upper, SEEK_SET) >= 0)
|
||||
{
|
||||
if (write (outfd, "", 1) == 1)
|
||||
FAIL_EXIT1 ("created a file larger than the off64_t range");
|
||||
}
|
||||
|
||||
uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
|
||||
/* Loop invariant: writing at lower succeeds, writing at upper fails. */
|
||||
while (lower + 1 < upper)
|
||||
{
|
||||
uint64_t middle = (lower + upper) / 2;
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
|
||||
", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
|
||||
xftruncate (outfd, 0);
|
||||
if (lseek64 (outfd, middle, SEEK_SET) >= 0
|
||||
&& write (outfd, "", 1) == 1)
|
||||
lower = middle;
|
||||
else
|
||||
upper = middle;
|
||||
}
|
||||
TEST_VERIFY (lower + 1 == upper);
|
||||
maximum_offset = lower;
|
||||
printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
|
||||
lower, lower);
|
||||
|
||||
/* Check that writing at the valid offset actually works. */
|
||||
xftruncate (outfd, 0);
|
||||
xlseek (outfd, lower, SEEK_SET);
|
||||
TEST_COMPARE (write (outfd, "", 1), 1);
|
||||
|
||||
/* Cross the boundary with a two-byte write. This can either result
|
||||
in a short write, or a failure. */
|
||||
xlseek (outfd, lower, SEEK_SET);
|
||||
ssize_t ret = write (outfd, " ", 2);
|
||||
if (ret < 0)
|
||||
{
|
||||
maximum_offset_errno = errno;
|
||||
maximum_offset_hard_limit = true;
|
||||
}
|
||||
else
|
||||
maximum_offset_hard_limit = false;
|
||||
|
||||
/* Check that writing at the next offset actually fails. This also
|
||||
obtains the expected errno value. */
|
||||
xftruncate (outfd, 0);
|
||||
const char *action;
|
||||
if (lseek64 (outfd, lower + 1, SEEK_SET) != 0)
|
||||
{
|
||||
if (write (outfd, "", 1) != -1)
|
||||
FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded",
|
||||
lower + 1);
|
||||
action = "writing";
|
||||
int errno_copy = errno;
|
||||
if (maximum_offset_hard_limit)
|
||||
TEST_COMPARE (errno_copy, maximum_offset_errno);
|
||||
else
|
||||
maximum_offset_errno = errno_copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
action = "seeking";
|
||||
maximum_offset_errno = errno;
|
||||
}
|
||||
printf ("info: %s out of range fails with %m (%d)\n",
|
||||
action, maximum_offset_errno);
|
||||
|
||||
xftruncate (outfd, 0);
|
||||
xlseek (outfd, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
/* Perform a copy of a file. */
|
||||
static void
|
||||
simple_file_copy (void)
|
||||
@@ -247,390 +138,6 @@ simple_file_copy (void)
|
||||
free (bytes);
|
||||
}
|
||||
|
||||
/* Test that reading from a pipe willfails. */
|
||||
static void
|
||||
pipe_as_source (void)
|
||||
{
|
||||
int pipefds[2];
|
||||
xpipe (pipefds);
|
||||
|
||||
for (int length = 0; length < 2; ++length)
|
||||
{
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: length=%d\n", __func__, length);
|
||||
|
||||
/* Make sure that there is something to copy in the pipe. */
|
||||
xwrite (pipefds[1], "@", 1);
|
||||
|
||||
TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
|
||||
length, 0), -1);
|
||||
/* Linux 4.10 and later return EINVAL. Older kernels return
|
||||
EXDEV. */
|
||||
TEST_VERIFY (errno == EINVAL || errno == EXDEV);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
TEST_COMPARE (outoff, 0);
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
|
||||
|
||||
/* Make sure that nothing was read. */
|
||||
char buf = 'A';
|
||||
TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
|
||||
TEST_COMPARE (buf, '@');
|
||||
}
|
||||
|
||||
xclose (pipefds[0]);
|
||||
xclose (pipefds[1]);
|
||||
}
|
||||
|
||||
/* Test that writing to a pipe fails. */
|
||||
static void
|
||||
pipe_as_destination (void)
|
||||
{
|
||||
/* Make sure that there is something to read in the input file. */
|
||||
xwrite (infd, "abc", 3);
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
int pipefds[2];
|
||||
xpipe (pipefds);
|
||||
|
||||
for (int length = 0; length < 2; ++length)
|
||||
{
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: length=%d\n", __func__, length);
|
||||
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
|
||||
length, 0), -1);
|
||||
/* Linux 4.10 and later return EINVAL. Older kernels return
|
||||
EXDEV. */
|
||||
TEST_VERIFY (errno == EINVAL || errno == EXDEV);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
TEST_COMPARE (outoff, 0);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
|
||||
/* Make sure that nothing was written. */
|
||||
struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
|
||||
TEST_COMPARE (poll (&pollfd, 1, 0), 0);
|
||||
}
|
||||
|
||||
xclose (pipefds[0]);
|
||||
xclose (pipefds[1]);
|
||||
}
|
||||
|
||||
/* Test a write failure after (potentially) writing some bytes.
|
||||
Failure occurs near the start of the buffer. */
|
||||
static void
|
||||
delayed_write_failure_beginning (void)
|
||||
{
|
||||
/* We need to write something to provoke the error. */
|
||||
if (current_size == 0)
|
||||
return;
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
/* Write failure near the start. The actual error code varies among
|
||||
file systems. */
|
||||
find_maximum_offset ();
|
||||
off64_t where = maximum_offset;
|
||||
|
||||
if (current_size == 1)
|
||||
++where;
|
||||
outoff = where;
|
||||
if (do_outoff)
|
||||
xlseek (outfd, 1, SEEK_SET);
|
||||
else
|
||||
xlseek (outfd, where, SEEK_SET);
|
||||
if (maximum_offset_hard_limit || where > maximum_offset)
|
||||
{
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
if (do_outoff)
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
|
||||
else
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
|
||||
TEST_COMPARE (outoff, where);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The offset is not a hard limit. This means we write one
|
||||
byte. */
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), 1);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
}
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
|
||||
TEST_COMPARE (outoff, where + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1);
|
||||
TEST_COMPARE (outoff, where);
|
||||
}
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, where + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test a write failure after (potentially) writing some bytes.
|
||||
Failure occurs near the end of the buffer. */
|
||||
static void
|
||||
delayed_write_failure_end (void)
|
||||
{
|
||||
if (current_size <= 1)
|
||||
/* This would be same as the first test because there is not
|
||||
enough data to write to make a difference. */
|
||||
return;
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
find_maximum_offset ();
|
||||
off64_t where = maximum_offset - current_size + 1;
|
||||
if (current_size == sizeof (random_data))
|
||||
/* Otherwise we do not reach the non-writable byte. */
|
||||
++where;
|
||||
outoff = where;
|
||||
if (do_outoff)
|
||||
xlseek (outfd, 1, SEEK_SET);
|
||||
else
|
||||
xlseek (outfd, where, SEEK_SET);
|
||||
ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The first copy succeeded. This happens in the emulation
|
||||
because the internal buffer of limited size does not
|
||||
necessarily cross the off64_t boundary on the first write
|
||||
operation. */
|
||||
if (test_verbose > 0)
|
||||
printf ("info: copy_file_range (%zu) returned %zd\n",
|
||||
sizeof (random_data), ret);
|
||||
TEST_VERIFY (ret > 0);
|
||||
TEST_VERIFY (ret < maximum_size);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, where + ret);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, ret);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
|
||||
|
||||
char *buffer = xmalloc (ret);
|
||||
TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
|
||||
TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
|
||||
free (buffer);
|
||||
|
||||
/* The second copy fails. */
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test a write failure across devices. */
|
||||
static void
|
||||
cross_device_failure (void)
|
||||
{
|
||||
if (xdevfile == NULL)
|
||||
/* Subtest not supported due to missing cross-device file. */
|
||||
return;
|
||||
|
||||
/* We need something to write. */
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
|
||||
current_size, 0), -1);
|
||||
TEST_COMPARE (errno, EXDEV);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
struct stat64 st;
|
||||
xfstat (xdevfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
|
||||
xclose (xdevfd);
|
||||
}
|
||||
|
||||
/* Try to exercise ENOSPC behavior with a tempfs file system (so that
|
||||
we do not have to fill up a regular file system to get the error).
|
||||
This function runs in a subprocess, so that we do not change the
|
||||
mount namespace of the actual test process. */
|
||||
static void
|
||||
enospc_failure_1 (void *closure)
|
||||
{
|
||||
#ifdef CLONE_NEWNS
|
||||
support_become_root ();
|
||||
|
||||
/* Make sure that we do not alter the file system mounts of the
|
||||
parents. */
|
||||
if (! support_enter_mount_namespace ())
|
||||
{
|
||||
printf ("warning: ENOSPC test skipped\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *mountpoint = closure;
|
||||
if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
|
||||
"size=500k") != 0)
|
||||
{
|
||||
printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The source file must reside on the same file system. */
|
||||
char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
|
||||
int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
|
||||
xwrite (intmpfsfd, random_data, sizeof (random_data));
|
||||
xlseek (intmpfsfd, 1, SEEK_SET);
|
||||
inoff = 1;
|
||||
|
||||
char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
|
||||
int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
|
||||
|
||||
/* Fill the file with data until ENOSPC is reached. */
|
||||
while (true)
|
||||
{
|
||||
ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
|
||||
if (ret < 0 && errno != ENOSPC)
|
||||
FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
|
||||
if (ret < sizeof (random_data))
|
||||
break;
|
||||
}
|
||||
TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
|
||||
TEST_COMPARE (errno, ENOSPC);
|
||||
off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
|
||||
TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
|
||||
|
||||
/* Constructed the expected file contents. */
|
||||
char *expected = xmalloc (maxsize);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
|
||||
/* Go back a little, so some bytes can be written. */
|
||||
enum { offset = 20000 };
|
||||
TEST_VERIFY_EXIT (offset < maxsize);
|
||||
TEST_VERIFY_EXIT (offset < sizeof (random_data));
|
||||
memcpy (expected + maxsize - offset, random_data + 1, offset);
|
||||
|
||||
if (do_outoff)
|
||||
{
|
||||
outoff = maxsize - offset;
|
||||
xlseek (outtmpfsfd, 2, SEEK_SET);
|
||||
}
|
||||
else
|
||||
xlseek (outtmpfsfd, -offset, SEEK_CUR);
|
||||
|
||||
/* First call is expected to succeed because we made room for some
|
||||
bytes. */
|
||||
TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
|
||||
maximum_size, 0), offset);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1 + offset);
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (outoff, maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
|
||||
struct stat64 st;
|
||||
xfstat (outtmpfsfd, &st);
|
||||
TEST_COMPARE (st.st_size, maxsize);
|
||||
char *actual = xmalloc (st.st_size);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
|
||||
TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
|
||||
|
||||
/* Second call should fail with ENOSPC. */
|
||||
TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
|
||||
maximum_size, 0), -1);
|
||||
TEST_COMPARE (errno, ENOSPC);
|
||||
|
||||
/* Offsets should be unchanged. */
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1 + offset);
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (outoff, maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
|
||||
TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
|
||||
|
||||
free (actual);
|
||||
free (expected);
|
||||
|
||||
xclose (intmpfsfd);
|
||||
xclose (outtmpfsfd);
|
||||
free (intmpfsfile);
|
||||
free (outtmpfsfile);
|
||||
|
||||
#else /* !CLONE_NEWNS */
|
||||
puts ("warning: ENOSPC test skipped (no mount namespaces)");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Call enospc_failure_1 in a subprocess. */
|
||||
static void
|
||||
enospc_failure (void)
|
||||
{
|
||||
char *mountpoint
|
||||
= support_create_temp_directory ("tst-copy_file_range-enospc-");
|
||||
support_isolate_in_subprocess (enospc_failure_1, mountpoint);
|
||||
free (mountpoint);
|
||||
}
|
||||
|
||||
/* The target file descriptor must have O_APPEND enabled. */
|
||||
static void
|
||||
oappend_failure (void)
|
||||
{
|
||||
/* Add data, to make sure we do not fail because there is
|
||||
insufficient input data. */
|
||||
xwrite (infd, random_data, current_size);
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
xclose (outfd);
|
||||
outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
current_size, 0), -1);
|
||||
TEST_COMPARE (errno, EBADF);
|
||||
}
|
||||
|
||||
/* Test that a short input file results in a shortened copy. */
|
||||
static void
|
||||
short_copy (void)
|
||||
@@ -721,14 +228,6 @@ struct test_case
|
||||
static struct test_case tests[] =
|
||||
{
|
||||
{ "simple_file_copy", simple_file_copy, .sizes = true },
|
||||
{ "pipe_as_source", pipe_as_source, },
|
||||
{ "pipe_as_destination", pipe_as_destination, },
|
||||
{ "delayed_write_failure_beginning", delayed_write_failure_beginning,
|
||||
.sizes = true },
|
||||
{ "delayed_write_failure_end", delayed_write_failure_end, .sizes = true },
|
||||
{ "cross_device_failure", cross_device_failure, .sizes = true },
|
||||
{ "enospc_failure", enospc_failure, },
|
||||
{ "oappend_failure", oappend_failure, .sizes = true },
|
||||
{ "short_copy", short_copy, .sizes = true },
|
||||
};
|
||||
|
||||
@@ -739,53 +238,18 @@ do_test (void)
|
||||
*p = rand () >> 24;
|
||||
|
||||
infd = create_temp_file ("tst-copy_file_range-in-", &infile);
|
||||
xclose (create_temp_file ("tst-copy_file_range-out-", &outfile));
|
||||
|
||||
/* Try to find a different directory from the default input/output
|
||||
file. */
|
||||
outfd = create_temp_file ("tst-copy_file_range-out-", &outfile);
|
||||
{
|
||||
struct stat64 instat;
|
||||
xfstat (infd, &instat);
|
||||
static const char *const candidates[] =
|
||||
{ NULL, "/var/tmp", "/dev/shm" };
|
||||
for (const char *const *c = candidates; c < array_end (candidates); ++c)
|
||||
ssize_t ret = copy_file_range (infd, NULL, outfd, NULL, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
const char *path = *c;
|
||||
char *to_free = NULL;
|
||||
if (path == NULL)
|
||||
{
|
||||
to_free = xreadlink ("/proc/self/exe");
|
||||
path = dirname (to_free);
|
||||
}
|
||||
|
||||
struct stat64 cstat;
|
||||
xstat (path, &cstat);
|
||||
if (cstat.st_dev == instat.st_dev)
|
||||
{
|
||||
free (to_free);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf ("info: using alternate temporary files directory: %s\n", path);
|
||||
xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
|
||||
free (to_free);
|
||||
break;
|
||||
if (errno == ENOSYS)
|
||||
FAIL_UNSUPPORTED ("copy_file_range is not support on this system");
|
||||
FAIL_EXIT1 ("copy_file_range probing call: %m");
|
||||
}
|
||||
if (xdevfile != NULL)
|
||||
{
|
||||
int xdevfd = mkstemp (xdevfile);
|
||||
if (xdevfd < 0)
|
||||
FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
|
||||
struct stat64 xdevst;
|
||||
xfstat (xdevfd, &xdevst);
|
||||
TEST_VERIFY (xdevst.st_dev != instat.st_dev);
|
||||
add_temp_file (xdevfile);
|
||||
xclose (xdevfd);
|
||||
}
|
||||
else
|
||||
puts ("warning: no alternate directory on different file system found");
|
||||
}
|
||||
xclose (infd);
|
||||
xclose (outfd);
|
||||
|
||||
for (do_inoff = 0; do_inoff < 2; ++do_inoff)
|
||||
for (do_outoff = 0; do_outoff < 2; ++do_outoff)
|
||||
@@ -827,7 +291,6 @@ do_test (void)
|
||||
|
||||
free (infile);
|
||||
free (outfile);
|
||||
free (xdevfile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+16
-2
@@ -64,7 +64,8 @@ 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-bz22415 tst-fgetc-after-eof
|
||||
tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
|
||||
tst-wfile-sync
|
||||
|
||||
tests-internal = tst-vtables tst-vtables-interposed tst-readline
|
||||
|
||||
@@ -72,6 +73,9 @@ ifeq (yes,$(build-shared))
|
||||
# Add test-fopenloc only if shared library is enabled since it depends on
|
||||
# shared localedata objects.
|
||||
tests += tst-fopenloc
|
||||
# Add tst-bz24228 only if shared library is enabled since it can never meet its
|
||||
# objective with static linking because the relevant code just is not there.
|
||||
tests += tst-bz24228
|
||||
endif
|
||||
test-srcs = test-freopen
|
||||
|
||||
@@ -152,11 +156,14 @@ CFLAGS-oldtmpfile.c += -fexceptions
|
||||
|
||||
CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\"
|
||||
|
||||
LDFLAGS-tst-bz24228 = -Wl,--version-script=tst-bz24228.map
|
||||
|
||||
tst_wprintf2-ARGS = "Some Text"
|
||||
|
||||
test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace
|
||||
tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace
|
||||
tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace
|
||||
tst-bz24228-ENV = MALLOC_TRACE=$(objpfx)tst-bz24228.mtrace
|
||||
|
||||
generated += test-fmemopen.mtrace test-fmemopen.check
|
||||
generated += tst-fopenloc.mtrace tst-fopenloc.check
|
||||
@@ -165,6 +172,7 @@ generated += tst-bz22415.mtrace tst-bz22415.check
|
||||
aux := fileops genops stdfiles stdio strops
|
||||
|
||||
ifeq ($(build-shared),yes)
|
||||
generated += tst-bz24228.mtrace tst-bz24228.check
|
||||
aux += oldfileops oldstdfiles
|
||||
endif
|
||||
|
||||
@@ -179,7 +187,8 @@ tests-special += $(objpfx)test-freopen.out $(objpfx)test-fmemopen-mem.out \
|
||||
ifeq (yes,$(build-shared))
|
||||
# Run tst-fopenloc-cmp.out and tst-openloc-mem.out only if shared
|
||||
# library is enabled since they depend on tst-fopenloc.out.
|
||||
tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out
|
||||
tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out \
|
||||
$(objpfx)tst-bz24228-mem.out
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -207,6 +216,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
|
||||
@@ -230,3 +240,7 @@ $(objpfx)tst-fopenloc-mem.out: $(objpfx)tst-fopenloc.out
|
||||
$(objpfx)tst-bz22415-mem.out: $(objpfx)tst-bz22415.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-bz22415.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-bz24228-mem.out: $(objpfx)tst-bz24228.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-bz24228.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+12
-4
@@ -789,9 +789,16 @@ _IO_unbuffer_all (void)
|
||||
|
||||
for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain)
|
||||
{
|
||||
int legacy = 0;
|
||||
|
||||
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
|
||||
if (__glibc_unlikely (_IO_vtable_offset (fp) != 0))
|
||||
legacy = 1;
|
||||
#endif
|
||||
|
||||
if (! (fp->_flags & _IO_UNBUFFERED)
|
||||
/* Iff stream is un-orientated, it wasn't used. */
|
||||
&& fp->_mode != 0)
|
||||
&& (legacy || fp->_mode != 0))
|
||||
{
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
int cnt;
|
||||
@@ -805,7 +812,7 @@ _IO_unbuffer_all (void)
|
||||
__sched_yield ();
|
||||
#endif
|
||||
|
||||
if (! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
|
||||
if (! legacy && ! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
|
||||
{
|
||||
fp->_flags |= _IO_USER_BUF;
|
||||
|
||||
@@ -816,7 +823,7 @@ _IO_unbuffer_all (void)
|
||||
|
||||
_IO_SETBUF (fp, NULL, 0);
|
||||
|
||||
if (fp->_mode > 0)
|
||||
if (! legacy && fp->_mode > 0)
|
||||
_IO_wsetb (fp, NULL, NULL, 0);
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
@@ -827,7 +834,8 @@ _IO_unbuffer_all (void)
|
||||
|
||||
/* Make sure that never again the wide char functions can be
|
||||
used. */
|
||||
fp->_mode = -1;
|
||||
if (! legacy)
|
||||
fp->_mode = -1;
|
||||
}
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
|
||||
@@ -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,29 @@
|
||||
/* BZ #24228 check for memory corruption in legacy libio
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <mcheck.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,5 @@
|
||||
# Hide the symbol from libc.so.6 to switch to the libio/oldfileops.c
|
||||
# implementation when it is available for the architecture.
|
||||
{
|
||||
local: _IO_stdin_used;
|
||||
};
|
||||
@@ -232,5 +232,6 @@ do_test (void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT 100
|
||||
#define PREPARE prepare
|
||||
#include <support/test-driver.c>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Test that _IO_wfile_sync does not crash (bug 20568).
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<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.
|
||||
+3
-2
@@ -508,11 +508,12 @@ _IO_wfile_sync (FILE *fp)
|
||||
generate the wide characters up to the current reading
|
||||
position. */
|
||||
int nread;
|
||||
|
||||
size_t wnread = (fp->_wide_data->_IO_read_ptr
|
||||
- fp->_wide_data->_IO_read_base);
|
||||
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
|
||||
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
|
||||
fp->_IO_read_base,
|
||||
fp->_IO_read_end, delta);
|
||||
fp->_IO_read_end, wnread);
|
||||
fp->_IO_read_ptr = fp->_IO_read_base + nread;
|
||||
delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
|
||||
}
|
||||
|
||||
@@ -14946,7 +14946,9 @@ am_pm "<U5348><U524D>";"<U5348><U5F8C>"
|
||||
|
||||
t_fmt_ampm "%p%I<U6642>%M<U5206>%S<U79D2>"
|
||||
|
||||
era "+:2:1990//01//01:+*:<U5E73><U6210>:%EC%Ey<U5E74>";/
|
||||
era "+:2:2020//01//01:+*:<U4EE4><U548C>:%EC%Ey<U5E74>";/
|
||||
"+:1:2019//05//01:2019//12//31:<U4EE4><U548C>:%EC<U5143><U5E74>";/
|
||||
"+:2:1990//01//01:2019//04//30:<U5E73><U6210>:%EC%Ey<U5E74>";/
|
||||
"+:1:1989//01//08:1989//12//31:<U5E73><U6210>:%EC<U5143><U5E74>";/
|
||||
"+:2:1927//01//01:1989//01//07:<U662D><U548C>:%EC%Ey<U5E74>";/
|
||||
"+:1:1926//12//25:1926//12//31:<U662D><U548C>:%EC<U5143><U5E74>";/
|
||||
|
||||
@@ -70,11 +70,11 @@ copy "da_DK"
|
||||
END LC_NUMERIC
|
||||
|
||||
LC_TIME
|
||||
abday "sab";"ata";/
|
||||
abday "sap";"ata";/
|
||||
"mar";"pin";/
|
||||
"sis";"tal";/
|
||||
"arf"
|
||||
day "sabaat";/
|
||||
day "sapaat";/
|
||||
"ataasinngorneq";/
|
||||
"marlunngorneq";/
|
||||
"pingasunngorneq";/
|
||||
|
||||
+4
-1
@@ -38,6 +38,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
|
||||
tst-malloc_info \
|
||||
tst-malloc-too-large \
|
||||
tst-malloc-stats-cancellation \
|
||||
tst-tcfree1 tst-tcfree2 tst-tcfree3 \
|
||||
|
||||
tests-static := \
|
||||
tst-interpose-static-nothread \
|
||||
@@ -53,7 +54,7 @@ tests-internal += \
|
||||
tst-dynarray-at-fail \
|
||||
|
||||
ifneq (no,$(have-tunables))
|
||||
tests += tst-malloc-usable-tunables
|
||||
tests += tst-malloc-usable-tunables tst-mxfast
|
||||
tests-static += tst-malloc-usable-static-tunables
|
||||
endif
|
||||
|
||||
@@ -195,6 +196,8 @@ tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
|
||||
tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
|
||||
tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
|
||||
|
||||
tst-mxfast-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=0:glibc.malloc.mxfast=0
|
||||
|
||||
ifeq ($(experimental-malloc),yes)
|
||||
CPPFLAGS-malloc.c += -DUSE_TCACHE=1
|
||||
else
|
||||
|
||||
@@ -237,6 +237,7 @@ TUNABLE_CALLBACK_FNDECL (set_tcache_max, size_t)
|
||||
TUNABLE_CALLBACK_FNDECL (set_tcache_count, size_t)
|
||||
TUNABLE_CALLBACK_FNDECL (set_tcache_unsorted_limit, size_t)
|
||||
#endif
|
||||
TUNABLE_CALLBACK_FNDECL (set_mxfast, size_t)
|
||||
#else
|
||||
/* Initialization routine. */
|
||||
#include <string.h>
|
||||
@@ -324,6 +325,7 @@ ptmalloc_init (void)
|
||||
TUNABLE_GET (tcache_unsorted_limit, size_t,
|
||||
TUNABLE_CALLBACK (set_tcache_unsorted_limit));
|
||||
# endif
|
||||
TUNABLE_GET (mxfast, size_t, TUNABLE_CALLBACK (set_mxfast));
|
||||
#else
|
||||
const char *s = NULL;
|
||||
if (__glibc_likely (_environ != NULL))
|
||||
|
||||
+107
-73
@@ -321,6 +321,10 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line,
|
||||
/* This is another arbitrary limit, which tunables can change. Each
|
||||
tcache bin will hold at most this number of chunks. */
|
||||
# define TCACHE_FILL_COUNT 7
|
||||
|
||||
/* Maximum chunks in tcache bins for tunables. This value must fit the range
|
||||
of tcache->counts[] entries, else they may overflow. */
|
||||
# define MAX_TCACHE_COUNT UINT16_MAX
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1624,7 +1628,7 @@ static INTERNAL_SIZE_T global_max_fast;
|
||||
|
||||
#define set_max_fast(s) \
|
||||
global_max_fast = (((s) == 0) \
|
||||
? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
|
||||
? MIN_CHUNK_SIZE / 2 : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
|
||||
|
||||
static inline INTERNAL_SIZE_T
|
||||
get_max_fast (void)
|
||||
@@ -2810,6 +2814,7 @@ systrim (size_t pad, mstate av)
|
||||
static void
|
||||
munmap_chunk (mchunkptr p)
|
||||
{
|
||||
size_t pagesize = GLRO (dl_pagesize);
|
||||
INTERNAL_SIZE_T size = chunksize (p);
|
||||
|
||||
assert (chunk_is_mmapped (p));
|
||||
@@ -2819,6 +2824,7 @@ munmap_chunk (mchunkptr p)
|
||||
if (DUMPED_MAIN_ARENA_CHUNK (p))
|
||||
return;
|
||||
|
||||
uintptr_t mem = (uintptr_t) chunk2mem (p);
|
||||
uintptr_t block = (uintptr_t) p - prev_size (p);
|
||||
size_t total_size = prev_size (p) + size;
|
||||
/* Unfortunately we have to do the compilers job by hand here. Normally
|
||||
@@ -2826,7 +2832,8 @@ munmap_chunk (mchunkptr p)
|
||||
page size. But gcc does not recognize the optimization possibility
|
||||
(in the moment at least) so we combine the two values into one before
|
||||
the bit test. */
|
||||
if (__builtin_expect (((block | total_size) & (GLRO (dl_pagesize) - 1)) != 0, 0))
|
||||
if (__glibc_unlikely ((block | total_size) & (pagesize - 1)) != 0
|
||||
|| __glibc_unlikely (!powerof2 (mem & (pagesize - 1))))
|
||||
malloc_printerr ("munmap_chunk(): invalid pointer");
|
||||
|
||||
atomic_decrement (&mp_.n_mmaps);
|
||||
@@ -2849,16 +2856,22 @@ mremap_chunk (mchunkptr p, size_t new_size)
|
||||
char *cp;
|
||||
|
||||
assert (chunk_is_mmapped (p));
|
||||
assert (((size + offset) & (GLRO (dl_pagesize) - 1)) == 0);
|
||||
|
||||
uintptr_t block = (uintptr_t) p - offset;
|
||||
uintptr_t mem = (uintptr_t) chunk2mem(p);
|
||||
size_t total_size = offset + size;
|
||||
if (__glibc_unlikely ((block | total_size) & (pagesize - 1)) != 0
|
||||
|| __glibc_unlikely (!powerof2 (mem & (pagesize - 1))))
|
||||
malloc_printerr("mremap_chunk(): invalid pointer");
|
||||
|
||||
/* Note the extra SIZE_SZ overhead as in mmap_chunk(). */
|
||||
new_size = ALIGN_UP (new_size + offset + SIZE_SZ, pagesize);
|
||||
|
||||
/* No need to remap if the number of pages does not change. */
|
||||
if (size + offset == new_size)
|
||||
if (total_size == new_size)
|
||||
return p;
|
||||
|
||||
cp = (char *) __mremap ((char *) p - offset, size + offset, new_size,
|
||||
cp = (char *) __mremap ((char *) block, total_size, new_size,
|
||||
MREMAP_MAYMOVE);
|
||||
|
||||
if (cp == MAP_FAILED)
|
||||
@@ -2888,6 +2901,8 @@ mremap_chunk (mchunkptr p, size_t new_size)
|
||||
typedef struct tcache_entry
|
||||
{
|
||||
struct tcache_entry *next;
|
||||
/* This field exists to detect double frees. */
|
||||
struct tcache_perthread_struct *key;
|
||||
} tcache_entry;
|
||||
|
||||
/* There is one of these for each thread, which contains the
|
||||
@@ -2897,7 +2912,7 @@ typedef struct tcache_entry
|
||||
time), this is for performance reasons. */
|
||||
typedef struct tcache_perthread_struct
|
||||
{
|
||||
char counts[TCACHE_MAX_BINS];
|
||||
uint16_t counts[TCACHE_MAX_BINS];
|
||||
tcache_entry *entries[TCACHE_MAX_BINS];
|
||||
} tcache_perthread_struct;
|
||||
|
||||
@@ -2910,7 +2925,11 @@ static __always_inline void
|
||||
tcache_put (mchunkptr chunk, size_t tc_idx)
|
||||
{
|
||||
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
|
||||
assert (tc_idx < TCACHE_MAX_BINS);
|
||||
|
||||
/* Mark this chunk as "in the tcache" so the test in _int_free will
|
||||
detect a double free. */
|
||||
e->key = tcache;
|
||||
|
||||
e->next = tcache->entries[tc_idx];
|
||||
tcache->entries[tc_idx] = e;
|
||||
++(tcache->counts[tc_idx]);
|
||||
@@ -2922,10 +2941,9 @@ static __always_inline void *
|
||||
tcache_get (size_t tc_idx)
|
||||
{
|
||||
tcache_entry *e = tcache->entries[tc_idx];
|
||||
assert (tc_idx < TCACHE_MAX_BINS);
|
||||
assert (tcache->entries[tc_idx] > 0);
|
||||
tcache->entries[tc_idx] = e->next;
|
||||
--(tcache->counts[tc_idx]);
|
||||
e->key = NULL;
|
||||
return (void *) e;
|
||||
}
|
||||
|
||||
@@ -3027,9 +3045,8 @@ __libc_malloc (size_t bytes)
|
||||
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
if (tc_idx < mp_.tcache_bins
|
||||
/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
|
||||
&& tcache
|
||||
&& tcache->entries[tc_idx] != NULL)
|
||||
&& tcache->counts[tc_idx] > 0)
|
||||
{
|
||||
return tcache_get (tc_idx);
|
||||
}
|
||||
@@ -3716,11 +3733,22 @@ _int_malloc (mstate av, size_t bytes)
|
||||
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
|
||||
{
|
||||
bck = victim->bk;
|
||||
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
|
||||
|| __builtin_expect (chunksize_nomask (victim)
|
||||
> av->system_mem, 0))
|
||||
malloc_printerr ("malloc(): memory corruption");
|
||||
size = chunksize (victim);
|
||||
mchunkptr next = chunk_at_offset (victim, size);
|
||||
|
||||
if (__glibc_unlikely (size <= 2 * SIZE_SZ)
|
||||
|| __glibc_unlikely (size > av->system_mem))
|
||||
malloc_printerr ("malloc(): invalid size (unsorted)");
|
||||
if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)
|
||||
|| __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
|
||||
malloc_printerr ("malloc(): invalid next size (unsorted)");
|
||||
if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
|
||||
malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
|
||||
if (__glibc_unlikely (bck->fd != victim)
|
||||
|| __glibc_unlikely (victim->fd != unsorted_chunks (av)))
|
||||
malloc_printerr ("malloc(): unsorted double linked list corrupted");
|
||||
if (__glibc_unlikely (prev_inuse (next)))
|
||||
malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
|
||||
|
||||
/*
|
||||
If a small request, try to use last remainder if it is the
|
||||
@@ -3841,10 +3869,14 @@ _int_malloc (mstate av, size_t bytes)
|
||||
{
|
||||
victim->fd_nextsize = fwd;
|
||||
victim->bk_nextsize = fwd->bk_nextsize;
|
||||
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
|
||||
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
|
||||
fwd->bk_nextsize = victim;
|
||||
victim->bk_nextsize->fd_nextsize = victim;
|
||||
}
|
||||
bck = fwd->bk;
|
||||
if (bck->fd != fwd)
|
||||
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -4076,6 +4108,9 @@ _int_malloc (mstate av, size_t bytes)
|
||||
victim = av->top;
|
||||
size = chunksize (victim);
|
||||
|
||||
if (__glibc_unlikely (size > av->system_mem))
|
||||
malloc_printerr ("malloc(): corrupted top size");
|
||||
|
||||
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
|
||||
{
|
||||
remainder_size = size - nb;
|
||||
@@ -4151,13 +4186,33 @@ _int_free (mstate av, mchunkptr p, int have_lock)
|
||||
#if USE_TCACHE
|
||||
{
|
||||
size_t tc_idx = csize2tidx (size);
|
||||
|
||||
if (tcache
|
||||
&& tc_idx < mp_.tcache_bins
|
||||
&& tcache->counts[tc_idx] < mp_.tcache_count)
|
||||
if (tcache != NULL && tc_idx < mp_.tcache_bins)
|
||||
{
|
||||
tcache_put (p, tc_idx);
|
||||
return;
|
||||
/* Check to see if it's already in the tcache. */
|
||||
tcache_entry *e = (tcache_entry *) chunk2mem (p);
|
||||
|
||||
/* This test succeeds on double free. However, we don't 100%
|
||||
trust it (it also matches random payload data at a 1 in
|
||||
2^<size_t> chance), so verify it's not an unlikely
|
||||
coincidence before aborting. */
|
||||
if (__glibc_unlikely (e->key == tcache))
|
||||
{
|
||||
tcache_entry *tmp;
|
||||
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
|
||||
for (tmp = tcache->entries[tc_idx];
|
||||
tmp;
|
||||
tmp = tmp->next)
|
||||
if (tmp == e)
|
||||
malloc_printerr ("free(): double free detected in tcache 2");
|
||||
/* If we get here, it was a coincidence. We've wasted a
|
||||
few cycles, but don't abort. */
|
||||
}
|
||||
|
||||
if (tcache->counts[tc_idx] < mp_.tcache_count)
|
||||
{
|
||||
tcache_put (p, tc_idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4278,6 +4333,8 @@ _int_free (mstate av, mchunkptr p, int have_lock)
|
||||
prevsize = prev_size (p);
|
||||
size += prevsize;
|
||||
p = chunk_at_offset(p, -((long) prevsize));
|
||||
if (__glibc_unlikely (chunksize(p) != prevsize))
|
||||
malloc_printerr ("corrupted size vs. prev_size while consolidating");
|
||||
unlink(av, p, bck, fwd);
|
||||
}
|
||||
|
||||
@@ -4439,6 +4496,8 @@ static void malloc_consolidate(mstate av)
|
||||
prevsize = prev_size (p);
|
||||
size += prevsize;
|
||||
p = chunk_at_offset(p, -((long) prevsize));
|
||||
if (__glibc_unlikely (chunksize(p) != prevsize))
|
||||
malloc_printerr ("corrupted size vs. prev_size in fastbins");
|
||||
unlink(av, p, bck, fwd);
|
||||
}
|
||||
|
||||
@@ -4498,11 +4557,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 */
|
||||
|
||||
/* oldmem size */
|
||||
if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0)
|
||||
|| __builtin_expect (oldsize >= av->system_mem, 0))
|
||||
@@ -4570,43 +4624,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);
|
||||
@@ -5104,8 +5122,11 @@ static inline int
|
||||
__always_inline
|
||||
do_set_tcache_count (size_t value)
|
||||
{
|
||||
LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
|
||||
mp_.tcache_count = value;
|
||||
if (value <= MAX_TCACHE_COUNT)
|
||||
{
|
||||
LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
|
||||
mp_.tcache_count = value;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5119,6 +5140,19 @@ do_set_tcache_unsorted_limit (size_t value)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
__always_inline
|
||||
do_set_mxfast (size_t value)
|
||||
{
|
||||
if (value >= 0 && value <= MAX_FAST_SIZE)
|
||||
{
|
||||
LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
|
||||
set_max_fast (value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__libc_mallopt (int param_number, int value)
|
||||
{
|
||||
@@ -5138,13 +5172,7 @@ __libc_mallopt (int param_number, int value)
|
||||
switch (param_number)
|
||||
{
|
||||
case M_MXFAST:
|
||||
if (value >= 0 && value <= MAX_FAST_SIZE)
|
||||
{
|
||||
LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
|
||||
set_max_fast (value);
|
||||
}
|
||||
else
|
||||
res = 0;
|
||||
do_set_mxfast (value);
|
||||
break;
|
||||
|
||||
case M_TRIM_THRESHOLD:
|
||||
@@ -5410,6 +5438,12 @@ __malloc_info (int options, FILE *fp)
|
||||
|
||||
__libc_lock_lock (ar_ptr->mutex);
|
||||
|
||||
/* Account for top chunk. The top-most available chunk is
|
||||
treated specially and is never in any bin. See "initial_top"
|
||||
comments. */
|
||||
avail = chunksize (ar_ptr->top);
|
||||
nblocks = 1; /* Top always exists. */
|
||||
|
||||
for (size_t i = 0; i < NFASTBINS; ++i)
|
||||
{
|
||||
mchunkptr p = fastbin (ar_ptr, i);
|
||||
@@ -5495,7 +5529,7 @@ __malloc_info (int options, FILE *fp)
|
||||
|
||||
for (size_t i = 0; i < nsizes; ++i)
|
||||
if (sizes[i].count != 0 && i != NFASTBINS)
|
||||
fprintf (fp, " \
|
||||
fprintf (fp, "\
|
||||
<size from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n",
|
||||
sizes[i].from, sizes[i].to, sizes[i].total, sizes[i].count);
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Test that glibc.malloc.mxfast tunable works.
|
||||
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/>. */
|
||||
|
||||
/* This test verifies that setting the glibc.malloc.mxfast tunable to
|
||||
zero results in free'd blocks being returned to the small bins, not
|
||||
the fast bins. */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <support/check.h>
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
struct mallinfo m;
|
||||
char *volatile p1;
|
||||
char *volatile p2;
|
||||
|
||||
/* Arbitrary value; must be in default fastbin range. */
|
||||
p1 = malloc (3);
|
||||
/* Something large so that p1 isn't a "top block" */
|
||||
p2 = malloc (512);
|
||||
free (p1);
|
||||
|
||||
m = mallinfo ();
|
||||
|
||||
/* This will fail if there are any blocks in the fastbins. */
|
||||
TEST_COMPARE (m.smblks, 0);
|
||||
|
||||
/* To keep gcc happy. */
|
||||
free (p2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Test that malloc tcache catches double free.
|
||||
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 <errno.h>
|
||||
#include <error.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Do one allocation of any size that fits in tcache. */
|
||||
char * volatile x = malloc (32);
|
||||
|
||||
free (x); // puts in tcache
|
||||
free (x); // should abort
|
||||
|
||||
printf("FAIL: tcache double free not detected\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test
|
||||
#define EXPECTED_SIGNAL SIGABRT
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Test that malloc tcache catches double free.
|
||||
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 <errno.h>
|
||||
#include <error.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char * volatile ptrs[20];
|
||||
int i;
|
||||
|
||||
/* Allocate enough small chunks so that when we free them all, the tcache
|
||||
is full, and the first one we freed is at the end of its linked list. */
|
||||
#define COUNT 20
|
||||
for (i=0; i<COUNT; i++)
|
||||
ptrs[i] = malloc (20);
|
||||
for (i=0; i<COUNT; i++)
|
||||
free (ptrs[i]);
|
||||
free (ptrs[0]);
|
||||
|
||||
printf("FAIL: tcache double free\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test
|
||||
#define EXPECTED_SIGNAL SIGABRT
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Test that malloc tcache catches double free.
|
||||
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 <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Prevent GCC from optimizing away any malloc/free pairs. */
|
||||
#pragma GCC optimize ("O0")
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Do two allocation of any size that fit in tcache, and one that
|
||||
doesn't. */
|
||||
int ** volatile a = malloc (32);
|
||||
int ** volatile b = malloc (32);
|
||||
/* This is just under the mmap threshold. */
|
||||
int ** volatile c = malloc (127 * 1024);
|
||||
|
||||
/* The invalid "tcache bucket" we might dereference will likely end
|
||||
up somewhere within this memory block, so make all the accidental
|
||||
"next" pointers cause segfaults. BZ #23907. */
|
||||
memset (c, 0xff, 127 * 1024);
|
||||
|
||||
free (a); // puts in tcache
|
||||
|
||||
/* A is now free and contains the key we use to detect in-tcache.
|
||||
Copy the key to the other chunks. */
|
||||
memcpy (b, a, 32);
|
||||
memcpy (c, a, 32);
|
||||
|
||||
/* This free tests the "are we in the tcache already" loop with a
|
||||
VALID bin but "coincidental" matching key. */
|
||||
free (b); // should NOT abort
|
||||
/* This free tests the "is it a valid tcache bin" test. */
|
||||
free (c); // should NOT abort
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+5
-5
@@ -1404,10 +1404,13 @@ failure occurs. The return value is zero if the end of the input file
|
||||
is encountered immediately.
|
||||
|
||||
If no bytes can be copied, to report an error, @code{copy_file_range}
|
||||
returns the value @math{-1} and sets @code{errno}. The following
|
||||
@code{errno} error conditions are specific to this function:
|
||||
returns the value @math{-1} and sets @code{errno}. The table below
|
||||
lists some of the error conditions for this function.
|
||||
|
||||
@table @code
|
||||
@item ENOSYS
|
||||
The kernel does not implement the required functionality.
|
||||
|
||||
@item EISDIR
|
||||
At least one of the descriptors @var{inputfd} or @var{outputfd} refers
|
||||
to a directory.
|
||||
@@ -1437,9 +1440,6 @@ reading.
|
||||
|
||||
The argument @var{outputfd} is not a valid file descriptor open for
|
||||
writing, or @var{outputfd} has been opened with @code{O_APPEND}.
|
||||
|
||||
@item EXDEV
|
||||
The input and output files reside on different file systems.
|
||||
@end table
|
||||
|
||||
In addition, @code{copy_file_range} can fail with the error codes
|
||||
|
||||
@@ -243,6 +243,18 @@ This probe is triggered when the
|
||||
value of this tunable.
|
||||
@end deftp
|
||||
|
||||
@deftp Probe memory_tcache_double_free (void *@var{$arg1}, int @var{$arg2})
|
||||
This probe is triggered when @code{free} determines that the memory
|
||||
being freed has probably already been freed, and resides in the
|
||||
per-thread cache. Note that there is an extremely unlikely chance
|
||||
that this probe will trigger due to random payload data remaining in
|
||||
the allocated memory matching the key used to detect double frees.
|
||||
This probe actually indicates that an expensive linear search of the
|
||||
tcache, looking for a double free, has happened. Argument @var{$arg1}
|
||||
is the memory location as passed to @code{free}, Argument @var{$arg2}
|
||||
is the tcache bin it resides in.
|
||||
@end deftp
|
||||
|
||||
@node Mathematical Function Probes
|
||||
@section Mathematical Function Probes
|
||||
|
||||
|
||||
+15
-3
@@ -188,8 +188,8 @@ per-thread cache. The default (and maximum) value is 1032 bytes on
|
||||
|
||||
@deftp Tunable glibc.malloc.tcache_count
|
||||
The maximum number of chunks of each size to cache. The default is 7.
|
||||
There is no upper limit, other than available system memory. If set
|
||||
to zero, the per-thread cache is effectively disabled.
|
||||
The upper limit is 65535. If set to zero, the per-thread cache is effectively
|
||||
disabled.
|
||||
|
||||
The approximate maximum overhead of the per-thread cache is thus equal
|
||||
to the number of bins times the chunk count in each bin times the size
|
||||
@@ -213,6 +213,18 @@ pre-fill the per-thread cache with. The default, or when set to zero,
|
||||
is no limit.
|
||||
@end deftp
|
||||
|
||||
@deftp Tunable glibc.malloc.mxfast
|
||||
One of the optimizations malloc uses is to maintain a series of ``fast
|
||||
bins'' that hold chunks up to a specific size. The default and
|
||||
maximum size which may be held this way is 80 bytes on 32-bit systems
|
||||
or 160 bytes on 64-bit systems. Applications which value size over
|
||||
speed may choose to reduce the size of requests which are serviced
|
||||
from fast bins with this tunable. Note that the value specified
|
||||
includes malloc's internal overhead, which is normally the size of one
|
||||
pointer, so add 4 on 32-bit systems or 8 on 64-bit systems to the size
|
||||
passed to @code{malloc} for the largest bin size to enable.
|
||||
@end deftp
|
||||
|
||||
@node Elision Tunables
|
||||
@section Elision Tunables
|
||||
@cindex elision tunables
|
||||
@@ -333,7 +345,7 @@ This tunable is specific to powerpc, powerpc64 and powerpc64le.
|
||||
The @code{glibc.tune.cpu=xxx} tunable allows the user to tell @theglibc{} to
|
||||
assume that the CPU is @code{xxx} where xxx may have one of these values:
|
||||
@code{generic}, @code{falkor}, @code{thunderxt88}, @code{thunderx2t99},
|
||||
@code{thunderx2t99p1}.
|
||||
@code{thunderx2t99p1}, @code{ares}.
|
||||
|
||||
This tunable is specific to aarch64.
|
||||
@end deftp
|
||||
|
||||
@@ -119,32 +119,32 @@ static const struct test_fff_f_data fma_test_data[] =
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, -min_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, min_subnorm_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, -min_subnorm_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, -max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, plus_infty, plus_infty, -max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, plus_zero, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, minus_zero, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, min_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, -min_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, min_subnorm_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, -min_subnorm_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, -max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, plus_infty, minus_infty, -max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, plus_zero, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, minus_zero, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, min_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, -min_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, min_subnorm_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, -min_subnorm_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, -max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, minus_infty, plus_infty, -max_value, minus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, plus_zero, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, minus_zero, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, min_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, -min_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, min_subnorm_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, -min_subnorm_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, -max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
TEST_fff_f (fma, minus_infty, minus_infty, -max_value, plus_infty, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED|XFAIL_ROUNDING_IBM128_LIBGCC),
|
||||
|
||||
AUTO_TESTS_fff_f (fma),
|
||||
};
|
||||
|
||||
@@ -86,6 +86,11 @@ tests := tst-dirname tst-tsearch tst-fdset tst-efgcvt tst-mntent tst-hsearch \
|
||||
tst-preadvwritev tst-preadvwritev64 tst-makedev tst-empty \
|
||||
tst-preadvwritev2 tst-preadvwritev64v2
|
||||
|
||||
# Tests which need libdl.
|
||||
ifeq (yes,$(build-shared))
|
||||
tests += tst-gethostid
|
||||
endif
|
||||
|
||||
tests-internal := tst-atomic tst-atomic-long tst-allocate_once
|
||||
tests-static := tst-empty
|
||||
|
||||
@@ -145,3 +150,5 @@ tst-allocate_once-ENV = MALLOC_TRACE=$(objpfx)tst-allocate_once.mtrace
|
||||
$(objpfx)tst-allocate_once-mem.out: $(objpfx)tst-allocate_once.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-allocate_once.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-gethostid: $(libdl)
|
||||
|
||||
@@ -319,6 +319,7 @@ error (int status, int errnum, const char *message, ...)
|
||||
|
||||
va_start (args, message);
|
||||
error_tail (status, errnum, message, args);
|
||||
va_end (args);
|
||||
|
||||
#ifdef _LIBC
|
||||
_IO_funlockfile (stderr);
|
||||
@@ -390,6 +391,7 @@ error_at_line (int status, int errnum, const char *file_name,
|
||||
|
||||
va_start (args, message);
|
||||
error_tail (status, errnum, message, args);
|
||||
va_end (args);
|
||||
|
||||
#ifdef _LIBC
|
||||
_IO_funlockfile (stderr);
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Basic test for gethostid.
|
||||
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 <gnu/lib-names.h>
|
||||
#include <nss.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/xdlfcn.h>
|
||||
#include <support/xstdio.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Initial test is run outside a chroot, to increase the likelihood of
|
||||
success. */
|
||||
static void
|
||||
outside_chroot (void *closure)
|
||||
{
|
||||
long id = gethostid ();
|
||||
printf ("info: host ID outside chroot: 0x%lx\n", id);
|
||||
}
|
||||
|
||||
/* The same, but this time perform a chroot operation. */
|
||||
static void
|
||||
in_chroot (void *closure)
|
||||
{
|
||||
const char *chroot_path = closure;
|
||||
xchroot (chroot_path);
|
||||
long id = gethostid ();
|
||||
printf ("info: host ID in chroot: 0x%lx\n", id);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
support_isolate_in_subprocess (outside_chroot, NULL);
|
||||
|
||||
/* Now run the test inside a chroot. */
|
||||
support_become_root ();
|
||||
if (!support_can_chroot ())
|
||||
/* Cannot perform further tests. */
|
||||
return 0;
|
||||
|
||||
/* Only use nss_files. */
|
||||
__nss_configure_lookup ("hosts", "files");
|
||||
|
||||
/* Load the DSO outside of the chroot. */
|
||||
xdlopen (LIBNSS_FILES_SO, RTLD_LAZY);
|
||||
|
||||
char *chroot_dir = support_create_temp_directory ("tst-gethostid-");
|
||||
support_isolate_in_subprocess (in_chroot, chroot_dir);
|
||||
|
||||
/* Tests with /etc/hosts in the chroot. */
|
||||
{
|
||||
char *path = xasprintf ("%s/etc", chroot_dir);
|
||||
add_temp_file (path);
|
||||
xmkdir (path, 0777);
|
||||
free (path);
|
||||
path = xasprintf ("%s/etc/hosts", chroot_dir);
|
||||
add_temp_file (path);
|
||||
|
||||
FILE *fp = xfopen (path, "w");
|
||||
xfclose (fp);
|
||||
printf ("info: chroot test with an empty /etc/hosts file\n");
|
||||
support_isolate_in_subprocess (in_chroot, chroot_dir);
|
||||
|
||||
char hostname[1024];
|
||||
int ret = gethostname (hostname, sizeof (hostname));
|
||||
if (ret < 0)
|
||||
printf ("warning: invalid result from gethostname: %d\n", ret);
|
||||
else if (strlen (hostname) == 0)
|
||||
puts ("warning: gethostname returned empty string");
|
||||
else
|
||||
{
|
||||
printf ("info: chroot test with IPv6 address in /etc/hosts for: %s\n",
|
||||
hostname);
|
||||
fp = xfopen (path, "w");
|
||||
/* Use an IPv6 address to induce another lookup failure. */
|
||||
fprintf (fp, "2001:db8::1 %s\n", hostname);
|
||||
xfclose (fp);
|
||||
support_isolate_in_subprocess (in_chroot, chroot_dir);
|
||||
}
|
||||
free (path);
|
||||
}
|
||||
free (chroot_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <limits.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static void
|
||||
do_test_with_invalid_flags (void)
|
||||
{
|
||||
#ifndef RWF_HIPRI
|
||||
# define RWF_HIPRI 0
|
||||
#endif
|
||||
@@ -39,6 +36,68 @@ do_test_with_invalid_flags (void)
|
||||
#endif
|
||||
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT \
|
||||
| RWF_APPEND)
|
||||
|
||||
static void
|
||||
do_test_with_invalid_fd (void)
|
||||
{
|
||||
char buf[256];
|
||||
struct iovec iov = { buf, sizeof buf };
|
||||
|
||||
/* Check with flag being 0 to use the fallback code which calls pwritev
|
||||
or writev. */
|
||||
TEST_VERIFY (preadv2 (-1, &iov, 1, -1, 0) == -1);
|
||||
TEST_COMPARE (errno, EBADF);
|
||||
TEST_VERIFY (pwritev2 (-1, &iov, 1, -1, 0) == -1);
|
||||
TEST_COMPARE (errno, EBADF);
|
||||
|
||||
/* Same tests as before but with flags being different than 0. Since
|
||||
there is no emulation for any flag value, fallback code returns
|
||||
ENOTSUP. This is different running on a kernel with preadv2/pwritev2
|
||||
support, where EBADF is returned). */
|
||||
TEST_VERIFY (preadv2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
|
||||
TEST_VERIFY (pwritev2 (-1, &iov, 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EBADF || errno == ENOTSUP);
|
||||
}
|
||||
|
||||
static void
|
||||
do_test_with_invalid_iov (void)
|
||||
{
|
||||
{
|
||||
char buf[256];
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = (size_t)SSIZE_MAX + 1;
|
||||
|
||||
TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, 0) == -1);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, 0) == -1);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
|
||||
/* Same as for invalid file descriptor tests, emulation fallback
|
||||
first checks for flag value and return ENOTSUP. */
|
||||
TEST_VERIFY (preadv2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
|
||||
TEST_VERIFY (pwritev2 (temp_fd, &iov, 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
|
||||
}
|
||||
|
||||
{
|
||||
/* An invalid iovec buffer should trigger an invalid memory access
|
||||
or an error (Linux for instance returns EFAULT). */
|
||||
struct iovec iov[IOV_MAX+1] = { 0 };
|
||||
|
||||
TEST_VERIFY (preadv2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
|
||||
TEST_VERIFY (pwritev2 (temp_fd, iov, IOV_MAX + 1, 0, RWF_HIPRI) == -1);
|
||||
TEST_VERIFY (errno == EINVAL || errno == ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_test_with_invalid_flags (void)
|
||||
{
|
||||
/* Set the next bit from the mask of all supported flags. */
|
||||
int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2;
|
||||
invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
|
||||
|
||||
@@ -30,6 +30,8 @@ do_test (void)
|
||||
{
|
||||
do_test_with_invalid_flags ();
|
||||
do_test_without_offset ();
|
||||
do_test_with_invalid_fd ();
|
||||
do_test_with_invalid_iov ();
|
||||
|
||||
return do_test_with_offset (0);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ do_test (void)
|
||||
{
|
||||
do_test_with_invalid_flags ();
|
||||
do_test_without_offset ();
|
||||
do_test_with_invalid_fd ();
|
||||
do_test_with_invalid_iov ();
|
||||
|
||||
return do_test_with_offset (0);
|
||||
}
|
||||
|
||||
+23
-6
@@ -241,9 +241,9 @@ LDLIBS-tst-minstack-throw = -lstdc++
|
||||
|
||||
tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
||||
tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \
|
||||
tst-mutex7 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-mutex7 tst-mutex9 tst-mutex10 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-mutexpi9 \
|
||||
tst-spin1 tst-spin2 tst-spin3 tst-spin4 \
|
||||
tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
|
||||
@@ -318,7 +318,10 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
||||
tst-minstack-throw \
|
||||
tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
|
||||
tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \
|
||||
tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock
|
||||
tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
|
||||
tst-rwlock-pwn \
|
||||
tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
|
||||
tst-unwind-thread
|
||||
|
||||
tests-internal := tst-rwlock19 tst-rwlock20 \
|
||||
tst-sem11 tst-sem12 tst-sem13 \
|
||||
@@ -382,7 +385,8 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
|
||||
tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4 \
|
||||
tst-oncex3 tst-oncex4
|
||||
ifeq ($(build-shared),yes)
|
||||
tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder
|
||||
tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder \
|
||||
tst-audit-threads
|
||||
tests-internal += tst-tls3 tst-tls3-malloc tst-tls5 tst-stackguard1
|
||||
tests-nolibpthread += tst-fini1
|
||||
ifeq ($(have-z-execstack),yes)
|
||||
@@ -394,7 +398,8 @@ 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-compat-forwarder-mod
|
||||
tst-join7mod tst-compat-forwarder-mod tst-audit-threads-mod1 \
|
||||
tst-audit-threads-mod2
|
||||
extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
|
||||
tst-cleanup4aux.o tst-cleanupx4aux.o
|
||||
test-extras += tst-cleanup4aux tst-cleanupx4aux
|
||||
@@ -709,6 +714,18 @@ endif
|
||||
|
||||
$(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so
|
||||
|
||||
tst-mutex10-ENV = GLIBC_TUNABLES=glibc.elision.enable=1
|
||||
|
||||
# Protect against a build using -Wl,-z,now.
|
||||
LDFLAGS-tst-audit-threads-mod1.so = -Wl,-z,lazy
|
||||
LDFLAGS-tst-audit-threads-mod2.so = -Wl,-z,lazy
|
||||
LDFLAGS-tst-audit-threads = -Wl,-z,lazy
|
||||
$(objpfx)tst-audit-threads: $(objpfx)tst-audit-threads-mod2.so
|
||||
$(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so
|
||||
tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so
|
||||
|
||||
CFLAGS-tst-unwind-thread.c += -funwind-tables
|
||||
|
||||
# The tests here better do not run in parallel
|
||||
ifneq ($(filter %tests,$(MAKECMDGOALS)),)
|
||||
.NOTPARALLEL:
|
||||
|
||||
@@ -572,7 +572,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
|
||||
/* Place the thread descriptor at the end of the stack. */
|
||||
#if TLS_TCB_AT_TP
|
||||
pd = (struct pthread *) ((char *) mem + size) - 1;
|
||||
pd = (struct pthread *) ((((uintptr_t) mem + size)
|
||||
- TLS_TCB_SIZE)
|
||||
& ~__static_tls_align_m1);
|
||||
#elif TLS_DTV_AT_TP
|
||||
pd = (struct pthread *) ((((uintptr_t) mem + size
|
||||
- __static_tls_size)
|
||||
|
||||
+8
-4
@@ -110,19 +110,23 @@ enum
|
||||
};
|
||||
#define PTHREAD_MUTEX_PSHARED_BIT 128
|
||||
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
#define PTHREAD_MUTEX_TYPE(m) \
|
||||
((m)->__data.__kind & 127)
|
||||
(atomic_load_relaxed (&((m)->__data.__kind)) & 127)
|
||||
/* Don't include NO_ELISION, as that type is always the same
|
||||
as the underlying lock type. */
|
||||
#define PTHREAD_MUTEX_TYPE_ELISION(m) \
|
||||
((m)->__data.__kind & (127|PTHREAD_MUTEX_ELISION_NP))
|
||||
(atomic_load_relaxed (&((m)->__data.__kind)) \
|
||||
& (127 | PTHREAD_MUTEX_ELISION_NP))
|
||||
|
||||
#if LLL_PRIVATE == 0 && LLL_SHARED == 128
|
||||
# define PTHREAD_MUTEX_PSHARED(m) \
|
||||
((m)->__data.__kind & 128)
|
||||
(atomic_load_relaxed (&((m)->__data.__kind)) & 128)
|
||||
#else
|
||||
# define PTHREAD_MUTEX_PSHARED(m) \
|
||||
(((m)->__data.__kind & 128) ? LLL_SHARED : LLL_PRIVATE)
|
||||
((atomic_load_relaxed (&((m)->__data.__kind)) & 128) \
|
||||
? LLL_SHARED : LLL_PRIVATE)
|
||||
#endif
|
||||
|
||||
/* The kernel when waking robust mutexes on exit never uses
|
||||
|
||||
@@ -405,8 +405,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);
|
||||
|
||||
@@ -516,7 +516,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||
struct timespec rt;
|
||||
if (__clock_gettime (CLOCK_MONOTONIC, &rt) != 0)
|
||||
__libc_fatal ("clock_gettime does not support "
|
||||
"CLOCK_MONOTONIC");
|
||||
"CLOCK_MONOTONIC\n");
|
||||
/* Convert the absolute timeout value to a relative
|
||||
timeout. */
|
||||
rt.tv_sec = abstime->tv_sec - rt.tv_sec;
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
int
|
||||
pthread_mutex_consistent (pthread_mutex_t *mutex)
|
||||
{
|
||||
/* Test whether this is a robust mutex with a dead owner. */
|
||||
if ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0
|
||||
/* Test whether this is a robust mutex with a dead owner.
|
||||
See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0
|
||||
|| mutex->__data.__owner != PTHREAD_MUTEX_INCONSISTENT)
|
||||
return EINVAL;
|
||||
|
||||
|
||||
@@ -27,12 +27,17 @@ __pthread_mutex_destroy (pthread_mutex_t *mutex)
|
||||
{
|
||||
LIBC_PROBE (mutex_destroy, 1, mutex);
|
||||
|
||||
if ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0
|
||||
&& mutex->__data.__nusers != 0)
|
||||
return EBUSY;
|
||||
|
||||
/* Set to an invalid value. */
|
||||
mutex->__data.__kind = -1;
|
||||
/* Set to an invalid value. Relaxed MO is enough as it is undefined behavior
|
||||
if the mutex is used after it has been destroyed. But you can reinitialize
|
||||
it with pthread_mutex_init. */
|
||||
atomic_store_relaxed (&(mutex->__data.__kind), -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
int
|
||||
pthread_mutex_getprioceiling (const pthread_mutex_t *mutex, int *prioceiling)
|
||||
{
|
||||
if (__builtin_expect ((mutex->__data.__kind
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if (__builtin_expect ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0, 0))
|
||||
return EINVAL;
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ __pthread_mutex_init (pthread_mutex_t *mutex,
|
||||
memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);
|
||||
|
||||
/* Copy the values from the attribute. */
|
||||
mutex->__data.__kind = imutexattr->mutexkind & ~PTHREAD_MUTEXATTR_FLAG_BITS;
|
||||
int mutex_kind = imutexattr->mutexkind & ~PTHREAD_MUTEXATTR_FLAG_BITS;
|
||||
|
||||
if ((imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST) != 0)
|
||||
{
|
||||
@@ -111,17 +111,17 @@ __pthread_mutex_init (pthread_mutex_t *mutex,
|
||||
return ENOTSUP;
|
||||
#endif
|
||||
|
||||
mutex->__data.__kind |= PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
mutex_kind |= PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
}
|
||||
|
||||
switch (imutexattr->mutexkind & PTHREAD_MUTEXATTR_PROTOCOL_MASK)
|
||||
{
|
||||
case PTHREAD_PRIO_INHERIT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:
|
||||
mutex->__data.__kind |= PTHREAD_MUTEX_PRIO_INHERIT_NP;
|
||||
mutex_kind |= PTHREAD_MUTEX_PRIO_INHERIT_NP;
|
||||
break;
|
||||
|
||||
case PTHREAD_PRIO_PROTECT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:
|
||||
mutex->__data.__kind |= PTHREAD_MUTEX_PRIO_PROTECT_NP;
|
||||
mutex_kind |= PTHREAD_MUTEX_PRIO_PROTECT_NP;
|
||||
|
||||
int ceiling = (imutexattr->mutexkind
|
||||
& PTHREAD_MUTEXATTR_PRIO_CEILING_MASK)
|
||||
@@ -145,7 +145,11 @@ __pthread_mutex_init (pthread_mutex_t *mutex,
|
||||
FUTEX_PRIVATE_FLAG FUTEX_WAKE. */
|
||||
if ((imutexattr->mutexkind & (PTHREAD_MUTEXATTR_FLAG_PSHARED
|
||||
| PTHREAD_MUTEXATTR_FLAG_ROBUST)) != 0)
|
||||
mutex->__data.__kind |= PTHREAD_MUTEX_PSHARED_BIT;
|
||||
mutex_kind |= PTHREAD_MUTEX_PSHARED_BIT;
|
||||
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
atomic_store_relaxed (&(mutex->__data.__kind), mutex_kind);
|
||||
|
||||
/* Default values: mutex not used yet. */
|
||||
// mutex->__count = 0; already done by memset
|
||||
|
||||
@@ -62,6 +62,8 @@ static int __pthread_mutex_lock_full (pthread_mutex_t *mutex)
|
||||
int
|
||||
__pthread_mutex_lock (pthread_mutex_t *mutex)
|
||||
{
|
||||
/* See concurrency notes regarding mutex type which is loaded from __kind
|
||||
in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
unsigned int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);
|
||||
|
||||
LIBC_PROBE (mutex_entry, 1, mutex);
|
||||
@@ -350,8 +352,14 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
int kind, robust;
|
||||
{
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
|
||||
kind = mutex_kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
robust = mutex_kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
}
|
||||
|
||||
if (robust)
|
||||
{
|
||||
@@ -502,7 +510,10 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_PP_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int kind = atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
@@ -607,15 +618,18 @@ hidden_def (__pthread_mutex_lock)
|
||||
void
|
||||
__pthread_mutex_cond_lock_adjust (pthread_mutex_t *mutex)
|
||||
{
|
||||
assert ((mutex->__data.__kind & PTHREAD_MUTEX_PRIO_INHERIT_NP) != 0);
|
||||
assert ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0);
|
||||
assert ((mutex->__data.__kind & PTHREAD_MUTEX_PSHARED_BIT) == 0);
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
|
||||
assert ((mutex_kind & PTHREAD_MUTEX_PRIO_INHERIT_NP) != 0);
|
||||
assert ((mutex_kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0);
|
||||
assert ((mutex_kind & PTHREAD_MUTEX_PSHARED_BIT) == 0);
|
||||
|
||||
/* Record the ownership. */
|
||||
pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
|
||||
mutex->__data.__owner = id;
|
||||
|
||||
if (mutex->__data.__kind == PTHREAD_MUTEX_PI_RECURSIVE_NP)
|
||||
if (mutex_kind == PTHREAD_MUTEX_PI_RECURSIVE_NP)
|
||||
++mutex->__data.__count;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,9 +27,10 @@ int
|
||||
pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling,
|
||||
int *old_ceiling)
|
||||
{
|
||||
/* The low bits of __kind aren't ever changed after pthread_mutex_init,
|
||||
so we don't need a lock yet. */
|
||||
if ((mutex->__data.__kind & PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0)
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_PRIO_PROTECT_NP) == 0)
|
||||
return EINVAL;
|
||||
|
||||
/* See __init_sched_fifo_prio. */
|
||||
|
||||
@@ -53,6 +53,8 @@ __pthread_mutex_timedlock (pthread_mutex_t *mutex,
|
||||
/* We must not check ABSTIME here. If the thread does not block
|
||||
abstime must not be checked for a valid value. */
|
||||
|
||||
/* See concurrency notes regarding mutex type which is loaded from __kind
|
||||
in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
switch (__builtin_expect (PTHREAD_MUTEX_TYPE_ELISION (mutex),
|
||||
PTHREAD_MUTEX_TIMED_NP))
|
||||
{
|
||||
@@ -338,8 +340,14 @@ __pthread_mutex_timedlock (pthread_mutex_t *mutex,
|
||||
case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
int kind, robust;
|
||||
{
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
|
||||
kind = mutex_kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
robust = mutex_kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
}
|
||||
|
||||
if (robust)
|
||||
{
|
||||
@@ -509,7 +517,10 @@ __pthread_mutex_timedlock (pthread_mutex_t *mutex,
|
||||
case PTHREAD_MUTEX_PP_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int kind = atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
int oldval;
|
||||
pid_t id = THREAD_GETMEM (THREAD_SELF, tid);
|
||||
|
||||
/* See concurrency notes regarding mutex type which is loaded from __kind
|
||||
in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
switch (__builtin_expect (PTHREAD_MUTEX_TYPE_ELISION (mutex),
|
||||
PTHREAD_MUTEX_TIMED_NP))
|
||||
{
|
||||
@@ -92,6 +94,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
&mutex->__data.__list.__next);
|
||||
/* We need to set op_pending before starting the operation. Also
|
||||
see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
do
|
||||
@@ -117,7 +122,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exist here. If we fall
|
||||
@@ -133,6 +143,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
int kind = PTHREAD_MUTEX_TYPE (mutex);
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. Also see comments at ENQUEUE_MUTEX. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
return EDEADLK;
|
||||
@@ -140,6 +152,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
|
||||
if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||
NULL);
|
||||
|
||||
@@ -158,6 +172,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
id, 0);
|
||||
if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
|
||||
{
|
||||
/* We haven't acquired the lock as it is already acquired by
|
||||
another owner. We do not need to ensure ordering wrt another
|
||||
memory access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -171,13 +188,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
if (oldval == id)
|
||||
lll_unlock (mutex->__data.__lock,
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
|
||||
/* FIXME This violates the mutex destruction requirements. See
|
||||
__pthread_mutex_unlock_full. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
}
|
||||
while ((oldval & FUTEX_OWNER_DIED) != 0);
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
mutex->__data.__owner = id;
|
||||
@@ -199,14 +223,25 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
int kind, robust;
|
||||
{
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int mutex_kind = atomic_load_relaxed (&(mutex->__data.__kind));
|
||||
kind = mutex_kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
robust = mutex_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 +250,16 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
{
|
||||
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return EDEADLK;
|
||||
}
|
||||
|
||||
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
|
||||
{
|
||||
/* We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Just bump the counter. */
|
||||
@@ -242,6 +281,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
{
|
||||
if ((oldval & FUTEX_OWNER_DIED) == 0)
|
||||
{
|
||||
/* We haven't acquired the lock as it is already acquired by
|
||||
another owner. We do not need to ensure ordering wrt another
|
||||
memory access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -262,6 +304,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
if (INTERNAL_SYSCALL_ERROR_P (e, __err)
|
||||
&& INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK)
|
||||
{
|
||||
/* The kernel has not yet finished the mutex owner death.
|
||||
We do not need to ensure ordering wrt another memory
|
||||
access. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
return EBUSY;
|
||||
@@ -279,7 +324,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
/* But it is inconsistent unless marked otherwise. */
|
||||
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
|
||||
/* Note that we deliberately exit here. If we fall
|
||||
@@ -302,13 +352,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
|
||||
0, 0);
|
||||
|
||||
/* To the kernel, this will be visible after the kernel has
|
||||
acquired the mutex in the syscall. */
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
return ENOTRECOVERABLE;
|
||||
}
|
||||
|
||||
if (robust)
|
||||
{
|
||||
/* We must not enqueue the mutex before we have acquired it.
|
||||
Also see comments at ENQUEUE_MUTEX. */
|
||||
__asm ("" ::: "memory");
|
||||
ENQUEUE_MUTEX_PI (mutex);
|
||||
/* We need to clear op_pending after we enqueue the mutex. */
|
||||
__asm ("" ::: "memory");
|
||||
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||
}
|
||||
|
||||
@@ -325,7 +382,10 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
case PTHREAD_MUTEX_PP_NORMAL_NP:
|
||||
case PTHREAD_MUTEX_PP_ADAPTIVE_NP:
|
||||
{
|
||||
int kind = mutex->__data.__kind & PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int kind = atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_KIND_MASK_NP;
|
||||
|
||||
oldval = mutex->__data.__lock;
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ int
|
||||
attribute_hidden
|
||||
__pthread_mutex_unlock_usercnt (pthread_mutex_t *mutex, int decr)
|
||||
{
|
||||
/* See concurrency notes regarding mutex type which is loaded from __kind
|
||||
in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);
|
||||
if (__builtin_expect (type &
|
||||
~(PTHREAD_MUTEX_KIND_MASK_NP|PTHREAD_MUTEX_ELISION_FLAGS_NP), 0))
|
||||
@@ -222,13 +224,19 @@ __pthread_mutex_unlock_full (pthread_mutex_t *mutex, int decr)
|
||||
/* If the previous owner died and the caller did not succeed in
|
||||
making the state consistent, mark the mutex as unrecoverable
|
||||
and make all waiters. */
|
||||
if ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0
|
||||
&& __builtin_expect (mutex->__data.__owner
|
||||
== PTHREAD_MUTEX_INCONSISTENT, 0))
|
||||
pi_notrecoverable:
|
||||
newowner = PTHREAD_MUTEX_NOTRECOVERABLE;
|
||||
|
||||
if ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0)
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
if ((atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_ROBUST_NORMAL_NP) != 0)
|
||||
{
|
||||
continue_pi_robust:
|
||||
/* Remove mutex from the list.
|
||||
@@ -251,7 +259,10 @@ __pthread_mutex_unlock_full (pthread_mutex_t *mutex, int decr)
|
||||
/* Unlock. Load all necessary mutex data before releasing the mutex
|
||||
to not violate the mutex destruction requirements (see
|
||||
lll_unlock). */
|
||||
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
/* See concurrency notes regarding __kind in struct __pthread_mutex_s
|
||||
in sysdeps/nptl/bits/thread-shared-types.h. */
|
||||
int robust = atomic_load_relaxed (&(mutex->__data.__kind))
|
||||
& PTHREAD_MUTEX_ROBUST_NORMAL_NP;
|
||||
private = (robust
|
||||
? PTHREAD_ROBUST_MUTEX_PSHARED (mutex)
|
||||
: PTHREAD_MUTEX_PSHARED (mutex));
|
||||
|
||||
@@ -314,8 +314,8 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
|
||||
harmless because the flag is just about the state of
|
||||
__readers, and all threads set the flag under the same
|
||||
conditions. */
|
||||
while ((atomic_load_relaxed (&rwlock->__data.__readers)
|
||||
& PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
while (((r = atomic_load_relaxed (&rwlock->__data.__readers))
|
||||
& PTHREAD_RWLOCK_RWAITING) != 0)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
int err = futex_abstimed_wait (&rwlock->__data.__readers,
|
||||
|
||||
@@ -94,15 +94,22 @@ __pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
|
||||
/* Same as in __pthread_rwlock_rdlock_full:
|
||||
We started the read phase, so we are also responsible for
|
||||
updating the write-phase futex. Relaxed MO is sufficient.
|
||||
Note that there can be no other reader that we have to wake
|
||||
because all other readers will see the read phase started by us
|
||||
(or they will try to start it themselves); if a writer started
|
||||
the read phase, we cannot have started it. Furthermore, we
|
||||
cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
|
||||
overwrite the value set by the most recent writer (or the readers
|
||||
before it in case of explicit hand-over) and we know that there
|
||||
are no waiting readers. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
We have to do the same steps as a writer would when handing over the
|
||||
read phase to use because other readers cannot distinguish between
|
||||
us and the writer.
|
||||
Note that __pthread_rwlock_tryrdlock callers will not have to be
|
||||
woken up because they will either see the read phase started by us
|
||||
or they will try to start it themselves; however, callers of
|
||||
__pthread_rwlock_rdlock_full just increase the reader count and then
|
||||
check what state the lock is in, so they cannot distinguish between
|
||||
us and a writer that acquired and released the lock in the
|
||||
meantime. */
|
||||
if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||
& PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -46,8 +46,15 @@ __pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
|
||||
&rwlock->__data.__readers, &r,
|
||||
r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||
{
|
||||
/* We have become the primary writer and we cannot have shared
|
||||
the PTHREAD_RWLOCK_FUTEX_USED flag with someone else, so we
|
||||
can simply enable blocking (see full wrlock code). */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
/* If we started a write phase, we need to enable readers to
|
||||
wait. If we did not, we must not change it because other threads
|
||||
may have set the PTHREAD_RWLOCK_FUTEX_USED in the meantime. */
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
THREAD_GETMEM (THREAD_SELF, tid));
|
||||
return 0;
|
||||
|
||||
@@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle)
|
||||
}
|
||||
|
||||
void
|
||||
__run_fork_handlers (enum __run_fork_handler_type who)
|
||||
__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
|
||||
{
|
||||
struct fork_handler *runp;
|
||||
|
||||
if (who == atfork_run_prepare)
|
||||
{
|
||||
lll_lock (atfork_lock, LLL_PRIVATE);
|
||||
if (do_locking)
|
||||
lll_lock (atfork_lock, LLL_PRIVATE);
|
||||
size_t sl = fork_handler_list_size (&fork_handlers);
|
||||
for (size_t i = sl; i > 0; i--)
|
||||
{
|
||||
@@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_handler_type who)
|
||||
else if (who == atfork_run_parent && runp->parent_handler)
|
||||
runp->parent_handler ();
|
||||
}
|
||||
lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||
if (do_locking)
|
||||
lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Dummy audit library for test-audit-threads.
|
||||
|
||||
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 <elf.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* We must use a dummy LD_AUDIT module to force the dynamic loader to
|
||||
*not* update the real PLT, and instead use a cached value for the
|
||||
lazy resolution result. It is the update of that cached value that
|
||||
we are testing for correctness by doing this. */
|
||||
|
||||
/* Library to be audited. */
|
||||
#define LIB "tst-audit-threads-mod2.so"
|
||||
/* CALLNUM is the number of retNum functions. */
|
||||
#define CALLNUM 7999
|
||||
|
||||
#define CONCATX(a, b) __CONCAT (a, b)
|
||||
|
||||
static int previous = 0;
|
||||
|
||||
unsigned int
|
||||
la_version (unsigned int ver)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
|
||||
{
|
||||
return LA_FLG_BINDTO | LA_FLG_BINDFROM;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
CONCATX(la_symbind, __ELF_NATIVE_CLASS) (ElfW(Sym) *sym,
|
||||
unsigned int ndx,
|
||||
uintptr_t *refcook,
|
||||
uintptr_t *defcook,
|
||||
unsigned int *flags,
|
||||
const char *symname)
|
||||
{
|
||||
const char * retnum = "retNum";
|
||||
char * num = strstr (symname, retnum);
|
||||
int n;
|
||||
/* Validate if the symbols are getting called in the correct order.
|
||||
This code is here to verify binutils does not optimize out the PLT
|
||||
entries that require the symbol binding. */
|
||||
if (num != NULL)
|
||||
{
|
||||
n = atoi (num);
|
||||
assert (n >= previous);
|
||||
assert (n <= CALLNUM);
|
||||
previous = n;
|
||||
}
|
||||
return sym->st_value;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Syscall wrapper that do not set errno. Linux powerpc version.
|
||||
/* Shared object with a huge number of functions for test-audit-threads.
|
||||
|
||||
Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
@@ -16,15 +17,6 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* __access_noerrno is used during process initialization in elf/dl-tunables.c
|
||||
before the TCB is initialized, prohibiting the usage of
|
||||
ABORT_TRANSACTION. */
|
||||
#undef ABORT_TRANSACTION
|
||||
#define ABORT_TRANSACTION
|
||||
|
||||
#include "sysdeps/unix/sysv/linux/not-errno.h"
|
||||
|
||||
/* Recover ABORT_TRANSACTION's previous value, in order to not affect
|
||||
other syscalls. */
|
||||
#undef ABORT_TRANSACTION
|
||||
#define ABORT_TRANSACTION ABORT_TRANSACTION_IMPL
|
||||
/* Define all the retNumN functions in a library. */
|
||||
#define definenum
|
||||
#include "tst-audit-threads.h"
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Test multi-threading using LD_AUDIT.
|
||||
|
||||
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/>. */
|
||||
|
||||
/* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a
|
||||
library with a huge number of functions in order to validate lazy symbol
|
||||
binding with an audit library. We use one thread per CPU to test that
|
||||
concurrent lazy resolution does not have any defects which would cause
|
||||
the process to fail. We use an LD_AUDIT library to force the testing of
|
||||
the relocation resolution caching code in the dynamic loader i.e.
|
||||
_dl_runtime_profile and _dl_profile_fixup. */
|
||||
|
||||
#include <support/xthread.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
static int do_test (void);
|
||||
|
||||
/* This test usually takes less than 3s to run. However, there are cases that
|
||||
take up to 30s. */
|
||||
#define TIMEOUT 60
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
||||
|
||||
/* Declare the functions we are going to call. */
|
||||
#define externnum
|
||||
#include "tst-audit-threads.h"
|
||||
#undef externnum
|
||||
|
||||
int num_threads;
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
void
|
||||
sync_all (int num)
|
||||
{
|
||||
pthread_barrier_wait (&barrier);
|
||||
}
|
||||
|
||||
void
|
||||
call_all_ret_nums (void)
|
||||
{
|
||||
/* Call each function one at a time from all threads. */
|
||||
#define callnum
|
||||
#include "tst-audit-threads.h"
|
||||
#undef callnum
|
||||
}
|
||||
|
||||
void *
|
||||
thread_main (void *unused)
|
||||
{
|
||||
call_all_ret_nums ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define STR2(X) #X
|
||||
#define STR(X) STR2(X)
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
int i;
|
||||
pthread_t *threads;
|
||||
|
||||
num_threads = get_nprocs ();
|
||||
if (num_threads <= 1)
|
||||
num_threads = 2;
|
||||
|
||||
/* Used to synchronize all the threads after calling each retNumN. */
|
||||
xpthread_barrier_init (&barrier, NULL, num_threads);
|
||||
|
||||
threads = (pthread_t *) xcalloc (num_threads, sizeof(pthread_t));
|
||||
for (i = 0; i < num_threads; i++)
|
||||
threads[i] = xpthread_create(NULL, thread_main, NULL);
|
||||
|
||||
for (i = 0; i < num_threads; i++)
|
||||
xpthread_join(threads[i]);
|
||||
|
||||
free (threads);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/* Helper header for test-audit-threads.
|
||||
|
||||
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/>. */
|
||||
|
||||
/* We use this helper to create a large number of functions, all of
|
||||
which will be resolved lazily and thus have their PLT updated.
|
||||
This is done to provide enough functions that we can statistically
|
||||
observe a thread vs. PLT resolution failure if one exists. */
|
||||
|
||||
#define CONCAT(a, b) a ## b
|
||||
#define NUM(x, y) CONCAT (x, y)
|
||||
|
||||
#define FUNC10(x) \
|
||||
FUNC (NUM (x, 0)); \
|
||||
FUNC (NUM (x, 1)); \
|
||||
FUNC (NUM (x, 2)); \
|
||||
FUNC (NUM (x, 3)); \
|
||||
FUNC (NUM (x, 4)); \
|
||||
FUNC (NUM (x, 5)); \
|
||||
FUNC (NUM (x, 6)); \
|
||||
FUNC (NUM (x, 7)); \
|
||||
FUNC (NUM (x, 8)); \
|
||||
FUNC (NUM (x, 9))
|
||||
|
||||
#define FUNC100(x) \
|
||||
FUNC10 (NUM (x, 0)); \
|
||||
FUNC10 (NUM (x, 1)); \
|
||||
FUNC10 (NUM (x, 2)); \
|
||||
FUNC10 (NUM (x, 3)); \
|
||||
FUNC10 (NUM (x, 4)); \
|
||||
FUNC10 (NUM (x, 5)); \
|
||||
FUNC10 (NUM (x, 6)); \
|
||||
FUNC10 (NUM (x, 7)); \
|
||||
FUNC10 (NUM (x, 8)); \
|
||||
FUNC10 (NUM (x, 9))
|
||||
|
||||
#define FUNC1000(x) \
|
||||
FUNC100 (NUM (x, 0)); \
|
||||
FUNC100 (NUM (x, 1)); \
|
||||
FUNC100 (NUM (x, 2)); \
|
||||
FUNC100 (NUM (x, 3)); \
|
||||
FUNC100 (NUM (x, 4)); \
|
||||
FUNC100 (NUM (x, 5)); \
|
||||
FUNC100 (NUM (x, 6)); \
|
||||
FUNC100 (NUM (x, 7)); \
|
||||
FUNC100 (NUM (x, 8)); \
|
||||
FUNC100 (NUM (x, 9))
|
||||
|
||||
#define FUNC7000() \
|
||||
FUNC1000 (1); \
|
||||
FUNC1000 (2); \
|
||||
FUNC1000 (3); \
|
||||
FUNC1000 (4); \
|
||||
FUNC1000 (5); \
|
||||
FUNC1000 (6); \
|
||||
FUNC1000 (7);
|
||||
|
||||
#ifdef FUNC
|
||||
# undef FUNC
|
||||
#endif
|
||||
|
||||
#ifdef externnum
|
||||
# define FUNC(x) extern int CONCAT (retNum, x) (void)
|
||||
#endif
|
||||
|
||||
#ifdef definenum
|
||||
# define FUNC(x) int CONCAT (retNum, x) (void) { return x; }
|
||||
#endif
|
||||
|
||||
#ifdef callnum
|
||||
# define FUNC(x) CONCAT (retNum, x) (); sync_all (x)
|
||||
#endif
|
||||
|
||||
/* A value of 7000 functions is chosen as an arbitrarily large
|
||||
number of functions that will allow us enough attempts to
|
||||
verify lazy resolution operation. */
|
||||
FUNC7000 ();
|
||||
@@ -0,0 +1,109 @@
|
||||
/* Testing race while enabling lock elision.
|
||||
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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
static pthread_barrier_t barrier;
|
||||
static pthread_mutex_t mutex;
|
||||
static long long int iteration_count = 1000000;
|
||||
static unsigned int thread_count = 3;
|
||||
|
||||
static void *
|
||||
thr_func (void *arg)
|
||||
{
|
||||
long long int i;
|
||||
for (i = 0; i < iteration_count; i++)
|
||||
{
|
||||
if ((uintptr_t) arg == 0)
|
||||
{
|
||||
xpthread_mutex_destroy (&mutex);
|
||||
xpthread_mutex_init (&mutex, NULL);
|
||||
}
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
|
||||
/* Test if enabling lock elision works if it is enabled concurrently.
|
||||
There was a race in FORCE_ELISION macro which leads to either
|
||||
pthread_mutex_destroy returning EBUSY as the owner was recorded
|
||||
by pthread_mutex_lock - in "normal mutex" code path - but was not
|
||||
resetted in pthread_mutex_unlock - in "elision" code path.
|
||||
Or it leads to the assertion in nptl/pthread_mutex_lock.c:
|
||||
assert (mutex->__data.__owner == 0);
|
||||
Please ensure that the test is run with lock elision:
|
||||
export GLIBC_TUNABLES=glibc.elision.enable=1 */
|
||||
xpthread_mutex_lock (&mutex);
|
||||
xpthread_mutex_unlock (&mutex);
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
unsigned int i;
|
||||
printf ("Starting %d threads to run %lld iterations.\n",
|
||||
thread_count, iteration_count);
|
||||
|
||||
pthread_t *threads = xmalloc (thread_count * sizeof (pthread_t));
|
||||
xpthread_barrier_init (&barrier, NULL, thread_count);
|
||||
xpthread_mutex_init (&mutex, NULL);
|
||||
|
||||
for (i = 0; i < thread_count; i++)
|
||||
threads[i] = xpthread_create (NULL, thr_func, (void *) (uintptr_t) i);
|
||||
|
||||
for (i = 0; i < thread_count; i++)
|
||||
xpthread_join (threads[i]);
|
||||
|
||||
xpthread_barrier_destroy (&barrier);
|
||||
free (threads);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#define OPT_ITERATIONS 10000
|
||||
#define OPT_THREADS 10001
|
||||
#define CMDLINE_OPTIONS \
|
||||
{ "iterations", required_argument, NULL, OPT_ITERATIONS }, \
|
||||
{ "threads", required_argument, NULL, OPT_THREADS },
|
||||
static void
|
||||
cmdline_process (int c)
|
||||
{
|
||||
long long int arg = strtoll (optarg, NULL, 0);
|
||||
switch (c)
|
||||
{
|
||||
case OPT_ITERATIONS:
|
||||
if (arg > 0)
|
||||
iteration_count = arg;
|
||||
break;
|
||||
case OPT_THREADS:
|
||||
if (arg > 0 && arg < 100)
|
||||
thread_count = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#define CMDLINE_PROCESS cmdline_process
|
||||
#define TIMEOUT 50
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,87 @@
|
||||
/* Test rwlock with PREFER_WRITER_NONRECURSIVE_NP (bug 23861).
|
||||
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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
/* We choose 10 iterations because this happens to be able to trigger the
|
||||
stall on contemporary hardware. */
|
||||
#define LOOPS 10
|
||||
/* We need 3 threads to trigger bug 23861. One thread as a writer, and
|
||||
two reader threads. The test verifies that the second-to-last reader
|
||||
is able to notify the *last* reader that it should be done waiting.
|
||||
If the second-to-last reader fails to notify the last reader or does
|
||||
so incorrectly then the last reader may stall indefinitely. */
|
||||
#define NTHREADS 3
|
||||
|
||||
_Atomic int do_exit;
|
||||
pthread_rwlockattr_t mylock_attr;
|
||||
pthread_rwlock_t mylock;
|
||||
|
||||
void *
|
||||
run_loop (void *a)
|
||||
{
|
||||
while (!do_exit)
|
||||
{
|
||||
if (random () & 1)
|
||||
{
|
||||
xpthread_rwlock_wrlock (&mylock);
|
||||
xpthread_rwlock_unlock (&mylock);
|
||||
}
|
||||
else
|
||||
{
|
||||
xpthread_rwlock_rdlock (&mylock);
|
||||
xpthread_rwlock_unlock (&mylock);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
xpthread_rwlockattr_init (&mylock_attr);
|
||||
xpthread_rwlockattr_setkind_np (&mylock_attr,
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
xpthread_rwlock_init (&mylock, &mylock_attr);
|
||||
|
||||
for (int n = 0; n < LOOPS; n++)
|
||||
{
|
||||
pthread_t tids[NTHREADS];
|
||||
do_exit = 0;
|
||||
for (int i = 0; i < NTHREADS; i++)
|
||||
tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||
/* Let the threads run for some time. */
|
||||
sleep (1);
|
||||
printf ("Exiting...");
|
||||
fflush (stdout);
|
||||
do_exit = 1;
|
||||
for (int i = 0; i < NTHREADS; i++)
|
||||
xpthread_join (tids[i]);
|
||||
printf ("done.\n");
|
||||
}
|
||||
pthread_rwlock_destroy (&mylock);
|
||||
pthread_rwlockattr_destroy (&mylock_attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT (DEFAULT_TIMEOUT + 3 * LOOPS)
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,355 @@
|
||||
/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* For a full analysis see comment:
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14
|
||||
|
||||
Provided here for reference:
|
||||
|
||||
--- Analysis of pthread_rwlock_tryrdlock() stall ---
|
||||
A read lock begins to execute.
|
||||
|
||||
In __pthread_rwlock_rdlock_full:
|
||||
|
||||
We can attempt a read lock, but find that the lock is
|
||||
in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit
|
||||
is set), and the lock is held by a primary writer
|
||||
(PTHREAD_RWLOCK_WRLOCKED is set). In this case we must
|
||||
wait for explicit hand over from the writer to us or
|
||||
one of the other waiters. The read lock threads are
|
||||
about to execute:
|
||||
|
||||
341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers,
|
||||
342 (1 << PTHREAD_RWLOCK_READER_SHIFT))
|
||||
343 + (1 << PTHREAD_RWLOCK_READER_SHIFT));
|
||||
|
||||
An unlock beings to execute.
|
||||
|
||||
Then in __pthread_rwlock_wrunlock:
|
||||
|
||||
547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
...
|
||||
549 while (!atomic_compare_exchange_weak_release
|
||||
550 (&rwlock->__data.__readers, &r,
|
||||
551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||
553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||
554 {
|
||||
...
|
||||
556 }
|
||||
|
||||
We clear PTHREAD_RWLOCK_WRLOCKED, and if there are
|
||||
no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE.
|
||||
|
||||
Back in the read lock.
|
||||
|
||||
The read lock adjusts __readres as above.
|
||||
|
||||
383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0
|
||||
384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||
385 {
|
||||
...
|
||||
390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r,
|
||||
391 r ^ PTHREAD_RWLOCK_WRPHASE))
|
||||
392 {
|
||||
|
||||
And then attemps to start the read phase.
|
||||
|
||||
Assume there happens to be a tryrdlock at this point, noting
|
||||
that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE
|
||||
is 1. So the try lock attemps to start the read phase.
|
||||
|
||||
In __pthread_rwlock_tryrdlock:
|
||||
|
||||
44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
45 {
|
||||
...
|
||||
49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
|
||||
50 && (rwlock->__data.__flags
|
||||
51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
|
||||
52 return EBUSY;
|
||||
53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
|
||||
54 }
|
||||
...
|
||||
89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
|
||||
90 &r, rnew));
|
||||
|
||||
And succeeds.
|
||||
|
||||
Back in the write unlock:
|
||||
|
||||
557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0)
|
||||
558 {
|
||||
...
|
||||
563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||
564 & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||
566 }
|
||||
|
||||
We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero
|
||||
and don't wake anyone. This is OK because we handed
|
||||
over to the trylock. It will be the trylock's responsibility
|
||||
to wake any waiters.
|
||||
|
||||
Back in the read lock:
|
||||
|
||||
The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because
|
||||
the __readers value was adjusted by the trylock, and so it falls through
|
||||
to waiting on the lock for explicit handover from either a new writer
|
||||
or a new reader.
|
||||
|
||||
448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||
450 abstime, private);
|
||||
|
||||
We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex
|
||||
is in use.
|
||||
|
||||
At this point we have readers waiting on the read lock
|
||||
to unlock. The wrlock is done. The trylock is finishing
|
||||
the installation of the read phase.
|
||||
|
||||
92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
93 {
|
||||
...
|
||||
105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
106 }
|
||||
|
||||
The trylock does note that we were the one that
|
||||
installed the read phase, but the comments are not
|
||||
correct, the execution ordering above shows that
|
||||
readers might indeed be waiting, and they are.
|
||||
|
||||
The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED,
|
||||
and the waiting reader is never worken becuase as noted
|
||||
above it is conditional on the futex being used.
|
||||
|
||||
The solution is for the trylock thread to inspect
|
||||
PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers.
|
||||
|
||||
--- Analysis of pthread_rwlock_trywrlock() stall ---
|
||||
|
||||
A write lock begins to execute, takes the write lock,
|
||||
and then releases the lock...
|
||||
|
||||
In pthread_rwlock_wrunlock():
|
||||
|
||||
547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
...
|
||||
549 while (!atomic_compare_exchange_weak_release
|
||||
550 (&rwlock->__data.__readers, &r,
|
||||
551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||
553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||
554 {
|
||||
...
|
||||
556 }
|
||||
|
||||
... leaving it in the write phase with zero readers
|
||||
(the case where we leave the write phase in place
|
||||
during a write unlock).
|
||||
|
||||
A write trylock begins to execute.
|
||||
|
||||
In __pthread_rwlock_trywrlock:
|
||||
|
||||
40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||
41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0)
|
||||
42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0))))
|
||||
43 {
|
||||
|
||||
The lock is not locked.
|
||||
|
||||
There are no readers.
|
||||
|
||||
45 if (atomic_compare_exchange_weak_acquire (
|
||||
46 &rwlock->__data.__readers, &r,
|
||||
47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||
|
||||
We atomically install the write phase and we take the
|
||||
exclusive write lock.
|
||||
|
||||
48 {
|
||||
49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||
|
||||
We get this far.
|
||||
|
||||
A reader lock begins to execute.
|
||||
|
||||
In pthread_rwlock_rdlock:
|
||||
|
||||
437 for (;;)
|
||||
438 {
|
||||
439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
441 {
|
||||
442 int private = __pthread_rwlock_get_private (rwlock);
|
||||
443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
444 && (!atomic_compare_exchange_weak_relaxed
|
||||
445 (&rwlock->__data.__wrphase_futex,
|
||||
446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)))
|
||||
447 continue;
|
||||
448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||
450 abstime, private);
|
||||
|
||||
We are in a write phase, so the while() on line 439 is true.
|
||||
|
||||
The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set
|
||||
since this is the first reader to lock.
|
||||
|
||||
The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED
|
||||
on the expectation that this reader will be woken during
|
||||
the handoff.
|
||||
|
||||
Back in pthread_rwlock_trywrlock:
|
||||
|
||||
50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
51 atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
52 THREAD_GETMEM (THREAD_SELF, tid));
|
||||
53 return 0;
|
||||
54 }
|
||||
...
|
||||
57 }
|
||||
|
||||
We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED,
|
||||
and so in the unlock we will not awaken the waiting reader.
|
||||
|
||||
The solution to this is to realize that if we did not start the write
|
||||
phase we need not write 1 or any other value to __wrphase_futex.
|
||||
This ensures that any readers (which saw __wrphase_futex != 0) can
|
||||
set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to
|
||||
wake them.
|
||||
|
||||
If we installed the write phase then all other readers are looping
|
||||
here:
|
||||
|
||||
In __pthread_rwlock_rdlock_full:
|
||||
|
||||
437 for (;;)
|
||||
438 {
|
||||
439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
441 {
|
||||
...
|
||||
508 }
|
||||
|
||||
waiting for the write phase to be installed or removed before they
|
||||
can begin waiting on __wrphase_futex (part of the algorithm), or
|
||||
taking a concurrent read lock, and thus we can safely write 1 to
|
||||
__wrphase_futex.
|
||||
|
||||
If we did not install the write phase then the readers may already
|
||||
be waiting on the futex, the original writer wrote 1 to __wrphase_futex
|
||||
as part of starting the write phase, and we cannot also write 1
|
||||
without loosing the PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||
|
||||
---
|
||||
|
||||
Summary for the pthread_rwlock_tryrdlock() stall:
|
||||
|
||||
The stall is caused by pthread_rwlock_tryrdlock failing to check
|
||||
that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex
|
||||
and then waking the futex.
|
||||
|
||||
The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||
correctly woken. Before the fix the test stalls as readers can
|
||||
wait forever on __wrphase_futex. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <support/xthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* We need only one lock to reproduce the issue. We will need multiple
|
||||
threads to get the exact case where we have a read, try, and unlock
|
||||
all interleaving to produce the case where the readers are waiting
|
||||
and the try fails to wake them. */
|
||||
pthread_rwlock_t onelock;
|
||||
|
||||
/* The number of threads is arbitrary but empirically chosen to have
|
||||
enough threads that we see the condition where waiting readers are
|
||||
not woken by a successful tryrdlock. */
|
||||
#define NTHREADS 32
|
||||
|
||||
_Atomic int do_exit;
|
||||
|
||||
void *
|
||||
run_loop (void *arg)
|
||||
{
|
||||
int i = 0, ret;
|
||||
while (!do_exit)
|
||||
{
|
||||
/* Arbitrarily choose if we are the writer or reader. Choose a
|
||||
high enough ratio of readers to writers to make it likely
|
||||
that readers block (and eventually are susceptable to
|
||||
stalling).
|
||||
|
||||
If we are a writer, take the write lock, and then unlock.
|
||||
If we are a reader, try the lock, then lock, then unlock. */
|
||||
if ((i % 8) != 0)
|
||||
xpthread_rwlock_wrlock (&onelock);
|
||||
else
|
||||
{
|
||||
if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0)
|
||||
{
|
||||
if (ret == EBUSY)
|
||||
xpthread_rwlock_rdlock (&onelock);
|
||||
else
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
/* Thread does some work and then unlocks. */
|
||||
xpthread_rwlock_unlock (&onelock);
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
int i;
|
||||
pthread_t tids[NTHREADS];
|
||||
xpthread_rwlock_init (&onelock, NULL);
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||
/* Run for some amount of time. Empirically speaking exercising
|
||||
the stall via pthread_rwlock_tryrdlock is much harder, and on
|
||||
a 3.5GHz 4 core x86_64 VM system it takes somewhere around
|
||||
20-200s to stall, approaching 100% stall past 200s. We can't
|
||||
wait that long for a regression test so we just test for 20s,
|
||||
and expect the stall to happen with a 5-10% chance (enough for
|
||||
developers to see). */
|
||||
sleep (20);
|
||||
/* Then exit. */
|
||||
printf ("INFO: Exiting...\n");
|
||||
do_exit = 1;
|
||||
/* If any readers stalled then we will timeout waiting for them. */
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
xpthread_join (tids[i]);
|
||||
printf ("INFO: Done.\n");
|
||||
xpthread_rwlock_destroy (&onelock);
|
||||
printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT 30
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
|
||||
|
||||
Summary for the pthread_rwlock_trywrlock() stall:
|
||||
|
||||
The stall is caused by pthread_rwlock_trywrlock setting
|
||||
__wrphase_futex futex to 1 and loosing the
|
||||
PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||
|
||||
The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||
correctly woken. Before the fix the test stalls as readers can
|
||||
wait forever on __wrphase_futex. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <support/xthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* We need only one lock to reproduce the issue. We will need multiple
|
||||
threads to get the exact case where we have a read, try, and unlock
|
||||
all interleaving to produce the case where the readers are waiting
|
||||
and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
|
||||
subsequent unlock fails to wake them. */
|
||||
pthread_rwlock_t onelock;
|
||||
|
||||
/* The number of threads is arbitrary but empirically chosen to have
|
||||
enough threads that we see the condition where waiting readers are
|
||||
not woken by a successful unlock. */
|
||||
#define NTHREADS 32
|
||||
|
||||
_Atomic int do_exit;
|
||||
|
||||
void *
|
||||
run_loop (void *arg)
|
||||
{
|
||||
int i = 0, ret;
|
||||
while (!do_exit)
|
||||
{
|
||||
/* Arbitrarily choose if we are the writer or reader. Choose a
|
||||
high enough ratio of readers to writers to make it likely
|
||||
that readers block (and eventually are susceptable to
|
||||
stalling).
|
||||
|
||||
If we are a writer, take the write lock, and then unlock.
|
||||
If we are a reader, try the lock, then lock, then unlock. */
|
||||
if ((i % 8) != 0)
|
||||
{
|
||||
if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
|
||||
{
|
||||
if (ret == EBUSY)
|
||||
xpthread_rwlock_wrlock (&onelock);
|
||||
else
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
xpthread_rwlock_rdlock (&onelock);
|
||||
/* Thread does some work and then unlocks. */
|
||||
xpthread_rwlock_unlock (&onelock);
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
int i;
|
||||
pthread_t tids[NTHREADS];
|
||||
xpthread_rwlock_init (&onelock, NULL);
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||
/* Run for some amount of time. The pthread_rwlock_tryrwlock stall
|
||||
is very easy to trigger and happens in seconds under the test
|
||||
conditions. */
|
||||
sleep (10);
|
||||
/* Then exit. */
|
||||
printf ("INFO: Exiting...\n");
|
||||
do_exit = 1;
|
||||
/* If any readers stalled then we will timeout waiting for them. */
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
xpthread_join (tids[i]);
|
||||
printf ("INFO: Done.\n");
|
||||
xpthread_rwlock_destroy (&onelock);
|
||||
printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+49
-41
@@ -19,12 +19,16 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <support/support.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
struct test_s
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
__attribute__ ((aligned(0x20))) int a;
|
||||
__attribute__ ((aligned(0x200))) int b;
|
||||
};
|
||||
|
||||
#define INIT_A 1
|
||||
@@ -36,15 +40,34 @@ __thread struct test_s s __attribute__ ((tls_model ("initial-exec"))) =
|
||||
.b = INIT_B
|
||||
};
|
||||
|
||||
/* Use noinline in combination with not static to ensure that the
|
||||
alignment check is really done. Otherwise it was optimized out! */
|
||||
__attribute__ ((noinline)) void
|
||||
check_alignment (const char *thr_name, const char *ptr_name,
|
||||
int *ptr, int alignment)
|
||||
{
|
||||
uintptr_t offset_aligment = ((uintptr_t) ptr) & (alignment - 1);
|
||||
if (offset_aligment)
|
||||
{
|
||||
FAIL_EXIT1 ("%s (%p) is not 0x%x-byte aligned in %s thread\n",
|
||||
ptr_name, ptr, alignment, thr_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_s (const char *thr_name)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
FAIL_EXIT1 ("initial value of s in %s thread wrong\n", thr_name);
|
||||
|
||||
check_alignment (thr_name, "s.a", &s.a, 0x20);
|
||||
check_alignment (thr_name, "s.b", &s.b, 0x200);
|
||||
}
|
||||
|
||||
static void *
|
||||
tf (void *arg)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
{
|
||||
puts ("initial value of s in child thread wrong");
|
||||
exit (1);
|
||||
}
|
||||
check_s ("child");
|
||||
|
||||
++s.a;
|
||||
|
||||
@@ -55,25 +78,14 @@ tf (void *arg)
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
{
|
||||
puts ("initial value of s in main thread wrong");
|
||||
exit (1);
|
||||
}
|
||||
check_s ("main");
|
||||
|
||||
pthread_attr_t a;
|
||||
|
||||
if (pthread_attr_init (&a) != 0)
|
||||
{
|
||||
puts ("attr_init failed");
|
||||
exit (1);
|
||||
}
|
||||
xpthread_attr_init (&a);
|
||||
|
||||
if (pthread_attr_setstacksize (&a, 1 * 1024 * 1024) != 0)
|
||||
{
|
||||
puts ("attr_setstacksize failed");
|
||||
return 1;
|
||||
}
|
||||
#define STACK_SIZE (1 * 1024 * 1024)
|
||||
xpthread_attr_setstacksize (&a, STACK_SIZE);
|
||||
|
||||
#define N 10
|
||||
int i;
|
||||
@@ -83,29 +95,25 @@ do_test (void)
|
||||
pthread_t th[M];
|
||||
int j;
|
||||
for (j = 0; j < M; ++j, ++s.a)
|
||||
if (pthread_create (&th[j], &a, tf, NULL) != 0)
|
||||
{
|
||||
puts ("pthread_create failed");
|
||||
exit (1);
|
||||
}
|
||||
th[j] = xpthread_create (&a, tf, NULL);
|
||||
|
||||
for (j = 0; j < M; ++j)
|
||||
if (pthread_join (th[j], NULL) != 0)
|
||||
{
|
||||
puts ("pthread_join failed");
|
||||
exit (1);
|
||||
}
|
||||
xpthread_join (th[j]);
|
||||
}
|
||||
|
||||
if (pthread_attr_destroy (&a) != 0)
|
||||
{
|
||||
puts ("attr_destroy failed");
|
||||
exit (1);
|
||||
}
|
||||
/* Also check the alignment of the tls variables if a misaligned stack is
|
||||
specified. */
|
||||
pthread_t th;
|
||||
void *thr_stack = NULL;
|
||||
thr_stack = xposix_memalign (0x200, STACK_SIZE + 1);
|
||||
xpthread_attr_setstack (&a, thr_stack + 1, STACK_SIZE);
|
||||
th = xpthread_create (&a, tf, NULL);
|
||||
xpthread_join (th);
|
||||
free (thr_stack);
|
||||
|
||||
xpthread_attr_destroy (&a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
||||
#include <support/test-driver.c>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#define USE_PTHREADS 1
|
||||
#include "../elf/tst-unwind-main.c"
|
||||
+1
-1
@@ -36,7 +36,7 @@ nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
|
||||
getsrvbynm_r getsrvbypt_r servicescache \
|
||||
dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
|
||||
xmalloc xstrdup aicache initgrcache gai res_hconf \
|
||||
netgroupcache
|
||||
netgroupcache nscd-inet_addr
|
||||
|
||||
ifeq ($(build-nscd)$(have-thread-library),yesyes)
|
||||
|
||||
|
||||
+2
-1
@@ -304,7 +304,8 @@ static int
|
||||
check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
|
||||
enum usekey use, ref_t start, size_t len)
|
||||
{
|
||||
assert (len >= 2);
|
||||
if (len < 2)
|
||||
return 0;
|
||||
|
||||
if (start > first_free || start + len > first_free
|
||||
|| (start & BLOCK_ALIGN_M1))
|
||||
|
||||
+6
-1
@@ -19,7 +19,6 @@
|
||||
|
||||
/* This file uses the getaddrinfo code but it compiles it without NSCD
|
||||
support. We just need a few symbol renames. */
|
||||
#define __inet_aton inet_aton
|
||||
#define __ioctl ioctl
|
||||
#define __getsockname getsockname
|
||||
#define __socket socket
|
||||
@@ -34,6 +33,12 @@
|
||||
#define __getifaddrs getifaddrs
|
||||
#define __freeifaddrs freeifaddrs
|
||||
|
||||
/* We do not want to export __inet_aton_exact. Get the prototype and
|
||||
change its visibility to hidden. */
|
||||
#include <arpa/inet.h>
|
||||
__typeof__ (__inet_aton_exact) __inet_aton_exact
|
||||
__attribute__ ((visibility ("hidden")));
|
||||
|
||||
/* We are nscd, so we don't want to be talking to ourselves. */
|
||||
#undef USE_NSCD
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
#define HAVE_LOOKUP_BUFFER 1
|
||||
#define HAVE_AF 1
|
||||
|
||||
#define __inet_aton inet_aton
|
||||
|
||||
/* We are nscd, so we don't want to be talking to ourselves. */
|
||||
#undef USE_NSCD
|
||||
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
|
||||
|
||||
/* This is really only for debugging. */
|
||||
if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
|
||||
__libc_fatal ("illegal status in internal_getgrouplist");
|
||||
__libc_fatal ("Illegal status in internal_getgrouplist.\n");
|
||||
|
||||
any_success |= status == NSS_STATUS_SUCCESS;
|
||||
|
||||
|
||||
+29
-13
@@ -113,7 +113,8 @@ do_notfound (struct database_dyn *db, int fd, request_header *req,
|
||||
static time_t
|
||||
addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
struct datahead *dh, struct dataset **resultp)
|
||||
struct datahead *dh, struct dataset **resultp,
|
||||
void **tofreep)
|
||||
{
|
||||
if (__glibc_unlikely (debug_level > 0))
|
||||
{
|
||||
@@ -139,6 +140,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
size_t group_len = strlen (key) + 1;
|
||||
struct name_list *first_needed
|
||||
= alloca (sizeof (struct name_list) + group_len);
|
||||
*tofreep = NULL;
|
||||
|
||||
if (netgroup_database == NULL
|
||||
&& __nss_database_lookup ("netgroup", NULL, NULL, &netgroup_database))
|
||||
@@ -151,6 +153,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
|
||||
memset (&data, '\0', sizeof (data));
|
||||
buffer = xmalloc (buflen);
|
||||
*tofreep = buffer;
|
||||
first_needed->next = first_needed;
|
||||
memcpy (first_needed->name, key, group_len);
|
||||
data.needed_groups = first_needed;
|
||||
@@ -439,8 +442,6 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
out:
|
||||
free (buffer);
|
||||
|
||||
*resultp = dataset;
|
||||
|
||||
return timeout;
|
||||
@@ -477,8 +478,12 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
group, group_len,
|
||||
db, uid);
|
||||
time_t timeout;
|
||||
void *tofree;
|
||||
if (result != NULL)
|
||||
timeout = result->head.timeout;
|
||||
{
|
||||
timeout = result->head.timeout;
|
||||
tofree = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
request_header req_get =
|
||||
@@ -487,7 +492,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
.key_len = group_len
|
||||
};
|
||||
timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
|
||||
&result);
|
||||
&result, &tofree);
|
||||
}
|
||||
|
||||
struct indataset
|
||||
@@ -560,7 +565,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
++dh->nreloads;
|
||||
if (cacheable)
|
||||
pthread_rwlock_unlock (&db->lock);
|
||||
return timeout;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (he == NULL)
|
||||
@@ -596,17 +601,30 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
dh->usable = false;
|
||||
}
|
||||
|
||||
out:
|
||||
free (tofree);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
||||
static time_t
|
||||
addgetnetgrentX_ignore (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
struct datahead *dh)
|
||||
{
|
||||
struct dataset *ignore;
|
||||
void *tofree;
|
||||
time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh,
|
||||
&ignore, &tofree);
|
||||
free (tofree);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
void
|
||||
addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
|
||||
void *key, uid_t uid)
|
||||
{
|
||||
struct dataset *ignore;
|
||||
|
||||
addgetnetgrentX (db, fd, req, key, uid, NULL, NULL, &ignore);
|
||||
addgetnetgrentX_ignore (db, fd, req, key, uid, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -619,10 +637,8 @@ readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
|
||||
.type = GETNETGRENT,
|
||||
.key_len = he->len
|
||||
};
|
||||
struct dataset *ignore;
|
||||
|
||||
return addgetnetgrentX (db, -1, &req, db->data + he->key, he->owner, he, dh,
|
||||
&ignore);
|
||||
return addgetnetgrentX_ignore
|
||||
(db, -1, &req, db->data + he->key, he->owner, he, dh);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Test the fallback implementation of copy_file_range.
|
||||
Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
||||
/* Legacy IPv4 text-to-address functions. Version for nscd.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
@@ -16,15 +16,17 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Get the declaration of the official copy_of_range function. */
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* Compile a local version of copy_file_range. */
|
||||
#define COPY_FILE_RANGE_DECL static
|
||||
#define COPY_FILE_RANGE copy_file_range_compat
|
||||
#include <io/copy_file_range-compat.c>
|
||||
/* We do not want to export __inet_aton_exact. Get the prototype and
|
||||
change the visibility to hidden. */
|
||||
#include <arpa/inet.h>
|
||||
__typeof__ (__inet_aton_exact) __inet_aton_exact
|
||||
__attribute__ ((visibility ("hidden")));
|
||||
|
||||
/* Re-use the test, but run it against copy_file_range_compat defined
|
||||
above. */
|
||||
#define copy_file_range copy_file_range_compat
|
||||
#include "tst-copy_file_range.c"
|
||||
/* Do not provide definitions of the public symbols exported from
|
||||
libc. */
|
||||
#undef weak_alias
|
||||
#define weak_alias(from, to)
|
||||
|
||||
#include <resolv/inet_addr.c>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user