Compare commits

...
Author SHA1 Message Date
Florian Weimer a0408ec51e libio: Disable vtable validation for pre-2.1 interposed handles [BZ #25203]
Commit c402355dfa ("libio: Disable
vtable validation in case of interposition [BZ #23313]") only covered
the interposable glibc 2.1 handles, in libio/stdfiles.c.  The
parallel code in libio/oldstdfiles.c needs similar detection logic.

Fixes (again) commit db3476aff1
("libio: Implement vtable verification [BZ #20191]").

Change-Id: Ief6f9f17e91d1f7263421c56a7dc018f4f595c21
(cherry picked from commit cb61630ed7)
2019-11-28 16:21:26 +01:00
Florian Weimer 8440c51d89 libio: Disable vtable validation in case of interposition [BZ #23313]
(cherry picked from commit c402355dfa)
2019-11-28 16:21:26 +01:00
Marcin Kościelnicki e73ac9ce90 rtld: Check __libc_enable_secure before honoring LD_PREFER_MAP_32BIT_EXEC (CVE-2019-19126) [BZ #25204]
The problem was introduced in glibc 2.23, in commit
b9eb92ab05
("Add Prefer_MAP_32BIT_EXEC to map executable pages with MAP_32BIT").

(cherry picked from commit d5dfad4326)
2019-11-22 13:54:56 +01:00
Dragan Mladjenovic d83ba68a3e mips: Force RWX stack for hard-float builds that can run on pre-4.8 kernels
Linux/Mips kernels prior to 4.8 could potentially crash the user
process when doing FPU emulation while running on non-executable
user stack.

Currently, gcc doesn't emit .note.GNU-stack for mips, but that will
change in the future. To ensure that glibc can be used with such
future gcc, without silently resulting in binaries that might crash
in runtime, this patch forces RWX stack for all built objects if
configured to run against minimum kernel version less than 4.8.

	* sysdeps/unix/sysv/linux/mips/Makefile
	(test-xfail-check-execstack):
	Move under mips-has-gnustack != yes.
	(CFLAGS-.o*, ASFLAGS-.o*): New rules.
	Apply -Wa,-execstack if mips-force-execstack == yes.
	* sysdeps/unix/sysv/linux/mips/configure: Regenerated.
	* sysdeps/unix/sysv/linux/mips/configure.ac
	(mips-force-execstack): New var.
	Set to yes for hard-float builds with minimum_kernel < 4.8.0
	or minimum_kernel not set at all.
	(mips-has-gnustack): New var.
	Use value of libc_cv_as_noexecstack
	if mips-force-execstack != yes, otherwise set to no.

(cherry picked from commit 33bc9efd91)
2019-11-05 08:56:16 -03:00
Andreas Schwab 60bc81ba47 Fix crash in _IO_wfile_sync (bug 20568)
When computing the length of the converted part of the stdio buffer, use
the number of consumed wide characters, not the (negative) distance to the
end of the wide buffer.

(cherry picked from commit 32ff397533)
2019-05-16 10:50:36 +02:00
Stefan Liebler aa6e767582 Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock. [BZ #24180]
While debugging a kernel warning, Thomas Gleixner, Sebastian Sewior and
Heiko Carstens found a bug in pthread_mutex_trylock due to misordered
instructions:
140:   a5 1b 00 01             oill    %r1,1
144:   e5 48 a0 f0 00 00       mvghi   240(%r10),0   <--- THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
14a:   e3 10 a0 e0 00 24       stg     %r1,224(%r10) <--- last THREAD_SETMEM of ENQUEUE_MUTEX_PI

vs (with compiler barriers):
140:   a5 1b 00 01             oill    %r1,1
144:   e3 10 a0 e0 00 24       stg     %r1,224(%r10)
14a:   e5 48 a0 f0 00 00       mvghi   240(%r10),0

Please have a look at the discussion:
"Re: WARN_ON_ONCE(!new_owner) within wake_futex_pi() triggerede"
(https://lore.kernel.org/lkml/20190202112006.GB3381@osiris/)

This patch is introducing the same compiler barriers and comments
for pthread_mutex_trylock as introduced for pthread_mutex_lock and
pthread_mutex_timedlock by commit 8f9450a0b7
"Add compiler barriers around modifications of the robust mutex list."

ChangeLog:

	[BZ #24180]
	* nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock):
	Add compiler barriers and comments.

(cherry picked from commit 823624bdc4)
2019-02-07 15:54:23 +01:00
H.J. Lu 2ad78b78d3 x86-64 memcmp: Use unsigned Jcc instructions on size [BZ #24155]
Since the size argument is unsigned. we should use unsigned Jcc
instructions, instead of signed, to check size.

Tested on x86-64 and x32, with and without --disable-multi-arch.

	[BZ #24155]
	CVE-2019-7309
	* NEWS: Updated for CVE-2019-7309.
	* sysdeps/x86_64/memcmp.S: Use RDX_LP for size.  Clear the
	upper 32 bits of RDX register for x32.  Use unsigned Jcc
	instructions, instead of signed.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcmp-2.
	* sysdeps/x86_64/x32/tst-size_t-memcmp-2.c: New test.

(cherry picked from commit 3f635fb433)
2019-02-04 11:42:15 -08:00
H.J. Lu f728a542bf x86-64 strnlen/wcsnlen: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes strnlen/wcsnlen for x32.  Tested on x86-64 and x32.  On
x86-64, libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/strlen.S: Use RSI_LP for length.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-strnlen.
	* sysdeps/x86_64/x32/tst-size_t-strnlen.c: New file.

(cherry picked from commit 5165de69c0)
2019-02-02 05:44:06 -08:00
H.J. Lu 3a84199109 x86-64 strncpy: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes strncpy for x32.  Tested on x86-64 and x32.  On x86-64,
libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: Use RDX_LP
	for length.
	* sysdeps/x86_64/multiarch/strcpy-ssse3.S: Likewise.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-strncpy.
	* sysdeps/x86_64/x32/tst-size_t-strncpy.c: New file.

(cherry picked from commit c7c54f65b0)
2019-02-02 05:38:32 -08:00
H.J. Lu c69b892fea x86-64 strncmp family: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes the strncmp family for x32.  Tested on x86-64 and x32.
On x86-64, libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/multiarch/strcmp-sse42.S: Use RDX_LP for length.
	* sysdeps/x86_64/strcmp.S: Likewise.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-strncasecmp,
	tst-size_t-strncmp and tst-size_t-wcsncmp.
	* sysdeps/x86_64/x32/tst-size_t-strncasecmp.c: New file.
	* sysdeps/x86_64/x32/tst-size_t-strncmp.c: Likewise.
	* sysdeps/x86_64/x32/tst-size_t-wcsncmp.c: Likewise.

(cherry picked from commit ee915088a0)
2019-02-02 05:36:53 -08:00
H.J. Lu 80647620ce x86-64 memset/wmemset: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes memset/wmemset for x32.  Tested on x86-64 and x32.  On
x86-64, libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/multiarch/memset-avx512-no-vzeroupper.S: Use
	RDX_LP for length.  Clear the upper 32 bits of RDX register.
	* sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S: Likewise.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memset.
	* sysdeps/x86_64/x32/tst-size_t-memset.c: New file.

(cherry picked from commit 82d0b4a4d7)
2019-02-02 05:33:08 -08:00
H.J. Lu 59c463c4ea x86-64 memrchr: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes memrchr for x32.  Tested on x86-64 and x32.  On x86-64,
libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/memrchr.S: Use RDX_LP for length.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memrchr.
	* sysdeps/x86_64/x32/tst-size_t-memrchr.c: New file.

(cherry picked from commit ecd8b842cf)
2019-02-02 05:20:08 -08:00
H.J. Lu e6597e77d1 x86-64 memcpy: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes memcpy for x32.  Tested on x86-64 and x32.  On x86-64,
libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/multiarch/memcpy-ssse3-back.S: Use RDX_LP for
	length.  Clear the upper 32 bits of RDX register.
	* sysdeps/x86_64/multiarch/memcpy-ssse3.S: Likewise.
	* sysdeps/x86_64/multiarch/memmove-avx512-no-vzeroupper.S:
	Likewise.
	* sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:
	Likewise.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcpy.
	tst-size_t-wmemchr.
	* sysdeps/x86_64/x32/tst-size_t-memcpy.c: New file.

(cherry picked from commit 231c56760c)
2019-02-02 05:18:04 -08:00
H.J. Lu 97a5229eaf x86-64 memcmp/wmemcmp: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes memcmp/wmemcmp for x32.  Tested on x86-64 and x32.  On
x86-64, libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/multiarch/memcmp-sse4.S: Use RDX_LP for length.
	Clear the upper 32 bits of RDX register.
	* sysdeps/x86_64/multiarch/memcmp-ssse3.S: Likewise.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcmp and
	tst-size_t-wmemcmp.
	* sysdeps/x86_64/x32/tst-size_t-memcmp.c: New file.
	* sysdeps/x86_64/x32/tst-size_t-wmemcmp.c: Likewise.

(cherry picked from commit b304fc201d)
2019-02-01 18:34:26 -08:00
H.J. Lu 8d525c4a76 x86-64 memchr/wmemchr: Properly handle the length parameter [BZ #24097]
On x32, the size_t parameter may be passed in the lower 32 bits of a
64-bit register with the non-zero upper 32 bits.  The string/memory
functions written in assembly can only use the lower 32 bits of a
64-bit register as length or must clear the upper 32 bits before using
the full 64-bit register for length.

This pach fixes memchr/wmemchr for x32.  Tested on x86-64 and x32.  On
x86-64, libc.so is the same with and withou the fix.

	[BZ #24097]
	CVE-2019-6488
	* sysdeps/x86_64/memchr.S: Use RDX_LP for length.  Clear the
	upper 32 bits of RDX register.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memchr.
	* sysdeps/x86_64/x32/test-size_t.h: New file.
	* sysdeps/x86_64/x32/tst-size_t-memchr.c: Likewise.

(cherry picked from commit 97700a34f3)
2019-02-01 16:42:32 -08:00
Florian Weimer 6b95c49d8e malloc: Always call memcpy in _int_realloc [BZ #24027]
This commit removes the custom memcpy implementation from _int_realloc
for small chunk sizes.  The ncopies variable has the wrong type, and
an integer wraparound could cause the existing code to copy too few
elements (leaving the new memory region mostly uninitialized).
Therefore, removing this code fixes bug 24027.

(cherry picked from commit b50dd3bc8c)
2019-01-01 10:49:43 +01:00
Stefan Liebler 7118ba3493 Fix segfault in maybe_script_execute.
If glibc is built with gcc 8 and -march=z900,
the testcase posix/tst-spawn4-compat crashes with a segfault.

In function maybe_script_execute, the new_argv array is dynamically
initialized on stack with (argc + 1) elements.
The function wants to add _PATH_BSHELL as the first argument
and writes out of bounds of new_argv.
There is an off-by-one because maybe_script_execute fails to count
the terminating NULL when sizing new_argv.

ChangeLog:

	* sysdeps/unix/sysv/linux/spawni.c (maybe_script_execute):
	Increment size of new_argv by one.

(cherry picked from commit 28669f86f6)
2018-09-10 14:29:21 +02:00
Martin Kuchta 1324858a3f pthread_cond_broadcast: Fix waiters-after-spinning case [BZ #23538]
(cherry picked from commit 99ea93ca31)
2018-08-27 19:21:10 +02:00
Tulio Magno Quites Machado Filho 71616d59ec powerpc: Fix syscalls during early process initialization [BZ #22685]
The tunables framework needs to execute syscall early in process
initialization, before the TCB is available for consumption.  This
behavior conflicts with powerpc{|64|64le}'s lock elision code, that
checks the TCB before trying to abort transactions immediately before
executing a syscall.

This patch adds a powerpc-specific implementation of __access_noerrno
that does not abort transactions before the executing syscall.

Tested on powerpc{|64|64le}.

	[BZ #22685]
	* sysdeps/powerpc/powerpc32/sysdep.h (ABORT_TRANSACTION_IMPL): Renamed
	from ABORT_TRANSACTION.
	(ABORT_TRANSACTION): Redirect to ABORT_TRANSACTION_IMPL.
	* sysdeps/powerpc/powerpc64/sysdep.h (ABORT_TRANSACTION,
	ABORT_TRANSACTION_IMPL): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/not-errno.h: New file.  Reuse
	Linux code, but remove the code that aborts transactions.

Signed-off-by: Tulio Magno Quites Machado Filho <tuliom@linux.vnet.ibm.com>
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
(cherry picked from commit 4612268a0a)
2018-02-12 11:34:48 -02:00
Adhemerval Zanella e84db8f9eb tunables: Use direct syscall for access (BZ#21744)
The function maybe_enable_malloc_check, which is called by
__tunables_init, calls __access_noerrno.  It isn't problem when
symbol is is in ld.so, which has a special version of __access_noerrno
without stack protector.  But when glibc is built with stack protector,
maybe_enable_malloc_check in libc.a can't call the regular version of
__access_noerrno with stack protector.

This patch changes how Linux defines the __access_noerrno to be an
inline call instead and thus preventing defining different build
rules for ld/static and shared.

	H.J. Lu  <hongjiu.lu@intel.com>
	Adhemerval Zanella  <adhemerval.zanella@linaro.org>

	[BZ #21744]
	* elf/dl-tunables.c: Include not-errno.h header.
	* include/unistd.h (__access_noerrno): Remove definition.
	* sysdeps/unix/sysv/linux/access.c (__access_noerrno): Likewise.
	* sysdeps/generic/not-errno.h: New file.
	* sysdeps/unix/sysv/linux/not-errno.h: Likewise.

(cherry picked from commit 95a7339258)
2018-02-12 11:34:47 -02:00
Arjun Shankar 9331dbdcd7 Fix integer overflows in internal memalign and malloc [BZ #22343] [BZ #22774]
When posix_memalign is called with an alignment less than MALLOC_ALIGNMENT
and a requested size close to SIZE_MAX, it falls back to malloc code
(because the alignment of a block returned by malloc is sufficient to
satisfy the call).  In this case, an integer overflow in _int_malloc leads
to posix_memalign incorrectly returning successfully.

Upon fixing this and writing a somewhat thorough regression test, it was
discovered that when posix_memalign is called with an alignment larger than
MALLOC_ALIGNMENT (so it uses _int_memalign instead) and a requested size
close to SIZE_MAX, a different integer overflow in _int_memalign leads to
posix_memalign incorrectly returning successfully.

Both integer overflows affect other memory allocation functions that use
_int_malloc (one affected malloc in x86) or _int_memalign as well.

This commit fixes both integer overflows.  In addition to this, it adds a
regression test to guard against false successful allocations by the
following memory allocation functions when called with too-large allocation
sizes and, where relevant, various valid alignments:
malloc, realloc, calloc, memalign, posix_memalign, aligned_alloc, valloc,
and pvalloc.

(cherry picked from commit 8e448310d7)
2018-02-09 12:54:03 +01:00
H.J. Lu dcc03e7081 x86-64: Properly align La_x86_64_retval to VEC_SIZE [BZ #22715]
_dl_runtime_profile calls _dl_call_pltexit, passing a pointer to
La_x86_64_retval which is allocated on stack.  The lrv_vector0
field in La_x86_64_retval must be aligned to size of vector register.
When allocating stack space for La_x86_64_retval, we need to make sure
that the address of La_x86_64_retval + RV_VECTOR0_OFFSET is aligned to
VEC_SIZE.  This patch checks the alignment of the lrv_vector0 field
and pads the stack space if needed.

Tested with x32 and x86-64 on SSE4, AVX and AVX512 machines.  It fixed

FAIL: elf/tst-audit10
FAIL: elf/tst-audit4
FAIL: elf/tst-audit5
FAIL: elf/tst-audit6
FAIL: elf/tst-audit7

on x32 AVX512 machine.

(cherry picked from commit 207a72e298)

	[BZ #22715]
	* sysdeps/x86_64/dl-trampoline.h (_dl_runtime_profile): Properly
	align La_x86_64_retval to VEC_SIZE.
2018-01-19 09:50:12 -08:00
Florian Weimer edcf13e25c nptl/tst-thread-exit-clobber: Run with any C++ compiler
We do not need thread_local support in the C++11 comiler, and the
minimum GCC version for glibc has C++11 support (if it has C++ support).

(cherry picked from commit 10d200dbac)
2018-01-17 10:32:55 +01:00
Florian Weimer abf2e34ee6 csu: Update __libgcc_s_init comment
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 08c6e95234)
2018-01-16 09:27:28 +01:00
Florian Weimer 8343b9da62 nptl/tst-minstack-throw: Compile in C++11 mode with GNU extensions
(cherry picked from commit b725132d2b)
2018-01-16 09:25:07 +01:00
Florian Weimer ea2da72230 nptl: Add PTHREAD_MIN_STACK C++ throw test [BZ #22636]
(cherry picked from commit 860b0240a5)
2018-01-16 09:24:57 +01:00
Florian Weimer 41acd32eb7 nptl: Add tst-minstack-cancel, tst-minstack-exit [BZ #22636]
I verified that without the guard accounting change in commit
630f4cc3aa (Fix stack guard size
accounting) and RTLD_NOW for libgcc_s introduced by commit
f993b87540 (nptl: Open libgcc.so with
RTLD_NOW during pthread_cancel), the tst-minstack-cancel test fails on
an AVX-512F machine.  tst-minstack-exit still passes, and either of
the mentioned commit by itself frees sufficient stack space to make
tst-minstack-cancel pass, too.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit d8b778907e)
2018-01-16 09:24:10 +01:00
Florian Weimer 3e75e676da nptl: Open libgcc.so with RTLD_NOW during pthread_cancel [BZ #22636]
Disabling lazy binding reduces stack usage during unwinding.

Note that RTLD_NOW only makes a difference if libgcc.so has not
already been loaded, so this is only a partial fix.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
(cherry picked from commit f993b87540)
2018-01-16 09:23:12 +01:00
Szabolcs Nagy 5d4c589ca4 [BZ #22637] Fix stack guard size accounting
Previously if user requested S stack and G guard when creating a
thread, the total mapping was S and the actual available stack was
S - G - static_tls, which is not what the user requested.

This patch fixes the guard size accounting by pretending the user
requested S+G stack.  This way all later logic works out except
when reporting the user requested stack size (pthread_getattr_np)
or when computing the minimal stack size (__pthread_get_minstack).

Normally this will increase thread stack allocations by one page.
TLS accounting is not affected, that will require a separate fix.

	[BZ #22637]
	* nptl/descr.h (stackblock, stackblock_size): Update comments.
	* nptl/allocatestack.c (allocate_stack): Add guardsize to stacksize.
	* nptl/nptl-init.c (__pthread_get_minstack): Remove guardsize from
	stacksize.
	* nptl/pthread_getattr_np.c (pthread_getattr_np): Likewise.

(cherry picked from commit 630f4cc3aa)
2018-01-16 09:22:12 +01:00
Florian Weimer aedc861c42 nptl: Add test for callee-saved register restore in pthread_exit
GCC PR 83641 results in a miscompilation of libpthread, which
causes pthread_exit not to restore callee-saved registers before
running destructors for objects on the stack.  This test detects
this situation:

info: unsigned int, direct pthread_exit call
tst-thread-exit-clobber.cc:80: numeric comparison failure
   left: 4148288912 (0xf741dd90); from: value
  right: 1600833940 (0x5f6ac994); from: magic_values.v2
info: double, direct pthread_exit call
info: unsigned int, indirect pthread_exit call
info: double, indirect pthread_exit call
error: 1 test failures

(cherry picked from commit 579396ee08)
2018-01-16 09:21:57 +01:00
Dmitry V. Levin 771c846a71 linux: make getcwd(3) fail if it cannot obtain an absolute path [BZ #22679]
Currently getcwd(3) can succeed without returning an absolute path
because the underlying getcwd syscall, starting with linux commit
v2.6.36-rc1~96^2~2, may succeed without returning an absolute path.

This is a conformance issue because "The getcwd() function shall
place an absolute pathname of the current working directory
in the array pointed to by buf, and return buf".

This is also a security issue because a non-absolute path returned
by getcwd(3) causes a buffer underflow in realpath(3).

Fix this by checking the path returned by getcwd syscall and falling
back to generic_getcwd if the path is not absolute, effectively making
getcwd(3) fail with ENOENT.  The error code is chosen for consistency
with the case when the current directory is unlinked.

[BZ #22679]
CVE-2018-1000001
* sysdeps/unix/sysv/linux/getcwd.c (__getcwd): Fall back to
generic_getcwd if the path returned by getcwd syscall is not absolute.
* io/tst-getcwd-abspath.c: New test.
* io/Makefile (tests): Add tst-getcwd-abspath.

(cherry picked from commit 52a713fdd0)
2018-01-16 09:07:08 +01:00
Florian Weimer 2ee370613c Synchronize support/ infrastructure with master
This commit updates the support/ subdirectory to
commit 1a51e46e4a
on the master branch.
2018-01-16 08:28:18 +01:00
Joseph Myers d11bdaa1f1 Fix position of tests-unsupported definition in assert/Makefile.
tests-unsupported has to be defined before the inclusion of Rules in a
subdirectory Makefile; otherwise it is ineffective.  This patch fixes
the ordering in assert/Makefile, where a recent test addition put
tests-unsupported too late (resulting in build failures when the C++
compiler was missing or broken, and thereby showing up the unrelated
bug 21987).

Incidentally, I don't see why these tests depend on
$(have-cxx-thread_local) rather than just a working C++ compiler.

Tested in such a configuration (broken compiler/libstdc++) with
build-many-glibcs.py.

	* assert/Makefile [$(have-cxx-thread_local)]: Move conditional
	variable definitions above inclusion of ../Rules.

(cherry picked from commit 75dfe623df)
2018-01-04 13:33:12 +01:00
Florian Weimer fc7ef00f02 nss_files: Avoid large buffers with many host addresses [BZ #22078]
The previous implementation had at least a quadratic space
requirement in the number of host addresses and aliases.

(cherry picked from commit d8425e116c)
2018-01-04 13:01:31 +01:00
Florian Weimer 8f8022df07 nss_files: Use struct scratch_buffer for gethostbyname [BZ #18023]
(cherry picked from commit 78e806fd8c)
2018-01-04 13:01:31 +01:00
Florian Weimer f67d3f0fa3 nss_files: Refactor gethostbyname3 multi case into separate function
This is in preparation of further cleanup work.

(cherry picked from commit 8ed70de2fa)
2018-01-04 13:01:30 +01:00
Florian Weimer e75e7573e1 Implement allocation buffers for internal use
This commit adds fixed-size allocation buffers.  The primary use
case is in NSS modules, where dynamically sized data is stored
in a fixed-size buffer provided by the caller.

Other uses include a replacement of mempcpy cascades (which is
safer due to the size checking inherent to allocation buffers).

(cherry picked from commit 4dd8e7c0ce)
2018-01-04 13:01:28 +01:00
Florian Weimer bf19b5fdcd __libc_dynarray_emplace_enlarge: Add missing else
Before, arrays of small elements received a starting allocation size of
8, not 16.

(cherry picked from commit ab5ac271e6)
2018-01-04 12:58:45 +01:00
Florian Weimer 9bb04ec133 dynarray: Set errno on overflow-induced allocation failure
This allows the caller to return directly on such an error, with an
appropriate errno value.

(cherry picked from commit 5898f4548e)
2018-01-04 12:58:40 +01:00
Florian Weimer c3fe737243 dynarray: Implement begin/end functions in the spirit of C++
(cherry picked from commit f8bf87face)
2018-01-04 12:58:33 +01:00
Florian Weimer 8871213e33 Add internal facility for dynamic array handling
This is intended as a type-safe alternative to obstacks and
hand-written realloc constructs.  The implementation avoids
writing function pointers to the heap.

(cherry picked from commit 91b6eb1140)
2018-01-04 12:55:54 +01:00
Florian Weimer ea00a80db7 Add check_mul_overflow_size_t
Backported from commit 2e0bbbfbf9.
2018-01-04 12:51:48 +01:00
Florian Weimer ab8b49432b Add <libc-pointer-arith.h> to help with backporting 2018-01-04 12:45:41 +01:00
Florian Weimer 0e64ee7986 getaddrinfo: Fix error handling in gethosts [BZ #21915] [BZ #21922]
The old code uses errno as the primary indicator for success or
failure.  This is wrong because errno is only set for specific
combinations of the status return value and the h_errno variable.

(cherry picked from commit f4a6be2582)
2018-01-04 12:32:36 +01:00
Florian Weimer e3ae300f3f getaddrinfo: Return EAI_NODATA if gethostbyname2_r reports NO_DATA [BZ #21922]
(cherry picked from commit 5f8340f583)
2018-01-04 12:00:15 +01:00
Florian Weimer e552550b40 getaddrinfo: In gaih_inet, use h_errno for certain status values only
h_errno is not set for NSS_STATUS_SUCCESS, so its value might not be
accurate at this point.

(cherry picked from commit a2881ef014)
2018-01-04 12:00:14 +01:00
Florian Weimer 5515224932 getaddrinfo: Properly set errno for NSS function lookup failure
(cherry picked from commit ad816a5e00)
2018-01-04 12:00:14 +01:00
Florian Weimer dc2d4b0ccd getaddrinfo: Use &h_errno has the h_errno pointer
This simplifies the code because it is not necessary to propagate the
temporary h_errno value to the thread-local variable.  It also increases
compatibility with NSS modules which update only one of the two places.

(cherry picked from commit 53250a21b8)
2018-01-04 12:00:13 +01:00
Florian Weimer 196990f97a getaddrinfo: Use &errno has the errno pointer
Similar code in nss/getXXbyYY_r.c is already using &errno as the
argument.

(cherry picked from commit 924b121c59)
2018-01-04 12:00:13 +01:00
Florian Weimer 346d7f942d getaddrinfo: Remove unreachable return statement from gaih_inet
(cherry picked from commit 0df595b23a)
2018-01-04 12:00:12 +01:00
Florian Weimer 84e7ab5b1b resolv/tst-resolv-basic: Add test cases for bug 21295
(cherry picked from commit 513a71a420)
2018-01-04 11:45:31 +01:00
Dmitry Bilunov 1ae1ef2e0d getaddrinfo: Merge IPv6 addresses and IPv4 addresses [BZ #21295]
(cherry picked from commit 5cf88a83f2)
2018-01-04 11:45:26 +01:00
Florian Weimer d3f885d7ea support_format_addrinfo: Fix flags and canonname formatting
The address family splitting via format_ai_family made unpredictable
the place where the canonname field was printed.  This commit adjusts
the implementation so that the ai_flags is checked for consistency
across the list, and ai_canonname must only be present on the first
list element.

Tests for AI_CANONNAME are added to resolv/tst-resolv-basic.

(cherry picked from commit 8ec69bb7ec)
2018-01-04 11:45:25 +01:00
Florian Weimer d8bda0713b resolv: Support an exactly sized buffer in ns_name_pack [BZ #21359]
This bug did not affect name resolution because those functions
indirectly call ns_name_pack with a buffer which is always larger
than the generated query packet, even in the case of the
longest-possible domain name.

(cherry picked from commit c803cb9b24)
2018-01-04 11:45:20 +01:00
Florian Weimer dbdcd29e71 resolv: Add test coverage for ns_name_unpack, ns_name_ntop
(cherry picked from commit 07d6f1a3ca)
2018-01-04 11:44:02 +01:00
Florian Weimer 6bdff2e237 getaddrinfo: Avoid stack copy of IPv6 address
(cherry picked from commit 76b8266f99)
2018-01-04 11:28:44 +01:00
Florian Weimer 0fdd895d77 __inet_pton_length: Implement new internal helper function
(cherry picked from commit 60149b2859)
2018-01-04 11:28:43 +01:00
Florian Weimer eda5f13d71 inet_pton: Reformat in GNU style
Generated machine code is identical on x86-64.

(cherry picked from commit d53b865288)
2018-01-04 11:28:43 +01:00
Florian Weimer b091ef7e2f getaddrinfo: Eliminate another strdup call
(cherry picked from commit 363911ce13)
2018-01-04 11:20:11 +01:00
Florian Weimer 8fbf5688ed getaddrinfo: Fix localplt failure involving strdup
(cherry picked from commit 6257fcfd58)
2018-01-04 11:20:07 +01:00
Florian Weimer 1f60575f88 getaddrinfo: Always allocate canonical name on the heap
A further simplification could eliminate the canon variable in
gaih_inet and replace it with canonbuf.  However, canonbuf is
used as a flag in the nscd code, which makes this somewhat
non-straightforward.

(cherry picked from commit 673cb072a4)
2018-01-04 11:20:00 +01:00
Florian Weimer 8a4bf46fe7 getaddrinfo: Unconditionally use malloc for address list
getaddrinfo has to call malloc eventually anyway, so the complexity
of avoiding malloc calls is not worth potential savings.

(cherry picked from commit 46ce8881ad)
2018-01-04 11:19:35 +01:00
H.J. Lu baf35427d6 x86-64: Regenerate libm-test-ulps for AVX512 mathvec tests
Update libm-test-ulps for AVX512 mathvec tests by running
“make regen-ulps” on Intel Xeon processor with AVX512.

	* sysdeps/x86_64/fpu/libm-test-ulps: Regenerated.

(cherry picked from commit fcaaca412f)
2018-01-04 11:19:34 +01:00
Aurelien Jarno 1998843fb7 elf: Check for empty tokens before dynamic string token expansion [BZ #22625]
The fillin_rpath function in elf/dl-load.c loops over each RPATH or
RUNPATH tokens and interprets empty tokens as the current directory
("./"). In practice the check for empty token is done *after* the
dynamic string token expansion. The expansion process can return an
empty string for the $ORIGIN token if __libc_enable_secure is set
or if the path of the binary can not be determined (/proc not mounted).

Fix that by moving the check for empty tokens before the dynamic string
token expansion. In addition, check for NULL pointer or empty strings
return by expand_dynamic_string_token.

The above changes highlighted a bug in decompose_rpath, an empty array
is represented by the first element being NULL at the fillin_rpath
level, but by using a -1 pointer in decompose_rpath and other functions.

Changelog:
	[BZ #22625]
	* elf/dl-load.c (fillin_rpath): Check for empty tokens before dynamic
	string token expansion. Check for NULL pointer or empty string possibly
	returned by expand_dynamic_string_token.
	(decompose_rpath): Check for empty path after dynamic string
	token expansion.
(cherry picked from commit 3e3c904dae)
2017-12-30 23:43:55 +01:00
Dmitry V. Levin 26748a28ca elf: do not substitute dst in $LD_LIBRARY_PATH twice [BZ #22627]
Starting with commit
glibc-2.18.90-470-g2a939a7e6d81f109d49306bc2e10b4ac9ceed8f9 that
introduced substitution of dynamic string tokens in fillin_rpath,
_dl_init_paths invokes _dl_dst_substitute for $LD_LIBRARY_PATH twice:
the first time it's called directly, the second time the result
is passed on to fillin_rpath which calls expand_dynamic_string_token
which in turn calls _dl_dst_substitute, leading to the following
behaviour:

$ mkdir -p /tmp/'$ORIGIN' && cd /tmp/'$ORIGIN' &&
  echo 'int main(){}' |gcc -xc - &&
  strace -qq -E LD_LIBRARY_PATH='$ORIGIN' -e /open ./a.out
open("/tmp//tmp/$ORIGIN/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/tmp//tmp/$ORIGIN/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/tmp//tmp/$ORIGIN/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/tmp//tmp/$ORIGIN/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

Fix this by removing the direct _dl_dst_substitute invocation.

* elf/dl-load.c (_dl_init_paths): Remove _dl_dst_substitute preparatory
code and invocation.

(cherry picked from commit bb195224ac)
2017-12-22 14:56:36 +00:00
Florian Weimer a8920e694a elf: Count components of the expanded path in _dl_init_path [BZ #22607]
(cherry picked from commit 3ff3dfa5af)
2017-12-16 14:47:11 +01:00
Florian Weimer 778a7c1825 elf: Compute correct array size in _dl_init_paths [BZ #22606]
(cherry picked from commit 8a0b17e48b)
2017-12-16 14:46:12 +01:00
Florian Weimer 6474863dcb <array_length.h>: New array_length and array_end macros
(cherry picked from commit c94a5688fb)
2017-12-16 14:44:54 +01:00
Dmitry V. Levin 17357d93cb NEWS: merge two sections about security related changes in 2.25.1
Commit glibc-2.25-70-g717743bb07471f95bef6ea63d9b12848ad91aaf6
introduced a second "Security related changes" section for 2.25.1.
Merge them back into a single section.
2017-12-15 00:30:45 +00:00
James Clarke 8f50b4a454 ia64: Add ipc_priv.h header to set __IPC_64 to zero
When running strace, IPC_64 was set in the command, but ia64 is
an architecture where CONFIG_ARCH_WANT_IPC_PARSE_VERSION *isn't* set
in the kernel, so ipc_parse_version just returns IPC_64 without
clearing the IPC_64 bit in the command.

	* sysdeps/unix/sysv/linux/ia64/ipc_priv.h: New file defining
	__IPC_64 to 0 to avoid IPC_64 being set.

Signed-off-by: James Clarke <jrtc27@jrtc27.com>

(cherry picked from commit 89bd8016b3)
2017-12-12 20:14:43 +01:00
Aurelien Jarno 4444f6a92b Update NEWS to add CVE-2017-15804 entry
(cherry picked from commit 15e84c63c0)
2017-12-01 22:20:26 +01:00
Florian Weimer b2b39e6671 posix/tst-glob-tilde.c: Add test for bug 22332
(cherry picked from commit 2fac6a6cd5)
2017-12-01 22:20:20 +01:00
Paul Eggert ee68a4419a glob: Fix buffer overflow during GLOB_TILDE unescaping [BZ #22332]
(cherry picked from commit a159b53fa0)
2017-12-01 22:20:12 +01:00
Florian Weimer 3b587362bd Update NEWS and ChangeLog for CVE-2017-15671
(cherry picked from commit 914c9994d2)
2017-12-01 22:19:56 +01:00
Florian Weimer a06cc8caa7 glob: Add new test tst-glob-tilde
The new test checks for memory leaks (see bug 22325) and attempts
to trigger the buffer overflow in bug 22320.

(cherry picked from commit e80fc1fc98)
2017-12-01 22:17:06 +01:00
Paul Eggert 717743bb07 CVE-2017-15670: glob: Fix one-byte overflow [BZ #22320]
(cherry picked from commit c369d66e54)
2017-12-01 22:12:34 +01:00
Adhemerval Zanella ea54198514 posix: Sync glob with gnulib [BZ #1062]
This patch syncs posix/glob.c implementation with gnulib version
b5ec983 (glob: simplify symlink detection).  The only difference
to gnulib code is

  * DT_UNKNOWN, DT_DIR, and DT_LNK definition in the case there
    were not already defined.  Gnulib code which uses
    HAVE_STRUCT_DIRENT_D_TYPE will redefine them wrongly because
    GLIBC does not define HAVE_STRUCT_DIRENT_D_TYPE.  Instead
    the patch check for each definition instead.

Also, the patch requires additional globfree and globfree64 files
for compatibility version on some architectures.  Also the code
simplification leads to not macro simplification (not need for
NO_GLOB_PATTERN_P anymore).

Checked on x86_64-linux-gnu and on a build using build-many-glibcs.py
for all major architectures.

	[BZ #1062]
	* posix/Makefile (routines): Add globfree, globfree64, and
	glob_pattern_p.
	* posix/flexmember.h: New file.
	* posix/glob_internal.h: Likewise.
	* posix/glob_pattern_p.c: Likewise.
	* posix/globfree.c: Likewise.
	* posix/globfree64.c: Likewise.
	* sysdeps/gnu/globfree64.c: Likewise.
	* sysdeps/unix/sysv/linux/alpha/globfree.c: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c: Likewise.
	* sysdeps/unix/sysv/linux/oldglob.c: Likewise.
	* sysdeps/unix/sysv/linux/wordsize-64/globfree64.c: Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/globfree.c: Likewise.
	* sysdeps/wordsize-64/globfree.c: Likewise.
	* sysdeps/wordsize-64/globfree64.c: Likewise.
	* posix/glob.c (HAVE_CONFIG_H): Use !_LIBC instead.
	[NDEBUG): Remove comments.
	(GLOB_ONLY_P, _AMIGA, VMS): Remove define.
	(dirent_type): New type.  Use uint_fast8_t not
	uint8_t, as C99 does not require uint8_t.
	(DT_UNKNOWN, DT_DIR, DT_LNK): New macros.
	(struct readdir_result): Use dirent_type.  Do not define skip_entry
	unless it is needed; this saves a byte on platforms lacking d_ino.
	(readdir_result_type, readdir_result_skip_entry):
	New functions, replacing ...
	(readdir_result_might_be_symlink, readdir_result_might_be_dir):
	 these functions, which were removed.  This makes the callers
	easier to read.  All callers changed.
	(D_INO_TO_RESULT): Now empty if there is no d_ino.
	(size_add_wrapv, glob_use_alloca): New static functions.
	(glob, glob_in_dir): Check for size_t overflow in several places,
	and fix some size_t checks that were not quite right.
	Remove old code using SHELL since Bash no longer
	uses this.
	(glob, prefix_array): Separate MS code better.
	(glob_in_dir): Remove old Amiga and VMS code.
	(globfree, __glob_pattern_type, __glob_pattern_p): Move to
	separate files.
	(glob_in_dir): Do not rely on undefined behavior in accessing
	struct members beyond their bounds.  Use a flexible array member
	instead
	(link_stat): Rename from link_exists2_p and return -1/0 instead of
	0/1.  Caller changed.
	(glob): Fix memory leaks.
	* posix/glob64 (globfree64): Move to separate file.
	* sysdeps/gnu/glob64.c (NO_GLOB_PATTERN_P): Remove define.
	(globfree64): Remove hidden alias.
	* sysdeps/unix/sysv/linux/Makefile (sysdeps_routines): Add
	oldglob.
	* sysdeps/unix/sysv/linux/alpha/glob.c (__new_globfree): Move to
	separate file.
	* sysdeps/unix/sysv/linux/i386/glob64.c (NO_GLOB_PATTERN_P): Remove
	define.
	Move compat code to separate file.
	* sysdeps/wordsize-64/glob.c (globfree): Move definitions to
	separate file.

(cherry picked from commit c66c908230)
2017-12-01 22:10:34 +01:00
H.J. Lu 2b54f16a8a i386: Hide __old_glob64 [BZ #18822]
Hide internal __old_glob64 function to allow direct access within
libc.so and libc.a without using GOT nor PLT.

	[BZ #18822]
	* sysdeps/unix/sysv/linux/i386/glob64.c (__old_glob64): Add
	libc_hidden_proto and libc_hidden_def.

(cherry picked from commit 2585d7b839)
2017-12-01 22:07:39 +01:00
Adhemerval Zanella 7bd7ddfab1 Fix missing posix_fadvise64 mips64 static build (BZ #21232)
This patch fixes the missing posix_fadvise64 symbol for static build
required for _FILE_OFFSET_BITS=64 on mips64 build.

Checked on a mips64-linux-gnu build with run-built-tests=no.

	[BZ #21232]
	* sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c: Add
	posix_fadvise64 weak_alias for static build.

(cherry picked from commit ed7d6072f2)
2017-11-30 13:46:09 +01:00
Adhemerval Zanella b0afcf5db7 posix: Do not use WNOHANG in waitpid call for Linux posix_spawn
As shown in some buildbot issues on aarch64 and powerpc, calling
clone (VFORK) and waitpid (WNOHANG) does not guarantee the child
is ready to be collected.  This patch changes the call back to 0
as before fe05e1cb6d fix.

This change can lead to the scenario 4.3 described in the commit,
where the waitpid call can hang undefinitely on the call.  However
this is also a very unlikely and also undefinied situation where
both the caller is trying to terminate a pid before posix_spawn
returns and the race pid reuse is triggered.  I don't see how to
correct handle this specific situation within posix_spawn.

Checked on x86_64-linux-gnu, aarch64-linux-gnu and
powerpc64-linux-gnu.

	* sysdeps/unix/sysv/linux/spawni.c (__spawnix): Use 0 instead of
	WNOHANG in waitpid call.

(cherry picked from commit aa95a2414e)
2017-11-27 00:20:02 +01:00
Adhemerval Zanella 59f0c83e71 posix: Fix improper assert in Linux posix_spawn (BZ#22273)
As noted by Florian Weimer, current Linux posix_spawn implementation
can trigger an assert if the auxiliary process is terminated before
actually setting the err member:

    340   /* Child must set args.err to something non-negative - we rely on
    341      the parent and child sharing VM.  */
    342   args.err = -1;
    [...]
    362   new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
    363                    CLONE_VM | CLONE_VFORK | SIGCHLD, &args);
    364
    365   if (new_pid > 0)
    366     {
    367       ec = args.err;
    368       assert (ec >= 0);

Another possible issue is killing the child between setting the err and
actually calling execve.  In this case the process will not ran, but
posix_spawn also will not report any error:

    269
    270   args->err = 0;
    271   args->exec (args->file, args->argv, args->envp);

As suggested by Andreas Schwab, this patch removes the faulty assert
and also handles any signal that happens before fork and execve as the
spawn was successful (and thus relaying the handling to the caller to
figure this out).  Different than Florian, I can not see why using
atomics to set err would help here, essentially the code runs
sequentially (due CLONE_VFORK) and I think it would not be legal the
compiler evaluate ec without checking for new_pid result (thus there
is no need to compiler barrier).

Summarizing the possible scenarios on posix_spawn execution, we
have:

  1. For default case with a success execution, args.err will be 0, pid
     will not be collected and it will be reported to caller.

  2. For default failure case, args.err will be positive and the it will
     be collected by the waitpid.  An error will be reported to the
     caller.

  3. For the unlikely case where the process was terminated and not
     collected by a caller signal handler, it will be reported as succeful
     execution and not be collected by posix_spawn (since args.err will
     be 0). The caller will need to actually handle this case.

  4. For the unlikely case where the process was terminated and collected
     by caller we have 3 other possible scenarios:

     4.1. The auxiliary process was terminated with args.err equal to 0:
	  it will handled as 1. (so it does not matter if we hit the pid
          reuse race since we won't possible collect an unexpected
          process).

     4.2. The auxiliary process was terminated after execve (due a failure
          in calling it) and before setting args.err to -1: it will also
          be handle as 1. but with the issue of not be able to report the
          caller a possible execve failures.

     4.3. The auxiliary process was terminated after args.err is set to -1:
          this is the case where it will be possible to hit the pid reuse
          case where we will need to collected the auxiliary pid but we
          can not be sure if it will be expected one.  I think for this
          case we need to actually change waitpid to use WNOHANG to avoid
          hanging indefinitely on the call and report an error to caller
          since we can't differentiate between a default failure as 2.
          and a possible pid reuse race issue.

Checked on x86_64-linux-gnu.

	* sysdeps/unix/sysv/linux/spawni.c (__spawnix): Handle the case where
	the auxiliary process is terminated by a signal before calling _exit
	or execve.

(cherry picked from commit fe05e1cb6d)
2017-11-26 23:09:11 +01:00
Andreas Schwab 0e6f64d9d0 Add test for bug 21041
(cherry picked from commit 40c06a3d04)
2017-11-21 20:18:05 +01:00
Andreas Schwab 06e775f464 Fix s390 version of pt-longjmp.c
(cherry picked from commit 5797b410a8)
2017-11-21 20:15:29 +01:00
Andreas Schwab 8182ccd9b8 Don't use IFUNC resolver for longjmp or system in libpthread (bug 21041)
Unlike the vfork forwarder and like the fork forwarder as in bug 19861,
there won't be a problem when the compiler does not turn this into a tail
call.

(cherry picked from commit fc5ad7024c)
2017-11-21 20:15:26 +01:00
James Clarke 46acbd0582 Fix TLS relocations against local symbols on powerpc32, sparc32 and sparc64
Normally, TLS relocations against local symbols are optimised by the linker
to be absolute.  However, gold does not do this, and so it is possible to
end up with, for example, R_SPARC_TLS_DTPMOD64 referring to a local symbol.
Since sym_map is left as null in elf_machine_rela for the special local
symbol case, the relocation handling thinks it has nothing to do, and so
the module gets left as 0.  Havoc then ensues when the variable in question
is accessed.

Before this fix, the main_local_gold program would receive a SIGBUS on
sparc64, and SIGSEGV on powerpc32.  With this fix applied, that test now
passes like the rest of them.

	* sysdeps/powerpc/powerpc32/dl-machine.h (elf_machine_rela):
	Assign sym_map to be map for local symbols, as TLS relocations
	use sym_map to determine whether the symbol is defined and to
	extract the TLS information.
	* sysdeps/sparc/sparc32/dl-machine.h (elf_machine_rela): Likewise.
	* sysdeps/sparc/sparc64/dl-machine.h (elf_machine_rela): Likewise.
2017-11-14 21:20:11 +01:00
Joseph Myers 0fc015b6db Install correct bits/long-double.h for MIPS64 (bug 22322).
Similar to bug 21987 for SPARC, MIPS64 wrongly installs the ldbl-128
version of bits/long-double.h, meaning incorrect results when using
headers installed from a 64-bit installation for a 32-bit build.  (I
haven't actually seen this cause build failures before its interaction
with bits/floatn.h did so - installed headers wrongly expecting
_Float128 to be available in a 32-bit configuration.)

This patch fixes the bug by moving the MIPS header to
sysdeps/mips/ieee754, which comes before sysdeps/ieee754/ldbl-128 in
the sysdeps directory ordering.  (bits/floatn.h will need a similar
fix - duplicating the ldbl-128 version for MIPS will suffice - for
headers from a 32-bit installation to be correct for 64-bit builds.)

Tested with build-many-glibcs.py (compilers build for
mips64-linux-gnu, where there was previously a libstdc++ build failure
as at
<https://sourceware.org/ml/libc-testresults/2017-q4/msg00130.html>).

	[BZ #22322]
	* sysdeps/mips/bits/long-double.h: Move to ....
	* sysdeps/mips/ieee754/bits/long-double.h: ... here.

(cherry picked from commit 37bb78cb8c)
2017-10-23 15:49:14 +00:00
H.J. Lu 61bebc863e x86-64: Use fxsave/xsave/xsavec in _dl_runtime_resolve [BZ #21265]
In _dl_runtime_resolve, use fxsave/xsave/xsavec to preserve all vector,
mask and bound registers.  It simplifies _dl_runtime_resolve and supports
different calling conventions.  ld.so code size is reduced by more than
1 KB.  However, use fxsave/xsave/xsavec takes a little bit more cycles
than saving and restoring vector and bound registers individually.

Latency for _dl_runtime_resolve to lookup the function, foo, from one
shared library plus libc.so:

                             Before    After     Change

Westmere (SSE)/fxsave         345      866       151%
IvyBridge (AVX)/xsave         420      643       53%
Haswell (AVX)/xsave           713      1252      75%
Skylake (AVX+MPX)/xsavec      559      719       28%
Skylake (AVX512+MPX)/xsavec   145      272       87%
Ryzen (AVX)/xsavec            280      553       97%

This is the worst case where portion of time spent for saving and
restoring registers is bigger than majority of cases.  With smaller
_dl_runtime_resolve code size, overall performance impact is negligible.

On IvyBridge, differences in build and test time of binutils with lazy
binding GCC and binutils are noises.  On Westmere, differences in
bootstrap and "makc check" time of GCC 7 with lazy binding GCC and
binutils are also noises.

	[BZ #21265]
	* sysdeps/x86/cpu-features-offsets.sym (XSAVE_STATE_SIZE_OFFSET):
	New.
	* sysdeps/x86/cpu-features.c: Include <libc-internal.h>.
	(get_common_indeces): Set xsave_state_size and
	bit_arch_XSAVEC_Usable if needed.
	(init_cpu_features): Remove bit_arch_Use_dl_runtime_resolve_slow
	and bit_arch_Use_dl_runtime_resolve_opt.
	* sysdeps/x86/cpu-features.h (bit_arch_Use_dl_runtime_resolve_opt):
	Removed.
	(bit_arch_Use_dl_runtime_resolve_slow): Likewise.
	(bit_arch_Prefer_No_AVX512): Updated.
	(bit_arch_MathVec_Prefer_No_AVX512): Likewise.
	(bit_arch_XSAVEC_Usable): New.
	(STATE_SAVE_OFFSET): Likewise.
	(STATE_SAVE_MASK): Likewise.
	[__ASSEMBLER__]: Include <cpu-features-offsets.h>.
	(cpu_features): Add xsave_state_size.
	(index_arch_Use_dl_runtime_resolve_opt): Removed.
	(index_arch_Use_dl_runtime_resolve_slow): Likewise.
	(index_arch_XSAVEC_Usable): New.
	* sysdeps/x86_64/dl-machine.h (elf_machine_runtime_setup):
	Replace _dl_runtime_resolve_sse, _dl_runtime_resolve_avx,
	_dl_runtime_resolve_avx_slow, _dl_runtime_resolve_avx_opt,
	_dl_runtime_resolve_avx512 and _dl_runtime_resolve_avx512_opt
	with _dl_runtime_resolve_fxsave, _dl_runtime_resolve_xsave and
	_dl_runtime_resolve_xsavec.
	* sysdeps/x86_64/dl-trampoline.S (DL_RUNTIME_UNALIGNED_VEC_SIZE):
	Removed.
	(DL_RUNTIME_RESOLVE_REALIGN_STACK): Check STATE_SAVE_ALIGNMENT
	instead of VEC_SIZE.
	(REGISTER_SAVE_BND0): Removed.
	(REGISTER_SAVE_BND1): Likewise.
	(REGISTER_SAVE_BND3): Likewise.
	(REGISTER_SAVE_RAX): Always defined to 0.
	(VMOV): Removed.
	(_dl_runtime_resolve_avx): Likewise.
	(_dl_runtime_resolve_avx_slow): Likewise.
	(_dl_runtime_resolve_avx_opt): Likewise.
	(_dl_runtime_resolve_avx512): Likewise.
	(_dl_runtime_resolve_avx512_opt): Likewise.
	(_dl_runtime_resolve_sse): Likewise.
	(_dl_runtime_resolve_sse_vex): Likewise.
	(USE_FXSAVE): New.
	(_dl_runtime_resolve_fxsave): Likewise.
	(USE_XSAVE): Likewise.
	(_dl_runtime_resolve_xsave): Likewise.
	(USE_XSAVEC): Likewise.
	(_dl_runtime_resolve_xsavec): Likewise.
	* sysdeps/x86_64/dl-trampoline.h (_dl_runtime_resolve_avx512):
	Removed.
	(_dl_runtime_resolve_avx512_opt): Likewise.
	(_dl_runtime_resolve_avx): Likewise.
	(_dl_runtime_resolve_avx_opt): Likewise.
	(_dl_runtime_resolve_sse): Likewise.
	(_dl_runtime_resolve_sse_vex): Likewise.
	(_dl_runtime_resolve_fxsave): New.
	(_dl_runtime_resolve_xsave): Likewise.
	(_dl_runtime_resolve_xsavec): Likewise.

(cherry picked from commit b52b0d793d)
2017-10-22 08:20:57 -07:00
H.J. Lu 74e1eb9078 x86-64: Verify that _dl_runtime_resolve preserves vector registers
On x86-64, _dl_runtime_resolve must preserve the first 8 vector
registers.  Add 3 _dl_runtime_resolve tests to verify that SSE,
AVX and AVX512 registers are preserved.

	* sysdeps/x86_64/Makefile (tests): Add tst-sse, tst-avx and
	tst-avx512.
	(test-extras): Add tst-avx-aux and tst-avx512-aux.
	(extra-test-objs): Add tst-avx-aux.o and tst-avx512-aux.o.
	(modules-names): Add tst-ssemod, tst-avxmod and tst-avx512mod.
	($(objpfx)tst-sse): New rule.
	($(objpfx)tst-avx): Likewise.
	($(objpfx)tst-avx512): Likewise.
	(CFLAGS-tst-avx-aux.c): New.
	(CFLAGS-tst-avxmod.c): Likewise.
	(CFLAGS-tst-avx512-aux.c): Likewise.
	(CFLAGS-tst-avx512mod.c): Likewise.
	* sysdeps/x86_64/tst-avx-aux.c: New file.
	* sysdeps/x86_64/tst-avx.c: Likewise.
	* sysdeps/x86_64/tst-avx512-aux.c: Likewise.
	* sysdeps/x86_64/tst-avx512.c: Likewise.
	* sysdeps/x86_64/tst-avx512mod.c: Likewise.
	* sysdeps/x86_64/tst-avxmod.c: Likewise.
	* sysdeps/x86_64/tst-sse.c: Likewise.
	* sysdeps/x86_64/tst-ssemod.c: Likewise.

(cherry picked from commit 3403a17fea)
2017-10-19 08:48:13 -07:00
Florian Weimer 595f287ae0 Synchronize support/ infrastructure with master
This commit updates the support/ subdirectory to
commit 84d0e6f052
on the master branch.
2017-10-11 13:09:11 +02:00
DJ Delorie 864ea5f657 Fix cast-after-dereference
Original code was dereferencing a char*, then casting the value
to size_t.  Should cast the pointer to size_t* then deference.

(cherry picked from commit f8cef4d07d)
2017-10-07 13:30:34 +02:00
DJ Delorie 0279dcae88 Fix BZ #21654 - grp-merge.c alignment
* grp/grp_merge.c (__copy_grp): Align char** to minimum pointer
alignment not char alignment.
(__merge_grp): Likewise.

(cherry picked from commit 4fa8ae49aa)
2017-10-07 13:30:33 +02:00
Joseph Myers ac93084c08 Fix sparc32 bits/long-double.h (bug 21987).
My refactoring of long double information

commit 0acb8a2a85
Author: Joseph Myers <joseph@codesourcery.com>
Date:   Wed Dec 14 18:27:56 2016 +0000

    Refactor long double information into bits/long-double.h.

resulted in sparc32 configurations installing the ldbl-opt version of
bits/long-double.h instead of the intended
sysdeps/unix/sysv/linux/sparc version.

For sparc32 by itself, this is not a problem, since the ldbl-opt
version is correct for sparc32.  However, both sparc32 and sparc64 are
supposed to install sets of headers that work for both of them, so
that a single sysroot, whichever order the libraries are built and
installed in, works for both.  The effect of having the wrong version
installed is that you end up with a miscompiled sparc64 libstdc++
which fails glibc's configure tests for the C++ compiler.

This patch moves the header from sysdeps/unix/sysv/linux/sparc to
separate copies of the same file for sparc32 and sparc64, to ensure it
comes before ldbl-opt in the sysdeps directory ordering.

Tested with build-many-glibcs.py for sparc64-linux-gnu and
sparcv9-linux-gnu.

	[BZ #21987]
	* sysdeps/unix/sysv/linux/sparc/bits/long-double.h: Remove file
	and copy to ...
	* sysdeps/unix/sysv/linux/sparc/sparc32/bits/long-double.h:
	... here.
	* sysdeps/unix/sysv/linux/sparc/sparc64/bits/long-double.h:
	... and here.

(cherry picked from commit 80f91666fe)
2017-10-02 15:54:30 +00:00
H.J. Lu b64e029866 Make copy of <bits/std_abs.h> from GCC 7 [BZ #21573]
<bits/std_abs.h> from GCC 7 will include /usr/include/stdlib.h from
"#include_next" (instead of stdlib/stdlib.h in the glibc source
directory), and this turns up as a make dependency.  Also make a copy
of <bits/std_abs.h> to prevent it from including /usr/include/stdlib.h.

	* configure.ac (find_cxx_header): Suppress compiler error message.
	* configure: Regenerated.

	[BZ #21573]
	* Makerules [$(c++-bits-std_abs-h) != ""] (before-compile): Add
	$(common-objpfx)bits/std_abs.h.
	[$(c++-bits-std_abs-h) != ""] ($(common-objpfx)bits/std_abs.h):
	New target.
	* config.make.in (c++-bits-std_abs-h): New.
	* configure.ac (find_cxx_header): Use "\,$1," with sed.
	(CXX_BITS_STD_ABS_H): New.
	(AC_SUBST(CXX_BITS_STD_ABS_H)): Likewise.
	* configure: Regenerated.

(cherry picked from commit a65ea28d18 and
 commit c2528fef3b)
2017-09-11 09:05:13 -07:00
H.J. Lu 4e4cfd7761 string/stratcliff.c: Replace int with size_t [BZ #21982]
Fix GCC 7 errors when string/stratcliff.c is compiled with -O3:

stratcliff.c: In function ‘do_test’:
cc1: error: assuming signed overflow does not occur when assuming that (X - c) <= X is always true [-Werror=strict-overflow]

	[BZ #21982]
	* string/stratcliff.c (do_test): Declare size, nchars, inner,
	middle and outer with size_t instead of int.  Repleace %d and
	%Zd with %zu in printf.  Update "MAX (0, nchars - 128)" and
	"MAX (outer, nchars - 64)" to support unsigned outer and
	nchars.  Also exit loop when outer == 0.

(cherry picked from commit 376b40a27a)
2017-09-11 08:52:18 -07:00
H.J. Lu e0252d3c64 Use "static const char domain[] ="
* resolv/tst-resolv-qtypes.c (domain): Changed to
	"const char domain[] =".

(cherry picked from commit 78bfa877b3)
2017-09-07 14:04:15 -07:00
H.J. Lu c71ddec7b3 Place $(elf-objpfx)sofini.os last [BZ #22051]
Since sofini.os terminates .eh_frame section, it should be placed last.

	[BZ #22051]
	* Makerules (build-module-helper-objlist): Filter out
	$(elf-objpfx)sofini.os.
	(build-shlib-objlist): Append $(elf-objpfx)sofini.os if it is
	needed.

(cherry picked from commit ecd0747df3)
2017-09-07 09:19:33 -07:00
Carlos O'Donell bc5ace67fe mutex: Fix robust mutex lock acquire (Bug 21778)
65810f0ef0 fixed a robust mutex bug but
introduced BZ 21778: if the CAS used to try to acquire a lock fails, the
expected value is not updated, which breaks other cases in the loce
acquisition loop.  The fix is to simply update the expected value with
the value returned by the CAS, which ensures that behavior is as if the
first case with the CAS never happened (if the CAS fails).

This is a regression introduced in the last release.

Tested on x86_64, i686, ppc64, ppc64le, s390x, aarch64, armv7hl.

(cherry picked from commit 5920a4a624)
2017-08-28 15:13:07 +02:00
Carlos O'Donell 4b3a995ad6 rwlock: Fix explicit hand-over (bug 21298)
Without this fix, the rwlock can fail to execute the explicit hand-over
in certain cases (e.g., empty critical sections that switch quickly between
read and write phases).  This can then lead to errors in how __wrphase_futex
is accessed, which in turn can lead to deadlocks.

(cherry picked from commit faf8c066df)
2017-08-28 15:13:06 +02:00
Florian Weimer 92a0e0c617 Synchronize support/ infrastructure with master
This commit updates the support/ subdirectory to
commit faf8c066df
on the master branch.
2017-08-28 14:38:52 +02:00
Florian Weimer 02aaa3c749 assert: Support types without operator== (int) [BZ #21972]
(cherry picked from commit b5889d25e9)
2017-08-21 16:15:48 +02:00
Florian Weimer 47db584c74 resolv: Reduce EDNS payload size to 1200 bytes [BZ #21361]
This hardens the stub resolver against fragmentation-based attacks.

(cherry picked from commit e14a27723c)
2017-08-15 10:47:08 +02:00
John David Anglin 092788065e Return to caller if dl_fixup fails to resolve callee on hppa.
[BZ 19170]
	* sysdeps/hppa/dl-trampoline.S (_dl_runtime_resolve): Return to caller
	if _dl_fixup fails.
2017-08-12 17:54:26 -04:00
John David Anglin 42f78808b1 Call exit directly in clone (BZ #21512)
[BZ #21512]
	* sysdeps/unix/sysv/linux/aarch64/clone.S (__clone): Call exit
	syscall instead of jump to _exit.
	* sysdeps/unix/sysv/linux/hppa/localplt.data: Remove _exit entry.
2017-08-12 17:37:14 -04:00
John David Anglin 80fadd69ca hppa: set __IPC_64 as zero for SysV IPC calls
In 1e5834c38a ("Refactor Linux ipc_priv header") a different
    approach to passing __IPC_64 as zero was created.  Hppa kernel ABI
    requires to oass __IPC_64 as zero since it does not set
    CONFIG_ARCH_WANT_IPC_PARSE_VERSION in the kernel.

    Checked on hppa-linux-gnu with some adjustments to avoid BZ#21016
    (basically by removing hppa compat implementations and adjusting
    required headers).

            * sysdeps/unix/sysv/linux/hppa/ipc_priv.h: New file.
2017-08-12 17:19:59 -04:00
John David Anglin a707dc9bd2 Add CFI annotation.
* sysdeps/unix/sysv/linux/hppa/clone.S (__clone): Add .cfi annotation.
	* sysdeps/unix/sysv/linux/hppa/getcontext.S (__getcontext): Likewise.
	* sysdeps/unix/sysv/linux/hppa/pt-vfork.S (__vfork): Likewise.
	* sysdeps/unix/sysv/linux/hppa/setcontext.S (__setcontext): Likewise.
2017-08-12 16:59:50 -04:00
John David Anglin c6f84dffdf Fix stack offset for r19 load in __getcontext.
* sysdeps/unix/sysv/linux/hppa/getcontext.S (__getcontext): Fix stack
	offset for r19 load.
2017-08-12 16:46:35 -04:00
John David Anglin 604c01c69a Fix __setcontext return value on hppa.
* sysdeps/unix/sysv/linux/hppa/setcontext.S (__setcontext): Return 0.
2017-08-12 16:28:32 -04:00
John David Anglin 725118c013 Fix syscall cancellation on hppa.
* sysdeps/unix/sysv/linux/hppa/sysdep-cancel.h (PSEUDO): Fix CFA offset.
	Use .cfi_def_cfa_offset instead of .cfi_offset.  Don't record stack
	pointer offset.  Correct PIC register offset.  Don't mention frame
	related instructions in epilogue.
	(PUSHARGS_1): Correct offset.
	(PUSHARGS_2): Likewise.
	(PUSHARGS_3): Likewise.
	(PUSHARGS_4): Likewise.
	(PUSHARGS_5): Likewise.
	(PUSHARGS_6): Likewise.
	(POPARGS_1): Don't mention register restore.
	(POPARGS_2): Likewise.
	(POPARGS_3): Likewise.
	(POPARGS_4): Likewise.
	(POPARGS_5): Likewise.
	(POPARGS_6): Likewise.
	* sysdeps/unix/sysv/linux/hppa/sysdep.h (SAVE_PIC): Don't mention
	copy of PIC register.
	(LOAD_PIC): Likewise don't mention restore.
	(DO_CALL): Fix CFA offset.  Use .cfi_def_cfa_offset instead of
	.cfi_offset.  Don't record stack pointer offset.  Correct PIC register
	offset.  Don't mention frame related instructions in epilogue.
2017-08-12 16:11:36 -04:00
John David Anglin fa13872f59 Fix [BZ 20098].
[BZ 20098]
	* sysdeps/hppa/dl-fptr.c (_dl_read_access_allowed): New.
	(_dl_lookup_address): Return address if it is not consistent with
	being a linker defined function pointer.  Likewise, return address
	if address and function descriptor addresses are not accessible.
2017-08-12 15:45:28 -04:00
John David Anglin 4a60d7e30a Fix [BZ locale/19838].
[BZ locale/19838]
	* sysdeps/unix/sysv/linux/hppa/bits/shm.h (SHMLBA): Set to page size.
2017-08-12 15:09:25 -04:00
John David Anglin d215bbdff3 Fix guard alignment in allocate_stack when stack grows up.
* nptl/allocatestack.c (allocate_stack): Align old and new guard
	addresses to page boundaries when the stack grows up.
2017-08-12 14:39:53 -04:00
John David Anglin f40b4e86f5 Fix failing sNaN tests on hppa.
* sysdeps/hppa/math-tests.h: New.
2017-08-12 13:43:57 -04:00
John David Anglin 241af18538 Use generic pthread support on hppa.
[BZ #21016]
        * sysdeps/hppa/nptl/bits/pthreadtypes.h: Update pthread_cond_t typedef.
        * sysdeps/unix/sysv/linux/hppa/pthread.h: Include
        bits/types/struct_timespec.h.
        (PTHREAD_MUTEX_INITIALIZER): Revise define.
        (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP): Likewise.
        (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP): Likewise.
        (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP): Likewise.
        (PTHREAD_RWLOCK_INITIALIZER): Likewise.
        (PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP): Likewise.
        (PTHREAD_COND_INITIALIZER): Likewise.
        Remove old definitions.
        * sysdeps/unix/sysv/linux/hppa/internaltypes.h: Delete.
        * sysdeps/unix/sysv/linux/hppa/pthread_cond_broadcast.c: Delete.
        * sysdeps/unix/sysv/linux/hppa/pthread_cond_destroy.c: Delete.
        * sysdeps/unix/sysv/linux/hppa/pthread_cond_init.c: Delete.
        * sysdeps/unix/sysv/linux/hppa/pthread_cond_signal.c: Delete.
        * sysdeps/unix/sysv/linux/hppa/pthread_cond_wait.c: Delete.
2017-08-12 13:02:52 -04:00
Florian Weimer f827b1cec3 assert: Suppress pedantic warning caused by statement expression [BZ #21242]
(cherry picked from commit 8b2c63e4e2)
2017-08-11 16:02:55 +02:00
John David Anglin 5b3f978ccf hppa: Fix register corruption in __longjmp.
[BZ #21049]
        * sysdeps/hppa/__longjmp.c (__longjmp): Move call to CHECK_SP up
        to avoid clobbering r26.
2017-08-08 21:29:21 -04:00
H.J. Lu 05dcf1ea88 x86-64: Use _dl_runtime_resolve_opt only with AVX512F [BZ #21871]
On AVX machines with XGETBV (ECX == 1) like Skylake processors,

(gdb) disass _dl_runtime_resolve_avx_opt
Dump of assembler code for function _dl_runtime_resolve_avx_opt:
   0x0000000000015890 <+0>:	push   %rax
   0x0000000000015891 <+1>:	push   %rcx
   0x0000000000015892 <+2>:	push   %rdx
   0x0000000000015893 <+3>:	mov    $0x1,%ecx
   0x0000000000015898 <+8>:	xgetbv
   0x000000000001589b <+11>:	mov    %eax,%r11d
   0x000000000001589e <+14>:	pop    %rdx
   0x000000000001589f <+15>:	pop    %rcx
   0x00000000000158a0 <+16>:	pop    %rax
   0x00000000000158a1 <+17>:	and    $0x4,%r11d
   0x00000000000158a5 <+21>:	bnd je 0x16200 <_dl_runtime_resolve_sse_vex>
End of assembler dump.

is slower than:

(gdb) disass _dl_runtime_resolve_avx_slow
Dump of assembler code for function _dl_runtime_resolve_avx_slow:
   0x0000000000015850 <+0>:	vorpd  %ymm0,%ymm1,%ymm8
   0x0000000000015854 <+4>:	vorpd  %ymm2,%ymm3,%ymm9
   0x0000000000015858 <+8>:	vorpd  %ymm4,%ymm5,%ymm10
   0x000000000001585c <+12>:	vorpd  %ymm6,%ymm7,%ymm11
   0x0000000000015860 <+16>:	vorpd  %ymm8,%ymm9,%ymm9
   0x0000000000015865 <+21>:	vorpd  %ymm10,%ymm11,%ymm10
   0x000000000001586a <+26>:	vpcmpeqd %xmm8,%xmm8,%xmm8
   0x000000000001586f <+31>:	vorpd  %ymm9,%ymm10,%ymm10
   0x0000000000015874 <+36>:	vptest %ymm10,%ymm8
   0x0000000000015879 <+41>:	bnd jae 0x158b0 <_dl_runtime_resolve_avx>
   0x000000000001587c <+44>:	vzeroupper
   0x000000000001587f <+47>:	bnd jmpq 0x16200 <_dl_runtime_resolve_sse_vex>
End of assembler dump.
(gdb)

since xgetbv takes much more cycles than single cycle operations like
vpord/vvpcmpeq/ptest.  _dl_runtime_resolve_opt should be used only with
AVX512 where AVX512 instructions lead to lower CPU frequency on Skylake
server.

	[BZ #21871]
	* sysdeps/x86/cpu-features.c (init_cpu_features): Set
	bit_arch_Use_dl_runtime_resolve_opt only with AVX512F.

(cherry picked from commit d2cf37c0a2)
2017-08-06 11:29:08 -07:00
Aurelien Jarno ea60566c17 i686/multiarch: Regenerate ulps
This comes from running “make regen-ulps” on an AMD Opteron 2378 CPU.

Changelog:
	* sysdeps/i386/i686/fpu/multiarch/libm-test-ulps: Regenerated.
2017-08-04 03:07:39 +02:00
Aurelien Jarno e249a1e2a3 Fix the return type of the getentropy stub
The return type of the getentropy stub is wrongly defined as ssize_t,
while both the <sys/random.h> header and the Linux implementation
define it as int. This patch fixes that.

Changelog:
	* stdlib/getentropy.c (getentropy): Change return type to int.
(cherry picked from commit 2b34e2716f)
2017-08-04 00:52:48 +02:00
Tulio Magno Quites Machado Filho da3e97f94c powerpc: Fix logbl on power7 [BZ# 21280]
1. Fix the results for negative subnormals by ignoring the signal when
    normalizing the value.
 2. Fix the output when the high part is a power of 2 and the low part
    is a nonzero number with opposite sign.  This fix is based on commit
    380bd0fd24.

After applying this patch, logbl() tests pass cleanly on POWER >= 7.

Tested on powerpc, powerpc64 and powerpc64le

	[BZ #21280]
	* sysdeps/powerpc/power7/fpu/s_logbl.c (__logbl): Ignore the
	signal of subnormals and adjust the exponent of power of 2 down
	when low part has opposite sign.

(cherry picked from commit c064f6a613)
2017-08-02 15:28:34 +02:00
H.J. Lu 0edeadc0d3 Avoid .symver on common symbols [BZ #21666]
The .symver directive on common symbol just creates a new common symbol,
not an alias and the newer assembler with the bug fix for

https://sourceware.org/bugzilla/show_bug.cgi?id=21661

will issue an error.  Before the fix, we got

$ readelf -sW libc.so | grep "loc[12s]"
  5109: 00000000003a0608     8 OBJECT  LOCAL  DEFAULT   36 loc1
  5188: 00000000003a0610     8 OBJECT  LOCAL  DEFAULT   36 loc2
  5455: 00000000003a0618     8 OBJECT  LOCAL  DEFAULT   36 locs
  6575: 00000000003a05f0     8 OBJECT  GLOBAL DEFAULT   36 locs@GLIBC_2.2.5
  7156: 00000000003a05f8     8 OBJECT  GLOBAL DEFAULT   36 loc1@GLIBC_2.2.5
  7312: 00000000003a0600     8 OBJECT  GLOBAL DEFAULT   36 loc2@GLIBC_2.2.5

in libc.so.  The versioned loc1, loc2 and locs have the wrong addresses.
After the fix, we got

$ readelf -sW libc.so | grep "loc[12s]"
  6570: 000000000039e3b8     8 OBJECT  GLOBAL DEFAULT   34 locs@GLIBC_2.2.5
  7151: 000000000039e3c8     8 OBJECT  GLOBAL DEFAULT   34 loc1@GLIBC_2.2.5
  7307: 000000000039e3c0     8 OBJECT  GLOBAL DEFAULT   34 loc2@GLIBC_2.2.5

	[BZ #21666]
	* misc/regexp.c (loc1): Add __attribute__ ((nocommon));
	(loc2): Likewise.
	(locs): Likewise.

(cherry picked from commit 388b4f1a02)
2017-07-26 10:09:00 -07:00
Szabolcs Nagy 7cdbe5b6f9 [AArch64] Use hidden __GI__dl_argv in rtld startup code
We rely on the symbol being locally defined so using extern symbol
is not correct and the linker may complain about the relocations.
2017-07-12 16:55:41 +01:00
H.J. Lu 49f97e641e x86-64: Align the stack in __tls_get_addr [BZ #21609]
This change forces realignment of the stack pointer in __tls_get_addr, so
that binaries compiled by GCCs older than GCC 4.9:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066

continue to work even if vector instructions are used in glibc which
require the ABI stack realignment.

__tls_get_addr_slow is added to handle the slow paths in the default
implementation of__tls_get_addr in elf/dl-tls.c.  The new __tls_get_addr
calls __tls_get_addr_slow after realigning the stack.  Internal calls
within ld.so go directly to the default implementation of __tls_get_addr
because they do not need stack realignment.

	[BZ #21609]
	* sysdeps/x86_64/Makefile (sysdep-dl-routines): Add tls_get_addr.
	(gen-as-const-headers): Add rtld-offsets.sym.
	* sysdeps/x86_64/dl-tls.c: New file.
	* sysdeps/x86_64/rtld-offsets.sym: Likwise.
	* sysdeps/x86_64/tls_get_addr.S: Likewise.
	* sysdeps/x86_64/dl-tls.h: Add multiple inclusion guards.
	* sysdeps/x86_64/tlsdesc.sym (TI_MODULE_OFFSET): New.
	(TI_OFFSET_OFFSET): Likwise.

(cherry picked from commit 031e519c95)
2017-07-06 14:34:43 +02:00
Florian Weimer adc7e06fb4 i686: Add missing IS_IN (libc) guards to vectorized strcspn
Since commit d957c4d3fa (i386: Compile
rtld-*.os with -mno-sse -mno-mmx -mfpmath=387), vector intrinsics can
no longer be used in ld.so, even if the compiled code never makes it
into the final ld.so link.  This commit adds the missing IS_IN (libc)
guard to the SSE 4.2 strcspn implementation, so that it can be used from
ld.so in the future.

(cherry picked from commit 69052a3a95)
2017-06-20 06:27:09 +02:00
Siddhesh Poyarekar 3776f38fcd Ignore and remove LD_HWCAP_MASK for AT_SECURE programs (bug #21209)
The LD_HWCAP_MASK environment variable may alter the selection of
function variants for some architectures.  For AT_SECURE process it
means that if an outdated routine has a bug that would otherwise not
affect newer platforms by default, LD_HWCAP_MASK will allow that bug
to be exploited.

To be on the safe side, ignore and disable LD_HWCAP_MASK for setuid
binaries.

	[BZ #21209]
	* elf/rtld.c (process_envvars): Ignore LD_HWCAP_MASK for
	AT_SECURE processes.
	* sysdeps/generic/unsecvars.h: Add LD_HWCAP_MASK.
	* elf/tst-env-setuid.c (test_parent): Test LD_HWCAP_MASK.
	(test_child): Likewise.
	* elf/Makefile (tst-env-setuid-ENV): Add LD_HWCAP_MASK.

(cherry picked from commit 1c1243b6fc)
2017-06-20 05:59:17 +02:00
Florian Weimer c69d4a0f68 ld.so: Reject overly long LD_AUDIT path elements
Also only process the last LD_AUDIT entry.

(cherry picked from commit 81b82fb966)
2017-06-19 22:44:08 +02:00
Florian Weimer 46703a3995 ld.so: Reject overly long LD_PRELOAD path elements
(cherry picked from commit 6d0ba62289)
2017-06-19 22:43:56 +02:00
Florian Weimer 3c7cd21290 CVE-2017-1000366: Ignore LD_LIBRARY_PATH for AT_SECURE=1 programs [BZ #21624]
LD_LIBRARY_PATH can only be used to reorder system search paths, which
is not useful functionality.

This makes an exploitable unbounded alloca in _dl_init_paths unreachable
for AT_SECURE=1 programs.

(cherry picked from commit f6110a8fee)
2017-06-19 18:31:27 +02:00
Santhosh Thottingal f92b102598 Correct collation rules for Malayalam.
[BZ #19922]
	* locales/iso14651_t1_common: Add collation rules for U+07DA to U+07DF.

	[BZ #19919]
	* locales/iso14651_t1_common: Correct collation of U+0D36 and U+0D37.
2017-06-11 10:26:45 -04:00
Florian Weimer b8d2e394a2 fork: Remove bogus parent PID assertions [BZ #21386]
(cherry picked from commit 1d2bc2eae9)
2017-06-09 13:21:43 +02:00
Joseph Myers 34b6f41c14 Fix test-math-vector-sincos.h aliasing.
x86_64 libmvec tests have been failing to build lately with GCC
mainline with -Wuninitialized errors, and Markus Trippelsdorf traced
this to an aliasing issue
<https://sourceware.org/ml/libc-alpha/2017-03/msg00169.html>.

This patch fixes the aliasing issue, so that the vectors-of-pointers
are initialized using a union instead of pointer casts.  This also
fixes the testsuite build failures with GCC mainline.

Tested for x86_64 (full testsuite with GCC 6; testsuite build with GCC
mainline with build-many-glibcs.py).

	* sysdeps/x86/fpu/test-math-vector-sincos.h (INIT_VEC_PTRS_LOOP):
	Use a union when storing pointers.
	(VECTOR_WRAPPER_fFF_2): Do not take address of integer vector and
	cast result when passing to INIT_VEC_PTRS_LOOP.
	(VECTOR_WRAPPER_fFF_3): Likewise.
	(VECTOR_WRAPPER_fFF_4): Likewise.

(cherry picked from commit ffe308e4fc)
2017-06-05 15:14:35 +02:00
Adhemerval Zanella ccb4fd7a65 Fix i686 memchr overflow calculation (BZ#21182)
This patch fixes the regression added by 23d2770 for final address
overflow calculation.  The subtraction of the considered size (16)
at line 120 is at wrong place, for sizes less than 16 subsequent
overflow check will not take in consideration an invalid size (since
the subtraction will be negative).  Also, the lea instruction also
does not raise the carry flag (CF) that is used in subsequent jbe
to check for overflow.

The fix is to follow x86_64 logic from 3daef2c where the overflow
is first check and a sub instruction is issued.  In case of resulting
negative size, CF will be set by the sub instruction and a NULL
result will be returned.  The patch also add similar tests reported
in bug report.

Checked on i686-linux-gnu and x86_64-linux-gnu.

	* string/test-memchr.c (do_test): Add BZ#21182 checks for address
	near end of a page.
	* sysdeps/i386/i686/multiarch/memchr-sse2.S (__memchr): Fix
	overflow calculation.

Cherry-pick of 3abeeec5f4.
2017-05-01 10:32:09 -03:00
H.J. Lu 06d79808f6 x86: Use AVX2 memcpy/memset on Skylake server [BZ #21396]
On Skylake server, AVX512 load/store instructions in memcpy/memset may
lead to lower CPU turbo frequency in certain situations.  Use of AVX2
in memcpy/memset has been observed to have improved overall performance
in many workloads due to the higher frequency.

Since AVX512ER is unique to Xeon Phi, this patch sets Prefer_No_AVX512
if AVX512ER isn't available so that AVX2 versions of memcpy/memset are
used on Skylake server.

	[BZ #21396]
	* sysdeps/x86/cpu-features.c (init_cpu_features): Set
	Prefer_No_AVX512 if AVX512ER isn't available.
	* sysdeps/x86/cpu-features.h (bit_arch_Prefer_No_AVX512): New.
	(index_arch_Prefer_No_AVX512): Likewise.
	* sysdeps/x86_64/multiarch/memcpy.S (__new_memcpy): Don't use
	AVX512 version if Prefer_No_AVX512 is set.
	* sysdeps/x86_64/multiarch/memcpy_chk.S (__memcpy_chk):
	Likewise.
	* sysdeps/x86_64/multiarch/memmove.S (__libc_memmove): Likewise.
	* sysdeps/x86_64/multiarch/memmove_chk.S (__memmove_chk):
	Likewise.
	* sysdeps/x86_64/multiarch/mempcpy.S (__mempcpy): Likewise.
	* sysdeps/x86_64/multiarch/mempcpy_chk.S (__mempcpy_chk):
	Likewise.
	* sysdeps/x86_64/multiarch/memset.S (memset): Likewise.
	* sysdeps/x86_64/multiarch/memset_chk.S (__memset_chk):
	Likewise.

(cherry picked from commit 4cb334c4d6)
2017-04-28 10:04:28 -07:00
H.J. Lu 4c6f97798f x86: Set Prefer_No_VZEROUPPER if AVX512ER is available
AVX512ER won't be implemented in any Xeon processors and will be in
all Xeon Phi processors.  Don't check CPU model number when setting
Prefer_No_VZEROUPPER for Xeon Phi.  Instead, set Prefer_No_VZEROUPPER
if AVX512ER is available.  It works with current and future Xeon Phi
and non-Xeon Phi processors.

	* sysdeps/x86/cpu-features.c (init_cpu_features): Set
	Prefer_No_VZEROUPPER if AVX512ER is available.
	* sysdeps/x86/cpu-features.h
	(bit_cpu_AVX512PF): New.
	(bit_cpu_AVX512ER): Likewise.
	(bit_cpu_AVX512CD): Likewise.
	(bit_cpu_AVX512BW): Likewise.
	(bit_cpu_AVX512VL): Likewise.
	(index_cpu_AVX512PF): Likewise.
	(index_cpu_AVX512ER): Likewise.
	(index_cpu_AVX512CD): Likewise.
	(index_cpu_AVX512BW): Likewise.
	(index_cpu_AVX512VL): Likewise.
	(reg_AVX512PF): Likewise.
	(reg_AVX512ER): Likewise.
	(reg_AVX512CD): Likewise.
	(reg_AVX512BW): Likewise.
	(reg_AVX512VL): Likewise.

(cherry picked from commit 1c53cb49de)
2017-04-28 10:03:29 -07:00
Adhemerval Zanella b30b1c97cc posix: Add cleanup on the trap list for globtest.sh
This patch prevents lingering files for SIGSEGV failures by adding
a cleanup handler on trap handler.  Checked on x86_64-linux-gnu.

	* posix/globtest.sh: Add cleanup routine on trap 0.

Cherry-pick of 4fee33f.
2017-04-20 15:04:29 -03:00
H.J. Lu 903b77defb x86-64: Improve branch predication in _dl_runtime_resolve_avx512_opt [BZ #21258]
On Skylake server, _dl_runtime_resolve_avx512_opt is used to preserve
the first 8 vector registers.  The code layout is

  if only %xmm0 - %xmm7 registers are used
     preserve %xmm0 - %xmm7 registers
  if only %ymm0 - %ymm7 registers are used
     preserve %ymm0 - %ymm7 registers
  preserve %zmm0 - %zmm7 registers

Branch predication always executes the fallthrough code path to preserve
%zmm0 - %zmm7 registers speculatively, even though only %xmm0 - %xmm7
registers are used.  This leads to lower CPU frequency on Skylake
server.  This patch changes the fallthrough code path to preserve
%xmm0 - %xmm7 registers instead:

  if whole %zmm0 - %zmm7 registers are used
    preserve %zmm0 - %zmm7 registers
  if only %ymm0 - %ymm7 registers are used
     preserve %ymm0 - %ymm7 registers
  preserve %xmm0 - %xmm7 registers

Tested on Skylake server.

	[BZ #21258]
	* sysdeps/x86_64/dl-trampoline.S (_dl_runtime_resolve_opt):
	Define only if _dl_runtime_resolve is defined to
	_dl_runtime_resolve_sse_vex.
	* sysdeps/x86_64/dl-trampoline.h (_dl_runtime_resolve_opt):
	Fallthrough to _dl_runtime_resolve_sse_vex.

(cherry picked from commit c15f8eb50c)
2017-04-07 09:52:49 -07:00
Mike Frysinger df29db0bec posix_spawn: use a larger min stack for -fstack-check [BZ #21253]
When glibc is built with -fstack-check, trying to use posix_spawn can
lead to segfaults due to gcc internally probing stack memory too far.
The new spawn API will allocate a minimum of 1 page, but the stack
checking logic might probe a couple of pages.  When it tries to walk
them, everything falls apart.

The gcc internal docs [1] state the default interval checking is one
page.  Which means we need two pages (the current one, and the next
probed).  No target currently defines it larger.

Further, it mentions that the default minimum stack size needed to
recover from an overflow is 4/8KiB for sjlj or 8/12KiB for others.
But some Linux targets (like mips and ppc) go up to 16KiB (and some
non-Linux targets go up to 24KiB).

Let's create each child with a minimum of 32KiB slack space to support
them all, and give us future breathing room.

No test is added as existing ones crash.  Even a simple call is
enough to trigger the problem:
	char *argv[] = { "/bin/ls", NULL };
	posix_spawn(NULL, "/bin/ls", NULL, NULL, argv, NULL);

[1] https://gcc.gnu.org/onlinedocs/gcc-6.3.0/gccint/Stack-Checking.html

(cherry picked from commit 21f042c804)
2017-04-03 15:20:00 -04:00
Wladimir J. van der Laan 74522eeeaa Call the right helper function when setting mallopt M_ARENA_MAX (BZ #21338)
Fixes a typo introduced in commit
be7991c070. This caused
mallopt(M_ARENA_MAX) as well as the environment variable
MALLOC_ARENA_MAX to not work as intended because it set the
wrong internal parameter.

 	[BZ #21338]
	* malloc/malloc.c: Call do_set_arena_max for M_ARENA_MAX
	instead of incorrect do_set_arena_test
2017-04-03 22:16:34 +05:30
Slava Barinov f035c8d055 fts: Fix symbol redirect for fts_set [BZ #21289]
In a 32-bit environment with _FILE_OFFSET_BITS=64, the __REDIRECT macro
combined with __THROW generates an invalid C++ declaration.

(cherry picked from commit ce39613205)
2017-03-31 09:30:52 +02:00
Mike Frysinger 27ab0d9518 posix_spawn: fix stack setup on ia64 [BZ #21275]
The ia64-specific clone2 call expects the base of the stack mapping and
the stack size as sep arguments, not an initial stack value as on other
stack-grows-down architectures.  Reuse the stack-grows-up macro so we
pass in the right stack base.

Reported-by: Matt Turner <mattst88@gentoo.org>
(cherry picked from commit ddc3fb3334)
2017-03-20 10:55:51 -04:00
Mike Frysinger 0889003c67 x86_64: fix static build of __mempcpy_chk for compilers defaulting to PIC/PIE
When glibc is compiled with gcc 6.2 that has been configured with
to default to PIC/PIE, the static version of __mempcpy_chk is not built,
as the test is done on PIC instead of SHARED.  Fix the test to check for
SHARED, like it is done for similar functions like __memcpy_chk.

2017-03-12  Mike Frysinger  <vapier@gentoo.org>

	* sysdeps/x86_64/mempcpy_chk.S (__mempcpy_chk): Check for SHARED
	instead of PIC.

(cherry picked from commit fbe355fbd1)
2017-03-15 16:12:22 -07:00
John David Anglin 8b3caa41b9 hppa: Fix setting of __libc_stack_end
The binutils package was recently changed to fix -z relro support on hppa.
See ld/21000 for details:
https://sourceware.org/bugzilla/show_bug.cgi?id=21000

This exposed a problem with the _dl_start_user function in the RTLD_START
define.  We need to set __libc_stack_end before it is made read only.  For
this, we need to define DL_STACK_END.  The offset of 0x160 gives the same
stack end as the code in _dl_start_user.

A build log with the attached patch is here:
https://buildd.debian.org/status/fetch.php?pkg=glibc&arch=hppa&ver=2.24-9&stamp=1487639205&raw=0

(cherry picked from commit 5d20a49aac)
2017-03-15 14:26:37 -07:00
Florian Weimer 69e0a87cc4 Document and fix --enable-bind-now [BZ #21015]
(cherry picked from commit 2d6ab5df3b)
2017-03-02 20:11:27 +01:00
Florian Weimer 93cf93e06c sunrpc: Improvements for UDP client timeout handling [BZ #20257]
This commit fixes various aspects in the UDP client timeout handling.
Timeouts are now applied in a more consistent fashion.  Discarded UDP
packets no longer prevent the timeout from happening at all.

(cherry picked from commit cf0bd2f73b)
2017-02-28 17:36:00 +01:00
Florian Weimer 045e368799 sunrpc: Avoid use-after-free read access in clntudp_call [BZ #21115]
After commit bc779a1a5b
(CVE-2016-4429: sunrpc: Do not use alloca in clntudp_call
[BZ #20112]), ancillary data is stored on the heap,
but it is accessed after it has been freed.

The test case must be run under a heap debugger such as valgrind
to observe the invalid access.  A malloc implementation which
immediately calls munmap on free would catch this bug as well.

(cherry picked from commit d42eed4a04)
2017-02-28 17:05:46 +01:00
Siddhesh Poyarekar 58520986c3 Fix getting tunable values on big-endian (BZ #21109)
The code to set value passed a tunable_val_t, which when cast to
int32_t on big-endian gives the wrong value.  Instead, use
tunable_val_t.numval instead, which can then be safely cast into
int32_t.

(cherry picked from commit 8cbc826c37)
2017-02-09 17:28:54 +01:00
395 changed files with 17778 additions and 2998 deletions
+1265
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -146,6 +146,12 @@ will be used, and CFLAGS sets optimization options for the compiler.
of routines called directly from assembler are excluded from this
protection.
'--enable-bind-now'
Disable lazy binding for installed shared objects. This provides
additional security hardening because it enables full RELRO and a
read-only global offset table (GOT), at the cost of slightly
increased program load times.
'--enable-pt_chown'
The file 'pt_chown' is a helper binary for 'grantpt' (*note
Pseudo-Terminals: Allocation.) that is installed setuid root to fix
+7
View File
@@ -386,6 +386,13 @@ LDFLAGS.so += $(hashstyle-LDFLAGS)
LDFLAGS-rtld += $(hashstyle-LDFLAGS)
endif
# If lazy relocations are disabled, add the -z now flag. Use
# LDFLAGS-lib.so instead of LDFLAGS.so, to avoid adding the flag to
# test modules.
ifeq ($(bind-now),yes)
LDFLAGS-lib.so += -Wl,-z,now
endif
# Command to run after every final link (executable or shared object).
# This is invoked with $(call after-link,...), so it should operate on
# the file $1. This can be set to do some sort of post-processing on
+15 -7
View File
@@ -127,6 +127,14 @@ $(common-objpfx)cstdlib: $(c++-cstdlib-header)
$(common-objpfx)cmath: $(c++-cmath-header)
$(INSTALL_DATA) $< $@T
$(move-if-change) $@T $@
ifneq (,$(c++-bits-std_abs-h))
# Also make a copy of <bits/std_abs.h> from GCC 7 to prevent it from
# including /usr/include/stdlib.h.
before-compile := $(common-objpfx)bits/std_abs.h $(before-compile)
$(common-objpfx)bits/std_abs.h: $(c++-bits-std_abs-h)
$(INSTALL_DATA) $< $@T
$(move-if-change) $@T $@
endif
endif
before-compile := $(common-objpfx)libc-abis.h $(before-compile)
@@ -588,7 +596,7 @@ $(LINK.o) -shared -static-libgcc -Wl,-O1 $(sysdep-LDFLAGS) \
$(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \
$(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \
-Wl,-soname=lib$(libprefix)$(@F:lib%.so=%).so$($(@F)-version) \
$(LDFLAGS.so) $(LDFLAGS-$(@F:lib%.so=%).so) \
$(LDFLAGS.so) $(LDFLAGS-lib.so) $(LDFLAGS-$(@F:lib%.so=%).so) \
-L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link)
endef
@@ -669,14 +677,17 @@ $(build-module-helper) -o $@ $(shlib-lds-flags) \
$(call after-link,$@)
endef
# sofini.os must be placed last since it terminates .eh_frame section.
build-module-helper-objlist = \
$(patsubst %_pic.a,$(whole-archive) %_pic.a $(no-whole-archive),\
$(filter-out %.lds $(map-file) $(+preinit) $(+postinit) \
$(elf-objpfx)sofini.os \
$(link-libc-deps),$^))
build-module-objlist = $(build-module-helper-objlist) $(LDLIBS-$(@F:%.so=%).so)
build-shlib-objlist = $(build-module-helper-objlist) \
$(LDLIBS-$(@F:lib%.so=%).so)
$(LDLIBS-$(@F:lib%.so=%).so) \
$(filter $(elf-objpfx)sofini.os,$^)
# Don't try to use -lc when making libc.so itself.
# Also omits crti.o and crtn.o, which we do not want
@@ -686,10 +697,6 @@ LDFLAGS-c.so = -nostdlib -nostartfiles
LDLIBS-c.so += $(libc.so-gnulib)
# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main
# If lazy relocation is disabled add the -z now flag.
ifeq ($(bind-now),yes)
LDFLAGS-c.so += -Wl,-z,now
endif
# Pre-link the objects of libc_pic.a so that we can locally resolve
# COMMON symbols before we link against ld.so. This is because ld.so
# contains some of libc_pic.a already, which will prevent the COMMONs
@@ -1104,7 +1111,8 @@ $(common-objpfx)format.lds: $(..)scripts/output-format.sed \
ifneq (unknown,$(output-format))
echo > $@.new 'OUTPUT_FORMAT($(output-format))'
else
$(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) $(LDFLAGS.so) \
$(LINK.o) -shared $(sysdep-LDFLAGS) $(rtld-LDFLAGS) \
$(LDFLAGS.so) $(LDFLAGS-lib.so) \
-x c /dev/null -o $@.so -Wl,--verbose -v 2>&1 \
| sed -n -f $< > $@.new
test -s $@.new
+107
View File
@@ -4,6 +4,113 @@ See the end for copying conditions.
Please send GNU C library bug reports via <http://sourceware.org/bugzilla/>
using `glibc' in the "product" field.
Version 2.25.1
Security related changes:
* The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes,
to avoid fragmentation-based spoofing attacks.
CVE-2017-15670: The glob function, when invoked with GLOB_TILDE, suffered
from a one-byte overflow during ~ operator processing (either on the stack
or the heap, depending on the length of the user name).
CVE-2017-15671: The glob function, when invoked with GLOB_TILDE,
would sometimes fail to free memory allocated during ~ operator
processing, leading to a memory leak and, potentially, to a denial
of service.
CVE-2017-15804: The glob function, when invoked with GLOB_TILDE and
without GLOB_NOESCAPE, could write past the end of a buffer while
unescaping user names. Reported by Tim Rühsen.
CVE-2017-1000408: Incorrect array size computation in _dl_init_paths leads
to the allocation of too much memory. (This is not a security bug per se,
it is mentioned here only because of the CVE assignment.) Reported by
Qualys.
CVE-2017-1000409: Buffer overflow in _dl_init_paths due to miscomputation
of the number of search path components. (This is not a security
vulnerability per se because no trust boundary is crossed if the fix for
CVE-2017-1000366 has been applied, but it is mentioned here only because
of the CVE assignment.) Reported by Qualys.
CVE-2017-16997: Incorrect handling of RPATH or RUNPATH containing $ORIGIN
for AT_SECURE or SUID binaries could be used to load libraries from the
current directory.
CVE-2018-1000001: Buffer underflow in realpath function when getcwd function
succeeds without returning an absolute path due to unexpected behaviour
of the Linux kernel getcwd syscall. Reported by halfdog.
CVE-2018-6485: The posix_memalign and memalign functions, when called with
an object size near the value of SIZE_MAX, would return a pointer to a
buffer which is too small, instead of NULL. Reported by Jakub Wilk.
CVE-2018-6551: The malloc function, when called with an object size near
the value of SIZE_MAX, would return a pointer to a buffer which is too
small, instead of NULL.
CVE-2019-6488: On x32, the size_t parameter may be passed in the lower
32 bits of a 64-bit register with with non-zero upper 32 bit. When it
happened, accessing the 32-bit size_t value as the full 64-bit register
in the assembly string/memory functions would cause a buffer overflow.
Reported by H.J. Lu.
CVE-2019-7309: x86-64 memcmp used signed Jcc instructions to check
size. For x86-64, memcmp on an object size larger than SSIZE_MAX
has undefined behavior. On x32, the size_t argument may be passed
in the lower 32 bits of the 64-bit RDX register with non-zero upper
32 bits. When it happened with the sign bit of RDX register set,
memcmp gave the wrong result since it treated the size argument as
zero. Reported by H.J. Lu.
CVE-2019-19126: ld.so failed to ignore the LD_PREFER_MAP_32BIT_EXEC
environment variable during program execution after a security
transition, allowing local attackers to restrict the possible mapping
addresses for loaded libraries and thus bypass ASLR for a setuid
program. Reported by Marcin Kościelnicki.
The following bugs are resolved with this release:
[20257] sunrpc: clntudp_call does not enforce timeout when receiving data
[20568] Fix crash in _IO_wfile_sync
[21015] Document and fix --enable-bind-now
[21109] Tunables broken on big-endian
[21115] sunrpc: Use-after-free in error path in clntudp_call
[21209] Ignore and remove LD_HWCAP_MASK for AT_SECURE programs
[21242] assert: Suppress pedantic warning caused by statement expression
[21265] x86-64: Use fxsave/xsave/xsavec in _dl_runtime_resolve
[21289] Fix symbol redirect for fts_set
[21295] getaddrinfo: Merge IPv6 addresses and IPv4 addresses
[21298] rwlock can deadlock on frequent reader/writer phase switching
[21359] resolv: Support an exactly sized buffer in ns_name_pack
[21386] Assertion in fork for distinct parent PID is incorrect
[21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366)
[21654] nss: Fix invalid cast in group merging
[21744] tunables: Use direct syscall for access
[21778] Robust mutex may deadlock
[21915] getaddrinfo: incorrect result handling for NSS service modules
[21922] getaddrinfo with AF_INET(6) returns EAI_NONAME, not EAI_NODATA
[21972] assert macro requires operator== (int) for its argument type
[22078] nss_files performance issue in hosts multi mode
[22322] libc: [mips64] wrong bits/long-double.h installed
[22343] malloc: Integer overflow in posix_memalign (CVE-2018-6485)
[22636] PTHREAD_STACK_MIN is too small on x86-64
[22627] $ORIGIN in $LD_LIBRARY_PATH is substituted twice
[22637] nptl: Fix stack guard size accounting
[22679] getcwd(3) can succeed without returning an absolute path
(CVE-2018-1000001)
[22685] powerpc: Fix syscalls during early process initialization
[22715] x86-64: Properly align La_x86_64_retval to VEC_SIZE
[22774] malloc: Integer overflow in malloc (CVE-2018-6551)
[23538] pthread_cond_broadcast: Fix waiters-after-spinning case
[24027] malloc: Integer overflow in realloc
[24097] Can't use 64-bit register for size_t in assembly codes for x32 (CVE-2019-6488)
[24155] x32 memcmp can treat positive length as 0 (if sign bit in RDX is set) (CVE-2019-7309)
[25204] Ignore LD_PREFER_MAP_32BIT_EXEC for SUID programs
Version 2.25
+10 -1
View File
@@ -25,6 +25,15 @@ include ../Makeconfig
headers := assert.h
routines := assert assert-perr __assert
tests := test-assert test-assert-perr
tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++
ifeq ($(have-cxx-thread_local),yes)
CFLAGS-tst-assert-c++.o = -std=c++11
LDLIBS-tst-assert-c++ = -lstdc++
CFLAGS-tst-assert-g++.o = -std=gnu++11
LDLIBS-tst-assert-g++ = -lstdc++
else
tests-unsupported += tst-assert-c++ tst-assert-g++
endif
include ../Rules
+14 -4
View File
@@ -85,19 +85,29 @@ __END_DECLS
/* When possible, define assert so that it does not add extra
parentheses around EXPR. Otherwise, those added parentheses would
suppress warnings we'd expect to be detected by gcc's -Wparentheses. */
# if !defined __GNUC__ || defined __STRICT_ANSI__
# if defined __cplusplus
# define assert(expr) \
(static_cast <bool> (expr) \
? void (0) \
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
# elif !defined __GNUC__ || defined __STRICT_ANSI__
# define assert(expr) \
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
# else
/* The first occurrence of EXPR is not evaluated due to the sizeof,
but will trigger any pedantic warnings masked by the __extension__
for the second occurrence. The ternary operator is required to
support function pointers and bit fields in this context, and to
suppress the evaluation of variable length arrays. */
# define assert(expr) \
({ \
((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
if (expr) \
; /* empty */ \
else \
__assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
})
}))
# endif
# ifdef __USE_GNU
@@ -113,7 +123,7 @@ __END_DECLS
C9x has a similar variable called __func__, but prefer the GCC one since
it demangles C++ function names. */
# if defined __cplusplus ? __GNUC_PREREQ (2, 6) : __GNUC_PREREQ (2, 4)
# define __ASSERT_FUNCTION __PRETTY_FUNCTION__
# define __ASSERT_FUNCTION __extension__ __PRETTY_FUNCTION__
# else
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __ASSERT_FUNCTION __func__
+78
View File
@@ -0,0 +1,78 @@
/* Tests for interactions between C++ and assert.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <assert.h>
/* The C++ standard requires that if the assert argument is a constant
subexpression, then the assert itself is one, too. */
constexpr int
check_constexpr ()
{
return (assert (true), 1);
}
/* Objects of this class can be contextually converted to bool, but
cannot be compared to int. */
struct no_int
{
no_int () = default;
no_int (const no_int &) = delete;
explicit operator bool () const
{
return true;
}
bool operator! () const; /* No definition. */
template <class T> bool operator== (T) const; /* No definition. */
template <class T> bool operator!= (T) const; /* No definition. */
};
/* This class tests that operator== is not used by assert. */
struct bool_and_int
{
bool_and_int () = default;
bool_and_int (const no_int &) = delete;
explicit operator bool () const
{
return true;
}
bool operator! () const; /* No definition. */
template <class T> bool operator== (T) const; /* No definition. */
template <class T> bool operator!= (T) const; /* No definition. */
};
static int
do_test ()
{
{
no_int value;
assert (value);
}
{
bool_and_int value;
assert (value);
}
return 0;
}
#include <support/test-driver.c>
+19
View File
@@ -0,0 +1,19 @@
/* Tests for interactions between C++ and assert. GNU C++11 version.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <tst-assert-c++.cc>
+1
View File
@@ -47,6 +47,7 @@ sysincludes = @SYSINCLUDES@
c++-sysincludes = @CXX_SYSINCLUDES@
c++-cstdlib-header = @CXX_CSTDLIB_HEADER@
c++-cmath-header = @CXX_CMATH_HEADER@
c++-bits-std_abs-h = @CXX_BITS_STD_ABS_H@
all-warnings = @all_warnings@
enable-werror = @enable_werror@
Vendored
+5 -1
View File
@@ -634,6 +634,7 @@ BISON
INSTALL_INFO
PERL
BASH_SHELL
CXX_BITS_STD_ABS_H
CXX_CMATH_HEADER
CXX_CSTDLIB_HEADER
CXX_SYSINCLUDES
@@ -5318,14 +5319,17 @@ fi
# copy of those headers in Makerules.
if test -n "$CXX"; then
find_cxx_header () {
echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \
| sed -n "\,$1:,{s/:\$//;p}"
}
CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
CXX_CMATH_HEADER="$(find_cxx_header cmath)"
CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)"
fi
# Test if LD_LIBRARY_PATH contains the notation for the current directory
# since this would lead to problems installing/building glibc.
# LD_LIBRARY_PATH contains the current directory if one of the following
+4 -1
View File
@@ -1176,13 +1176,16 @@ AC_SUBST(CXX_SYSINCLUDES)
# copy of those headers in Makerules.
if test -n "$CXX"; then
find_cxx_header () {
echo "#include <$1>" | $CXX -M -MP -x c++ - | sed -n "/$1:/{s/:\$//;p}"
echo "#include <$1>" | $CXX -M -MP -x c++ - 2>/dev/null \
| sed -n "\,$1:,{s/:\$//;p}"
}
CXX_CSTDLIB_HEADER="$(find_cxx_header cstdlib)"
CXX_CMATH_HEADER="$(find_cxx_header cmath)"
CXX_BITS_STD_ABS_H="$(find_cxx_header bits/std_abs.h)"
fi
AC_SUBST(CXX_CSTDLIB_HEADER)
AC_SUBST(CXX_CMATH_HEADER)
AC_SUBST(CXX_BITS_STD_ABS_H)
# Test if LD_LIBRARY_PATH contains the notation for the current directory
# since this would lead to problems installing/building glibc.
+2 -1
View File
@@ -1398,6 +1398,7 @@ $(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so
$(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
$(objpfx)tst-nodelete-dlclose-plugin.so
tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096
tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
LD_HWCAP_MASK=0xffffffff
tst-env-setuid-tunables-ENV = \
GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096
+47 -53
View File
@@ -37,6 +37,7 @@
#include <sysdep.h>
#include <stap-probe.h>
#include <libc-internal.h>
#include <array_length.h>
#include <dl-dst.h>
#include <dl-load.h>
@@ -103,7 +104,9 @@ static size_t ncapstr attribute_relro;
static size_t max_capstrlen attribute_relro;
/* Get the generated information about the trusted directories. */
/* Get the generated information about the trusted directories. Use
an array of concatenated strings to avoid relocations. See
gen-trusted-dirs.awk. */
#include "trusted-dirs.h"
static const char system_dirs[] = SYSTEM_DIRS;
@@ -111,9 +114,7 @@ static const size_t system_dirs_len[] =
{
SYSTEM_DIRS_LEN
};
#define nsystem_dirs_len \
(sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
#define nsystem_dirs_len array_length (system_dirs_len)
static bool
is_trusted_path (const char *path, size_t len)
@@ -433,32 +434,41 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
{
char *cp;
size_t nelems = 0;
char *to_free;
while ((cp = __strsep (&rpath, sep)) != NULL)
{
struct r_search_path_elem *dirp;
char *to_free = NULL;
size_t len = 0;
to_free = cp = expand_dynamic_string_token (l, cp, 1);
size_t len = strlen (cp);
/* `strsep' can pass an empty string. This has to be
interpreted as `use the current directory'. */
if (len == 0)
/* `strsep' can pass an empty string. */
if (*cp != '\0')
{
static const char curwd[] = "./";
cp = (char *) curwd;
to_free = cp = expand_dynamic_string_token (l, cp, 1);
/* expand_dynamic_string_token can return NULL in case of empty
path or memory allocation failure. */
if (cp == NULL)
continue;
/* Compute the length after dynamic string token expansion and
ignore empty paths. */
len = strlen (cp);
if (len == 0)
{
free (to_free);
continue;
}
/* Remove trailing slashes (except for "/"). */
while (len > 1 && cp[len - 1] == '/')
--len;
/* Now add one if there is none so far. */
if (len > 0 && cp[len - 1] != '/')
cp[len++] = '/';
}
/* Remove trailing slashes (except for "/"). */
while (len > 1 && cp[len - 1] == '/')
--len;
/* Now add one if there is none so far. */
if (len > 0 && cp[len - 1] != '/')
cp[len++] = '/';
/* Make sure we don't use untrusted directories if we run SUID. */
if (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len))
{
@@ -621,6 +631,14 @@ decompose_rpath (struct r_search_path_struct *sps,
necessary. */
free (copy);
/* There is no path after expansion. */
if (result[0] == NULL)
{
free (result);
sps->dirs = (struct r_search_path_elem **) -1;
return false;
}
sps->dirs = result;
/* The caller will change this value if we haven't used a real malloc. */
sps->malloced = 1;
@@ -688,9 +706,8 @@ _dl_init_paths (const char *llp)
+ ncapstr * sizeof (enum r_dir_status))
/ sizeof (struct r_search_path_elem));
rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
* round_size * sizeof (struct r_search_path_elem));
rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size
* sizeof (*rtld_search_dirs.dirs[0]));
if (rtld_search_dirs.dirs[0] == NULL)
{
errstring = N_("cannot create cache for search path");
@@ -776,37 +793,14 @@ _dl_init_paths (const char *llp)
if (llp != NULL && *llp != '\0')
{
size_t nllp;
const char *cp = llp;
char *llp_tmp;
#ifdef SHARED
/* Expand DSTs. */
size_t cnt = DL_DST_COUNT (llp, 1);
if (__glibc_likely (cnt == 0))
llp_tmp = strdupa (llp);
else
{
/* Determine the length of the substituted string. */
size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt);
/* Allocate the necessary memory. */
llp_tmp = (char *) alloca (total + 1);
llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1);
}
#else
llp_tmp = strdupa (llp);
#endif
char *llp_tmp = strdupa (llp);
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
elements it has. */
nllp = 1;
while (*cp)
{
if (*cp == ':' || *cp == ';')
++nllp;
++cp;
}
size_t nllp = 1;
for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
if (*cp == ':' || *cp == ';')
++nllp;
env_path_list.dirs = (struct r_search_path_elem **)
malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));
+2 -2
View File
@@ -21,8 +21,6 @@
# define _TUNABLE_TYPES_H_
#include <stddef.h>
typedef void (*tunable_callback_t) (void *);
typedef enum
{
TUNABLE_TYPE_INT_32,
@@ -43,6 +41,8 @@ typedef union
const char *strval;
} tunable_val_t;
typedef void (*tunable_callback_t) (tunable_val_t *);
/* Security level for tunables. This decides what to do with individual
tunables for AT_SECURE binaries. */
typedef enum
+4
View File
@@ -30,6 +30,8 @@
#define TUNABLES_INTERNAL 1
#include "dl-tunables.h"
#include <not-errno.h>
#if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
# define GLIBC_TUNABLES "GLIBC_TUNABLES"
#endif
@@ -455,6 +457,8 @@ __tunable_set_val (tunable_id_t id, void *valp, tunable_callback_t callback)
if (cur->strval == NULL)
return;
/* Caller does not need the value, just call the callback with our tunable
value. */
if (valp == NULL)
goto cb;
+165 -33
View File
@@ -99,14 +99,121 @@ uintptr_t __pointer_chk_guard_local
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
#endif
/* Length limits for names and paths, to protect the dynamic linker,
particularly when __libc_enable_secure is active. */
#ifdef NAME_MAX
# define SECURE_NAME_LIMIT NAME_MAX
#else
# define SECURE_NAME_LIMIT 255
#endif
#ifdef PATH_MAX
# define SECURE_PATH_LIMIT PATH_MAX
#else
# define SECURE_PATH_LIMIT 1024
#endif
/* List of auditing DSOs. */
/* Check that AT_SECURE=0, or that the passed name does not contain
directories and is not overly long. Reject empty names
unconditionally. */
static bool
dso_name_valid_for_suid (const char *p)
{
if (__glibc_unlikely (__libc_enable_secure))
{
/* Ignore pathnames with directories for AT_SECURE=1
programs, and also skip overlong names. */
size_t len = strlen (p);
if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL)
return false;
}
return *p != '\0';
}
/* LD_AUDIT variable contents. Must be processed before the
audit_list below. */
const char *audit_list_string;
/* Cyclic list of auditing DSOs. audit_list->next is the first
element. */
static struct audit_list
{
const char *name;
struct audit_list *next;
} *audit_list;
/* Iterator for audit_list_string followed by audit_list. */
struct audit_list_iter
{
/* Tail of audit_list_string still needing processing, or NULL. */
const char *audit_list_tail;
/* The list element returned in the previous iteration. NULL before
the first element. */
struct audit_list *previous;
/* Scratch buffer for returning a name which is part of
audit_list_string. */
char fname[SECURE_NAME_LIMIT];
};
/* Initialize an audit list iterator. */
static void
audit_list_iter_init (struct audit_list_iter *iter)
{
iter->audit_list_tail = audit_list_string;
iter->previous = NULL;
}
/* Iterate through both audit_list_string and audit_list. */
static const char *
audit_list_iter_next (struct audit_list_iter *iter)
{
if (iter->audit_list_tail != NULL)
{
/* First iterate over audit_list_string. */
while (*iter->audit_list_tail != '\0')
{
/* Split audit list at colon. */
size_t len = strcspn (iter->audit_list_tail, ":");
if (len > 0 && len < sizeof (iter->fname))
{
memcpy (iter->fname, iter->audit_list_tail, len);
iter->fname[len] = '\0';
}
else
/* Do not return this name to the caller. */
iter->fname[0] = '\0';
/* Skip over the substring and the following delimiter. */
iter->audit_list_tail += len;
if (*iter->audit_list_tail == ':')
++iter->audit_list_tail;
/* If the name is valid, return it. */
if (dso_name_valid_for_suid (iter->fname))
return iter->fname;
/* Otherwise, wrap around and try the next name. */
}
/* Fall through to the procesing of audit_list. */
}
if (iter->previous == NULL)
{
if (audit_list == NULL)
/* No pre-parsed audit list. */
return NULL;
/* Start of audit list. The first list element is at
audit_list->next (cyclic list). */
iter->previous = audit_list->next;
return iter->previous->name;
}
if (iter->previous == audit_list)
/* Cyclic list wrap-around. */
return NULL;
iter->previous = iter->previous->next;
return iter->previous->name;
}
#ifndef HAVE_INLINED_SYSCALLS
/* Set nonzero during loading and initialization of executable and
libraries, cleared before the executable's entry point runs. This
@@ -716,6 +823,42 @@ static const char *preloadlist attribute_relro;
/* Nonzero if information about versions has to be printed. */
static int version_info attribute_relro;
/* The LD_PRELOAD environment variable gives list of libraries
separated by white space or colons that are loaded before the
executable's dependencies and prepended to the global scope list.
(If the binary is running setuid all elements containing a '/' are
ignored since it is insecure.) Return the number of preloads
performed. */
unsigned int
handle_ld_preload (const char *preloadlist, struct link_map *main_map)
{
unsigned int npreloads = 0;
const char *p = preloadlist;
char fname[SECURE_PATH_LIMIT];
while (*p != '\0')
{
/* Split preload list at space/colon. */
size_t len = strcspn (p, " :");
if (len > 0 && len < sizeof (fname))
{
memcpy (fname, p, len);
fname[len] = '\0';
}
else
fname[0] = '\0';
/* Skip over the substring and the following delimiter. */
p += len;
if (*p != '\0')
++p;
if (dso_name_valid_for_suid (fname))
npreloads += do_preload (fname, main_map, "LD_PRELOAD");
}
return npreloads;
}
static void
dl_main (const ElfW(Phdr) *phdr,
ElfW(Word) phnum,
@@ -1238,11 +1381,13 @@ of this helper program; chances are you did not intend to run this program.\n\
GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
/* If we have auditing DSOs to load, do it now. */
if (__glibc_unlikely (audit_list != NULL))
bool need_security_init = true;
if (__glibc_unlikely (audit_list != NULL)
|| __glibc_unlikely (audit_list_string != NULL))
{
/* Iterate over all entries in the list. The order is important. */
struct audit_ifaces *last_audit = NULL;
struct audit_list *al = audit_list->next;
struct audit_list_iter al_iter;
audit_list_iter_init (&al_iter);
/* Since we start using the auditing DSOs right away we need to
initialize the data structures now. */
@@ -1253,9 +1398,14 @@ of this helper program; chances are you did not intend to run this program.\n\
use different values (especially the pointer guard) and will
fail later on. */
security_init ();
need_security_init = false;
do
while (true)
{
const char *name = audit_list_iter_next (&al_iter);
if (name == NULL)
break;
int tls_idx = GL(dl_tls_max_dtv_idx);
/* Now it is time to determine the layout of the static TLS
@@ -1264,7 +1414,7 @@ of this helper program; chances are you did not intend to run this program.\n\
no DF_STATIC_TLS bit is set. The reason is that we know
glibc will use the static model. */
struct dlmopen_args dlmargs;
dlmargs.fname = al->name;
dlmargs.fname = name;
dlmargs.map = NULL;
const char *objname;
@@ -1277,7 +1427,7 @@ of this helper program; chances are you did not intend to run this program.\n\
not_loaded:
_dl_error_printf ("\
ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
al->name, err_str);
name, err_str);
if (malloced)
free ((char *) err_str);
}
@@ -1381,10 +1531,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
goto not_loaded;
}
}
al = al->next;
}
while (al != audit_list->next);
/* If we have any auditing modules, announce that we already
have two objects loaded. */
@@ -1462,23 +1609,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
if (__glibc_unlikely (preloadlist != NULL))
{
/* The LD_PRELOAD environment variable gives list of libraries
separated by white space or colons that are loaded before the
executable's dependencies and prepended to the global scope
list. If the binary is running setuid all elements
containing a '/' are ignored since it is insecure. */
char *list = strdupa (preloadlist);
char *p;
HP_TIMING_NOW (start);
/* Prevent optimizing strsep. Speed is not important here. */
while ((p = (strsep) (&list, " :")) != NULL)
if (p[0] != '\0'
&& (__builtin_expect (! __libc_enable_secure, 1)
|| strchr (p, '/') == NULL))
npreloads += do_preload (p, main_map, "LD_PRELOAD");
npreloads += handle_ld_preload (preloadlist, main_map);
HP_TIMING_NOW (stop);
HP_TIMING_DIFF (diff, start, stop);
HP_TIMING_ACCUM_NT (load_time, diff);
@@ -1663,7 +1795,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
if (tcbp == NULL)
tcbp = init_tls ();
if (__glibc_likely (audit_list == NULL))
if (__glibc_likely (need_security_init))
/* Initialize security features. But only if we have not done it
earlier. */
security_init ();
@@ -2294,9 +2426,7 @@ process_dl_audit (char *str)
char *p;
while ((p = (strsep) (&str, ":")) != NULL)
if (p[0] != '\0'
&& (__builtin_expect (! __libc_enable_secure, 1)
|| strchr (p, '/') == NULL))
if (dso_name_valid_for_suid (p))
{
/* This is using the local malloc, not the system malloc. The
memory can never be freed. */
@@ -2360,7 +2490,7 @@ process_envvars (enum mode *modep)
break;
}
if (memcmp (envline, "AUDIT", 5) == 0)
process_dl_audit (&envline[6]);
audit_list_string = &envline[6];
break;
case 7:
@@ -2404,7 +2534,8 @@ process_envvars (enum mode *modep)
case 10:
/* Mask for the important hardware capabilities. */
if (memcmp (envline, "HWCAP_MASK", 10) == 0)
if (!__libc_enable_secure
&& memcmp (envline, "HWCAP_MASK", 10) == 0)
GLRO(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL,
0, 0);
break;
@@ -2418,7 +2549,8 @@ process_envvars (enum mode *modep)
case 12:
/* The library search path. */
if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
if (!__libc_enable_secure
&& memcmp (envline, "LIBRARY_PATH", 12) == 0)
{
library_path = &envline[13];
break;
+12
View File
@@ -213,6 +213,12 @@ test_child (void)
return 1;
}
if (getenv ("LD_HWCAP_MASK") != NULL)
{
printf ("LD_HWCAP_MASK still set\n");
return 1;
}
return 0;
}
#endif
@@ -233,6 +239,12 @@ test_parent (void)
return 1;
}
if (getenv ("LD_HWCAP_MASK") == NULL)
{
printf ("LD_HWCAP_MASK lost\n");
return 1;
}
return 0;
}
#endif
+17 -1
View File
@@ -85,6 +85,14 @@ __copy_grp (const struct group srcgrp, const size_t buflen,
}
members[i] = NULL;
/* Align for pointers. We can't simply align C because we need to
align destbuf[c]. */
if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0)
{
uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1);
c += __alignof__(char **) - mis_align;
}
/* Copy the pointers from the members array into the buffer and assign them
to the gr_mem member of destgrp. */
destgrp->gr_mem = (char **) &destbuf[c];
@@ -129,7 +137,7 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
/* Get the count of group members from the last sizeof (size_t) bytes in the
mergegrp buffer. */
savedmemcount = (size_t) *(savedend - sizeof (size_t));
savedmemcount = *(size_t *) (savedend - sizeof (size_t));
/* Get the count of new members to add. */
for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++)
@@ -168,6 +176,14 @@ __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
/* Add the NULL-terminator. */
members[savedmemcount + memcount] = NULL;
/* Align for pointers. We can't simply align C because we need to
align savedbuf[c]. */
if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0)
{
uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1);
c += __alignof__(char **) - mis_align;
}
/* Copy the member array back into the buffer after the member list and free
the member array. */
savedgrp->gr_mem = (char **) &savedbuf[c];
+5
View File
@@ -63,6 +63,11 @@ modules := ISO8859-1 ISO8859-2 ISO8859-3 ISO8859-4 ISO8859-5 \
MAC-CENTRALEUROPE KOI8-RU ISO8859-9E \
CP770 CP771 CP772 CP773 CP774
# If lazy binding is disabled, use BIND_NOW for the gconv modules.
ifeq ($(bind-now),yes)
LDFLAGS.so += -Wl,-z,now
endif
modules.so := $(addsuffix .so, $(modules))
ifeq (yes,$(build-shared))
+367
View File
@@ -0,0 +1,367 @@
/* Allocation from a fixed-size buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Allocation buffers are used to carve out sub-allocations from a
larger allocation. Their primary application is in writing NSS
modules, which receive a caller-allocated buffer in which they are
expected to store variable-length results:
void *buffer = ...;
size_t buffer_size = ...;
struct alloc_buffer buf = alloc_buffer_create (buffer, buffer_size);
result->gr_name = alloc_buffer_copy_string (&buf, name);
// Allocate a list of group_count groups and copy strings into it.
char **group_list = alloc_buffer_alloc_array
(&buf, char *, group_count + 1);
if (group_list == NULL)
return ...; // Request a larger buffer.
for (int i = 0; i < group_count; ++i)
group_list[i] = alloc_buffer_copy_string (&buf, group_list_src[i]);
group_list[group_count] = NULL;
...
if (alloc_buffer_has_failed (&buf))
return ...; // Request a larger buffer.
result->gr_mem = group_list;
...
Note that it is not necessary to check the results of individual
allocation operations if the returned pointer is not dereferenced.
Allocation failure is sticky, so one check using
alloc_buffer_has_failed at the end covers all previous failures.
A different use case involves combining multiple heap allocations
into a single, large one. In the following example, an array of
doubles and an array of ints is allocated:
size_t double_array_size = ...;
size_t int_array_size = ...;
void *heap_ptr;
struct alloc_buffer buf = alloc_buffer_allocate
(double_array_size * sizeof (double) + int_array_size * sizeof (int),
&heap_ptr);
_Static_assert (__alignof__ (double) >= __alignof__ (int),
"no padding after double array");
double *double_array = alloc_buffer_alloc_array
(&buf, double, double_array_size);
int *int_array = alloc_buffer_alloc_array (&buf, int, int_array_size);
if (alloc_buffer_has_failed (&buf))
return ...; // Report error.
...
free (heap_ptr);
The advantage over manual coding is that the computation of the
allocation size does not need an overflow check. In case of an
overflow, one of the subsequent allocations from the buffer will
fail. The initial size computation is checked for consistency at
run time, too. */
#ifndef _ALLOC_BUFFER_H
#define _ALLOC_BUFFER_H
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/param.h>
/* struct alloc_buffer objects refer to a region of bytes in memory of a
fixed size. The functions below can be used to allocate single
objects and arrays from this memory region, or write to its end.
On allocation failure (or if an attempt to write beyond the end of
the buffer with one of the copy functions), the buffer enters a
failed state.
struct alloc_buffer objects can be copied. The backing buffer will
be shared, but the current write position will be independent.
Conceptually, the memory region consists of a current write pointer
and a limit, beyond which the write pointer cannot move. */
struct alloc_buffer
{
/* uintptr_t is used here to simplify the alignment code, and to
avoid issues undefined subtractions if the buffer covers more
than half of the address space (which would result in differences
which could not be represented as a ptrdiff_t value). */
uintptr_t __alloc_buffer_current;
uintptr_t __alloc_buffer_end;
};
enum
{
/* The value for the __alloc_buffer_current member which marks the
buffer as invalid (together with a zero-length buffer). */
__ALLOC_BUFFER_INVALID_POINTER = 0,
};
/* Internal function. Terminate the process using __libc_fatal. */
void __libc_alloc_buffer_create_failure (void *start, size_t size);
/* Create a new allocation buffer. The byte range from START to START
+ SIZE - 1 must be valid, and the allocation buffer allocates
objects from that range. If START is NULL (so that SIZE must be
0), the buffer is marked as failed immediately. */
static inline struct alloc_buffer
alloc_buffer_create (void *start, size_t size)
{
uintptr_t current = (uintptr_t) start;
uintptr_t end = (uintptr_t) start + size;
if (end < current)
__libc_alloc_buffer_create_failure (start, size);
return (struct alloc_buffer) { current, end };
}
/* Internal function. See alloc_buffer_allocate below. */
struct alloc_buffer __libc_alloc_buffer_allocate (size_t size, void **pptr)
__attribute__ ((nonnull (2)));
/* Allocate a buffer of SIZE bytes using malloc. The returned buffer
is in a failed state if malloc fails. *PPTR points to the start of
the buffer and can be used to free it later, after the returned
buffer has been freed. */
static __always_inline __attribute__ ((nonnull (2)))
struct alloc_buffer alloc_buffer_allocate (size_t size, void **pptr)
{
return __libc_alloc_buffer_allocate (size, pptr);
}
/* Mark the buffer as failed. */
static inline void __attribute__ ((nonnull (1)))
alloc_buffer_mark_failed (struct alloc_buffer *buf)
{
buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER;
buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER;
}
/* Return the remaining number of bytes in the buffer. */
static __always_inline __attribute__ ((nonnull (1))) size_t
alloc_buffer_size (const struct alloc_buffer *buf)
{
return buf->__alloc_buffer_end - buf->__alloc_buffer_current;
}
/* Return true if the buffer has been marked as failed. */
static inline bool __attribute__ ((nonnull (1)))
alloc_buffer_has_failed (const struct alloc_buffer *buf)
{
return buf->__alloc_buffer_current == __ALLOC_BUFFER_INVALID_POINTER;
}
/* Add a single byte to the buffer (consuming the space for this
byte). Mark the buffer as failed if there is not enough room. */
static inline void __attribute__ ((nonnull (1)))
alloc_buffer_add_byte (struct alloc_buffer *buf, unsigned char b)
{
if (__glibc_likely (buf->__alloc_buffer_current < buf->__alloc_buffer_end))
{
*(unsigned char *) buf->__alloc_buffer_current = b;
++buf->__alloc_buffer_current;
}
else
alloc_buffer_mark_failed (buf);
}
/* Obtain a pointer to LENGTH bytes in BUF, and consume these bytes.
NULL is returned if there is not enough room, and the buffer is
marked as failed, or if the buffer has already failed.
(Zero-length allocations from an empty buffer which has not yet
failed succeed.) */
static inline __attribute__ ((nonnull (1))) void *
alloc_buffer_alloc_bytes (struct alloc_buffer *buf, size_t length)
{
if (length <= alloc_buffer_size (buf))
{
void *result = (void *) buf->__alloc_buffer_current;
buf->__alloc_buffer_current += length;
return result;
}
else
{
alloc_buffer_mark_failed (buf);
return NULL;
}
}
/* Internal function. Statically assert that the type size is
constant and valid. */
static __always_inline size_t
__alloc_buffer_assert_size (size_t size)
{
if (!__builtin_constant_p (size))
{
__errordecl (error, "type size is not constant");
error ();
}
else if (size == 0)
{
__errordecl (error, "type size is zero");
error ();
}
return size;
}
/* Internal function. Statically assert that the type alignment is
constant and valid. */
static __always_inline size_t
__alloc_buffer_assert_align (size_t align)
{
if (!__builtin_constant_p (align))
{
__errordecl (error, "type alignment is not constant");
error ();
}
else if (align == 0)
{
__errordecl (error, "type alignment is zero");
error ();
}
else if (!powerof2 (align))
{
__errordecl (error, "type alignment is not a power of two");
error ();
}
return align;
}
/* Internal function. Obtain a pointer to an object. */
static inline __attribute__ ((nonnull (1))) void *
__alloc_buffer_alloc (struct alloc_buffer *buf, size_t size, size_t align)
{
if (size == 1 && align == 1)
return alloc_buffer_alloc_bytes (buf, size);
size_t current = buf->__alloc_buffer_current;
size_t aligned = roundup (current, align);
size_t new_current = aligned + size;
if (aligned >= current /* No overflow in align step. */
&& new_current >= size /* No overflow in size computation. */
&& new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
{
buf->__alloc_buffer_current = new_current;
return (void *) aligned;
}
else
{
alloc_buffer_mark_failed (buf);
return NULL;
}
}
/* Obtain a TYPE * pointer to an object in BUF of TYPE. Consume these
bytes from the buffer. Return NULL and mark the buffer as failed
if if there is not enough room in the buffer, or if the buffer has
failed before. */
#define alloc_buffer_alloc(buf, type) \
((type *) __alloc_buffer_alloc \
(buf, __alloc_buffer_assert_size (sizeof (type)), \
__alloc_buffer_assert_align (__alignof__ (type))))
/* Internal function. Obtain a pointer to an object which is
subsequently added. */
static inline const __attribute__ ((nonnull (1))) void *
__alloc_buffer_next (struct alloc_buffer *buf, size_t align)
{
if (align == 1)
return (const void *) buf->__alloc_buffer_current;
size_t current = buf->__alloc_buffer_current;
size_t aligned = roundup (current, align);
if (aligned >= current /* No overflow in align step. */
&& aligned <= buf->__alloc_buffer_end) /* Room in buffer. */
{
buf->__alloc_buffer_current = aligned;
return (const void *) aligned;
}
else
{
alloc_buffer_mark_failed (buf);
return NULL;
}
}
/* Like alloc_buffer_alloc, but do not advance the pointer beyond the
object (so a subseqent call to alloc_buffer_next or
alloc_buffer_alloc returns the same pointer). Note that the buffer
is still aligned according to the requirements of TYPE. The effect
of this function is similar to allocating a zero-length array from
the buffer. */
#define alloc_buffer_next(buf, type) \
((const type *) __alloc_buffer_next \
(buf, __alloc_buffer_assert_align (__alignof__ (type))))
/* Internal function. Allocate an array. */
void * __libc_alloc_buffer_alloc_array (struct alloc_buffer *buf,
size_t size, size_t align,
size_t count)
__attribute__ ((nonnull (1)));
/* Obtain a TYPE * pointer to an array of COUNT objects in BUF of
TYPE. Consume these bytes from the buffer. Return NULL and mark
the buffer as failed if if there is not enough room in the buffer,
or if the buffer has failed before. (Zero-length allocations from
an empty buffer which has not yet failed succeed.) */
#define alloc_buffer_alloc_array(buf, type, count) \
((type *) __libc_alloc_buffer_alloc_array \
(buf, __alloc_buffer_assert_size (sizeof (type)), \
__alloc_buffer_assert_align (__alignof__ (type)), \
count))
/* Internal function. See alloc_buffer_copy_bytes below. */
struct alloc_buffer __libc_alloc_buffer_copy_bytes (struct alloc_buffer,
const void *, size_t)
__attribute__ ((nonnull (2)));
/* Copy SIZE bytes starting at SRC into the buffer. If there is not
enough room in the buffer, the buffer is marked as failed. No
alignment of the buffer is performed. */
static inline __attribute__ ((nonnull (1, 2))) void
alloc_buffer_copy_bytes (struct alloc_buffer *buf, const void *src, size_t size)
{
*buf = __libc_alloc_buffer_copy_bytes (*buf, src, size);
}
/* Internal function. See alloc_buffer_copy_string below. */
struct alloc_buffer __libc_alloc_buffer_copy_string (struct alloc_buffer,
const char *)
__attribute__ ((nonnull (2)));
/* Copy the string at SRC into the buffer, including its null
terminator. If there is not enough room in the buffer, the buffer
is marked as failed. Return a pointer to the string. */
static inline __attribute__ ((nonnull (1, 2))) char *
alloc_buffer_copy_string (struct alloc_buffer *buf, const char *src)
{
char *result = (char *) buf->__alloc_buffer_current;
*buf = __libc_alloc_buffer_copy_string (*buf, src);
if (alloc_buffer_has_failed (buf))
result = NULL;
return result;
}
#ifndef _ISOMAC
libc_hidden_proto (__libc_alloc_buffer_alloc_array)
libc_hidden_proto (__libc_alloc_buffer_allocate)
libc_hidden_proto (__libc_alloc_buffer_copy_bytes)
libc_hidden_proto (__libc_alloc_buffer_copy_string)
libc_hidden_proto (__libc_alloc_buffer_create_failure)
#endif
#endif /* _ALLOC_BUFFER_H */
+36
View File
@@ -0,0 +1,36 @@
/* The array_length and array_end macros.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _ARRAY_LENGTH_H
#define _ARRAY_LENGTH_H
/* array_length (VAR) is the number of elements in the array VAR. VAR
must evaluate to an array, not a pointer. */
#define array_length(var) \
__extension__ ({ \
_Static_assert (!__builtin_types_compatible_p \
(__typeof (var), __typeof (&(var)[0])), \
"argument must be an array"); \
sizeof (var) / sizeof ((var)[0]); \
})
/* array_end (VAR) is a pointer one past the end of the array VAR.
VAR must evaluate to an array, not a pointer. */
#define array_end(var) (&(var)[array_length (var)])
#endif /* _ARRAY_LENGTH_H */
+1
View File
@@ -0,0 +1 @@
#include <libc-internal.h>
-3
View File
@@ -37,8 +37,6 @@ extern void res_pquery (const res_state __statp, const unsigned char *__msg,
extern int res_ourserver_p (const res_state __statp,
const struct sockaddr_in6 *__inp);
extern void __res_iclose (res_state statp, bool free_addr);
extern int __res_nopt(res_state statp, int n0, unsigned char *buf, int buflen,
int anslen);
libc_hidden_proto (__res_ninit)
libc_hidden_proto (__res_maybe_init)
libc_hidden_proto (__res_nclose)
@@ -91,7 +89,6 @@ libresolv_hidden_proto (__res_nameinquery)
libresolv_hidden_proto (__res_queriesmatch)
libresolv_hidden_proto (__res_nsend)
libresolv_hidden_proto (__b64_ntop)
libresolv_hidden_proto (__res_nopt)
libresolv_hidden_proto (__dn_count_labels)
libresolv_hidden_proto (__p_secstodate)
-7
View File
@@ -181,13 +181,6 @@ extern int __getlogin_r_loginuid (char *name, size_t namesize)
# include <dl-unistd.h>
# endif
# if IS_IN (rtld) || !defined SHARED
/* __access variant that does not set errno. Used in very early initialization
code in libc.a and ld.so. It follows access return semantics (zero for
sucess otherwise a value different than 0). */
extern __typeof (__access) __access_noerrno attribute_hidden;
# endif
__END_DECLS
# endif
+6 -2
View File
@@ -45,14 +45,18 @@ routines := htonl htons \
in6_addr getnameinfo if_index ifaddrs inet6_option \
getipv4sourcefilter setipv4sourcefilter \
getsourcefilter setsourcefilter inet6_opt inet6_rth \
inet6_scopeid_pton
inet6_scopeid_pton deadline
aux := check_pf check_native ifreq
tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
tst-gethnm test-ifaddrs bug-if1 test-inet6_opt tst-ether_line \
tst-getni1 tst-getni2 tst-inet6_rth tst-checks tst-checks-posix \
tst-sockaddr tst-inet6_scopeid_pton test-hnto-types
tst-sockaddr tst-inet6_scopeid_pton test-hnto-types tst-deadline
# tst-deadline must be linked statically so that we can access
# internal functions.
tests-static += tst-deadline
include ../Rules
+122
View File
@@ -0,0 +1,122 @@
/* Computing deadlines for timeouts.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <net-internal.h>
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
struct deadline_current_time internal_function
__deadline_current_time (void)
{
struct deadline_current_time result;
if (__clock_gettime (CLOCK_MONOTONIC, &result.current) != 0)
{
struct timeval current_tv;
if (__gettimeofday (&current_tv, NULL) == 0)
__libc_fatal ("Fatal error: gettimeofday system call failed\n");
result.current.tv_sec = current_tv.tv_sec;
result.current.tv_nsec = current_tv.tv_usec * 1000;
}
assert (result.current.tv_sec >= 0);
return result;
}
/* A special deadline value for which __deadline_is_infinite is
true. */
static inline struct deadline
infinite_deadline (void)
{
return (struct deadline) { { -1, -1 } };
}
struct deadline internal_function
__deadline_from_timeval (struct deadline_current_time current,
struct timeval tv)
{
assert (__is_timeval_valid_timeout (tv));
/* Compute second-based deadline. Perform the addition in
uintmax_t, which is unsigned, to simply overflow detection. */
uintmax_t sec = current.current.tv_sec;
sec += tv.tv_sec;
if (sec < (uintmax_t) tv.tv_sec)
return infinite_deadline ();
/* Compute nanosecond deadline. */
int nsec = current.current.tv_nsec + tv.tv_usec * 1000;
if (nsec >= 1000 * 1000 * 1000)
{
/* Carry nanosecond overflow to seconds. */
nsec -= 1000 * 1000 * 1000;
if (sec + 1 < sec)
return infinite_deadline ();
++sec;
}
/* This uses a GCC extension, otherwise these casts for detecting
overflow would not be defined. */
if ((time_t) sec < 0 || sec != (uintmax_t) (time_t) sec)
return infinite_deadline ();
return (struct deadline) { { sec, nsec } };
}
int internal_function
__deadline_to_ms (struct deadline_current_time current,
struct deadline deadline)
{
if (__deadline_is_infinite (deadline))
return INT_MAX;
if (current.current.tv_sec > deadline.absolute.tv_sec
|| (current.current.tv_sec == deadline.absolute.tv_sec
&& current.current.tv_nsec >= deadline.absolute.tv_nsec))
return 0;
time_t sec = deadline.absolute.tv_sec - current.current.tv_sec;
if (sec >= INT_MAX)
/* This value will overflow below. */
return INT_MAX;
int nsec = deadline.absolute.tv_nsec - current.current.tv_nsec;
if (nsec < 0)
{
/* Borrow from the seconds field. */
assert (sec > 0);
--sec;
nsec += 1000 * 1000 * 1000;
}
/* Prepare for rounding up to milliseconds. */
nsec += 999999;
if (nsec > 1000 * 1000 * 1000)
{
assert (sec < INT_MAX);
++sec;
nsec -= 1000 * 1000 * 1000;
}
unsigned int msec = nsec / (1000 * 1000);
if (sec > INT_MAX / 1000)
return INT_MAX;
msec += sec * 1000;
if (msec > INT_MAX)
return INT_MAX;
return msec;
}
+89
View File
@@ -20,11 +20,100 @@
#define _NET_INTERNAL_H 1
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
int __inet6_scopeid_pton (const struct in6_addr *address,
const char *scope, uint32_t *result)
internal_function attribute_hidden;
libc_hidden_proto (__inet6_scopeid_pton)
/* Deadline handling for enforcing timeouts.
Code should call __deadline_current_time to obtain the current time
and cache it locally. The cache needs updating after every
long-running or potentially blocking operation. Deadlines relative
to the current time can be computed using __deadline_from_timeval.
The deadlines may have to be recomputed in response to certain
events (such as an incoming packet), but they are absolute (not
relative to the current time). A timeout suitable for use with the
poll function can be computed from such a deadline using
__deadline_to_ms.
The fields in the structs defined belowed should only be used
within the implementation. */
/* Cache of the current time. Used to compute deadlines from relative
timeouts and vice versa. */
struct deadline_current_time
{
struct timespec current;
};
/* Return the current time. Terminates the process if the current
time is not available. */
struct deadline_current_time __deadline_current_time (void)
internal_function attribute_hidden;
/* Computed absolute deadline. */
struct deadline
{
struct timespec absolute;
};
/* For internal use only. */
static inline bool
__deadline_is_infinite (struct deadline deadline)
{
return deadline.absolute.tv_nsec < 0;
}
/* Return true if the current time is at the deadline or past it. */
static inline bool
__deadline_elapsed (struct deadline_current_time current,
struct deadline deadline)
{
return !__deadline_is_infinite (deadline)
&& (current.current.tv_sec > deadline.absolute.tv_sec
|| (current.current.tv_sec == deadline.absolute.tv_sec
&& current.current.tv_nsec >= deadline.absolute.tv_nsec));
}
/* Return the deadline which occurs first. */
static inline struct deadline
__deadline_first (struct deadline left, struct deadline right)
{
if (__deadline_is_infinite (right)
|| left.absolute.tv_sec < right.absolute.tv_sec
|| (left.absolute.tv_sec == right.absolute.tv_sec
&& left.absolute.tv_nsec < right.absolute.tv_nsec))
return left;
else
return right;
}
/* Add TV to the current time and return it. Returns a special
infinite absolute deadline on overflow. */
struct deadline __deadline_from_timeval (struct deadline_current_time,
struct timeval tv)
internal_function attribute_hidden;
/* Compute the number of milliseconds until the specified deadline,
from the current time in the argument. The result is mainly for
use with poll. If the deadline has already passed, return 0. If
the result would overflow an int, return INT_MAX. */
int __deadline_to_ms (struct deadline_current_time, struct deadline)
internal_function attribute_hidden;
/* Return true if TV.tv_sec is non-negative and TV.tv_usec is in the
interval [0, 999999]. */
static inline bool
__is_timeval_valid_timeout (struct timeval tv)
{
return tv.tv_sec >= 0 && tv.tv_usec >= 0 && tv.tv_usec < 1000 * 1000;
}
#endif /* _NET_INTERNAL_H */
+188
View File
@@ -0,0 +1,188 @@
/* Tests for computing deadlines for timeouts.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <inet/net-internal.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <support/check.h>
/* Find the maximum value which can be represented in a time_t. */
static time_t
time_t_max (void)
{
_Static_assert (0 > (time_t) -1, "time_t is signed");
uintmax_t current = 1;
while (true)
{
uintmax_t next = current * 2;
/* This cannot happen because time_t is signed. */
TEST_VERIFY_EXIT (next > current);
++next;
if ((time_t) next < 0 || next != (uintmax_t) (time_t) next)
/* Value cannot be represented in time_t. Return the previous
value. */
return current;
current = next;
}
}
static int
do_test (void)
{
{
struct deadline_current_time current_time = __deadline_current_time ();
TEST_VERIFY (current_time.current.tv_sec >= 0);
current_time = __deadline_current_time ();
/* Due to CLOCK_MONOTONIC, either seconds or nanoseconds are
greater than zero. This is also true for the gettimeofday
fallback. */
TEST_VERIFY (current_time.current.tv_sec >= 0);
TEST_VERIFY (current_time.current.tv_sec > 0
|| current_time.current.tv_nsec > 0);
}
/* Check basic computations of deadlines. */
struct deadline_current_time current_time = { { 1, 123456789 } };
struct deadline deadline = __deadline_from_timeval
(current_time, (struct timeval) { 0, 1 });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 123457789);
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1);
deadline = __deadline_from_timeval
(current_time, ((struct timeval) { 0, 2 }));
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 123458789);
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1);
deadline = __deadline_from_timeval
(current_time, ((struct timeval) { 1, 0 }));
TEST_VERIFY (deadline.absolute.tv_sec == 2);
TEST_VERIFY (deadline.absolute.tv_nsec == 123456789);
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
/* Check if timeouts are correctly rounded up to the next
millisecond. */
for (int i = 0; i < 999999; ++i)
{
++current_time.current.tv_nsec;
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
}
/* A full millisecond has elapsed, so the time to the deadline is
now less than 1000. */
++current_time.current.tv_nsec;
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999);
/* Check __deadline_to_ms carry-over. */
current_time = (struct deadline_current_time) { { 9, 123456789 } };
deadline = (struct deadline) { { 10, 122456789 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999);
deadline = (struct deadline) { { 10, 122456790 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
deadline = (struct deadline) { { 10, 123456788 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
deadline = (struct deadline) { { 10, 123456789 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
/* Check __deadline_to_ms overflow. */
deadline = (struct deadline) { { INT_MAX - 1, 1 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == INT_MAX);
/* Check __deadline_to_ms for elapsed deadlines. */
current_time = (struct deadline_current_time) { { 9, 123456789 } };
deadline.absolute = current_time.current;
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
current_time = (struct deadline_current_time) { { 9, 123456790 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
current_time = (struct deadline_current_time) { { 10, 0 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
current_time = (struct deadline_current_time) { { 10, 123456788 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
current_time = (struct deadline_current_time) { { 10, 123456789 } };
TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
/* Check carry-over in __deadline_from_timeval. */
current_time = (struct deadline_current_time) { { 9, 998000001 } };
for (int i = 0; i < 2000; ++i)
{
deadline = __deadline_from_timeval
(current_time, (struct timeval) { 1, i });
TEST_VERIFY (deadline.absolute.tv_sec == 10);
TEST_VERIFY (deadline.absolute.tv_nsec == 998000001 + i * 1000);
}
for (int i = 2000; i < 3000; ++i)
{
deadline = __deadline_from_timeval
(current_time, (struct timeval) { 2, i });
TEST_VERIFY (deadline.absolute.tv_sec == 12);
TEST_VERIFY (deadline.absolute.tv_nsec == 1 + (i - 2000) * 1000);
}
/* Check infinite deadlines. */
deadline = __deadline_from_timeval
((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1000 } },
(struct timeval) { time_t_max (), 1 });
TEST_VERIFY (__deadline_is_infinite (deadline));
deadline = __deadline_from_timeval
((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1001 } },
(struct timeval) { time_t_max (), 1 });
TEST_VERIFY (!__deadline_is_infinite (deadline));
deadline = __deadline_from_timeval
((struct deadline_current_time)
{ { time_t_max (), 1000 * 1000 * 1000 - 1000 } },
(struct timeval) { 0, 1 });
TEST_VERIFY (__deadline_is_infinite (deadline));
deadline = __deadline_from_timeval
((struct deadline_current_time)
{ { time_t_max () / 2 + 1, 0 } },
(struct timeval) { time_t_max () / 2 + 1, 0 });
TEST_VERIFY (__deadline_is_infinite (deadline));
/* Check __deadline_first behavior. */
deadline = __deadline_first
((struct deadline) { { 1, 2 } },
(struct deadline) { { 1, 3 } });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 2);
deadline = __deadline_first
((struct deadline) { { 1, 3 } },
(struct deadline) { { 1, 2 } });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 2);
deadline = __deadline_first
((struct deadline) { { 1, 2 } },
(struct deadline) { { 2, 1 } });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 2);
deadline = __deadline_first
((struct deadline) { { 1, 2 } },
(struct deadline) { { 2, 4 } });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 2);
deadline = __deadline_first
((struct deadline) { { 2, 4 } },
(struct deadline) { { 1, 2 } });
TEST_VERIFY (deadline.absolute.tv_sec == 1);
TEST_VERIFY (deadline.absolute.tv_nsec == 2);
return 0;
}
#include <support/test-driver.c>
+1 -1
View File
@@ -72,7 +72,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
tst-fts tst-fts-lfs tst-open-tmpfile
tst-fts tst-fts-lfs tst-open-tmpfile tst-getcwd-abspath
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
+1 -1
View File
@@ -193,7 +193,7 @@ FTS *__REDIRECT (fts_open, (char * const *, int,
int (*)(const FTSENT **, const FTSENT **)),
fts64_open);
FTSENT *__REDIRECT (fts_read, (FTS *), fts64_read);
int __REDIRECT (fts_set, (FTS *, FTSENT *, int), fts64_set) __THROW;
int __REDIRECT_NTH (fts_set, (FTS *, FTSENT *, int), fts64_set);
# else
# define fts_children fts64_children
# define fts_close fts64_close
+66
View File
@@ -0,0 +1,66 @@
/* BZ #22679 getcwd(3) should not succeed without returning an absolute path.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/namespace.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/test-driver.h>
#include <support/xunistd.h>
#include <unistd.h>
static char *chroot_dir;
/* The actual test. Run it in a subprocess, so that the test harness
can remove the temporary directory in --direct mode. */
static void
getcwd_callback (void *closure)
{
xchroot (chroot_dir);
errno = 0;
char *cwd = getcwd (NULL, 0);
TEST_COMPARE (errno, ENOENT);
TEST_VERIFY (cwd == NULL);
errno = 0;
cwd = realpath (".", NULL);
TEST_COMPARE (errno, ENOENT);
TEST_VERIFY (cwd == NULL);
_exit (0);
}
static int
do_test (void)
{
support_become_root ();
if (!support_can_chroot ())
return EXIT_UNSUPPORTED;
chroot_dir = support_create_temp_directory ("tst-getcwd-abspath-");
support_isolate_in_subprocess (getcwd_callback, NULL);
return 0;
}
#include <support/test-driver.c>
+2 -1
View File
@@ -61,7 +61,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
bug-memstream1 bug-wmemstream1 \
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
tst-ftell-append tst-fputws
tst-ftell-append tst-fputws tst-wfile-sync
ifeq (yes,$(build-shared))
# Add test-fopenloc only if shared library is enabled since it depends on
# shared localedata objects.
@@ -198,6 +198,7 @@ $(objpfx)tst-ungetwc1.out: $(gen-locales)
$(objpfx)tst-ungetwc2.out: $(gen-locales)
$(objpfx)tst-widetext.out: $(gen-locales)
$(objpfx)tst_wprintf2.out: $(gen-locales)
$(objpfx)tst-wfile-sync.out: $(gen-locales)
endif
$(objpfx)test-freopen.out: test-freopen.sh $(objpfx)test-freopen
+5
View File
@@ -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);
}
}
+39
View File
@@ -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>
+1
View File
@@ -0,0 +1 @@
This is a test of _IO_wfile_sync.
+16
View File
@@ -70,3 +70,19 @@ _IO_vtable_check (void)
__libc_fatal ("Fatal error: glibc detected an invalid stdio handle\n");
}
/* Some variants of libstdc++ interpose _IO_2_1_stdin_ etc. and
install their own vtables directly, without calling _IO_init or
other functions. Detect this by looking at the vtables values
during startup, and disable vtable validation in this case. */
#ifdef SHARED
__attribute__ ((constructor))
static void
check_stdfiles_vtables (void)
{
if (_IO_2_1_stdin_.vtable != &_IO_file_jumps
|| _IO_2_1_stdout_.vtable != &_IO_file_jumps
|| _IO_2_1_stderr_.vtable != &_IO_file_jumps)
IO_set_accept_foreign_vtables (&_IO_vtable_check);
}
#endif
+3 -2
View File
@@ -526,11 +526,12 @@ _IO_wfile_sync (_IO_FILE *fp)
generate the wide characters up to the current reading
position. */
int nread;
size_t wnread = (fp->_wide_data->_IO_read_ptr
- fp->_wide_data->_IO_read_base);
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
fp->_IO_read_base,
fp->_IO_read_end, delta);
fp->_IO_read_end, wnread);
fp->_IO_read_ptr = fp->_IO_read_base + nread;
delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
}
+8
View File
@@ -1,3 +1,11 @@
2017-06-11 Santhosh Thottingal <santhosh.thottingal@gmail.com>
[BZ #19922]
* locales/iso14651_t1_common: Add collation rules for U+07DA to U+07DF.
[BZ #19919]
* locales/iso14651_t1_common: Correct collation of U+0D36 and U+0D37.
2017-01-01 Joseph Myers <joseph@codesourcery.com>
* All files with FSF copyright notices: Update copyright dates
+22 -4
View File
@@ -1042,9 +1042,9 @@ collating-element <ml-bh> from "<U0D2D><U0D4D>"
collating-element <ml-m> from "<U0D2E><U0D4D>"
collating-element <ml-y> from "<U0D2F><U0D4D>"
collating-element <ml-v> from "<U0D35><U0D4D>"
collating-element <ml-s> from "<U0D38><U0D4D>"
collating-element <ml-ss> from "<U0D36><U0D4D>"
collating-element <ml-sh> from "<U0D37><U0D4D>"
collating-element <ml-s> from "<U0D38><U0D4D>"
collating-element <ml-h> from "<U0D39><U0D4D>"
collating-element <ml-zh> from "<U0D34><U0D4D>"
collating-element <ml-rr> from "<U0D31><U0D4D>"
@@ -1103,8 +1103,8 @@ collating-symbol <ml-rra>
collating-symbol <ml-la>
collating-symbol <ml-lla>
collating-symbol <ml-va>
collating-symbol <ml-sha>
collating-symbol <ml-ssa>
collating-symbol <ml-sha>
collating-symbol <ml-sa>
collating-symbol <ml-ha>
collating-symbol <ml-avagrah>
@@ -1126,6 +1126,12 @@ collating-symbol <mlvs-o>
collating-symbol <mlvs-au>
collating-symbol <ml-visarga>
collating-symbol <ml-virama>
collating-symbol <ml-atomic-chillu-k>
collating-symbol <ml-atomic-chillu-n>
collating-symbol <ml-atomic-chillu-nn>
collating-symbol <ml-atomic-chillu-l>
collating-symbol <ml-atomic-chillu-ll>
collating-symbol <ml-atomic-chillu-r>
#
# <BENGALI>
#
@@ -4552,6 +4558,12 @@ collating-symbol <TIB-subA>
<mlvs-o>
<mlvs-au>
<ml-visarga>
<ml-atomic-chillu-k>
<ml-atomic-chillu-n>
<ml-atomic-chillu-nn>
<ml-atomic-chillu-l>
<ml-atomic-chillu-ll>
<ml-atomic-chillu-r>
#
# <BENGALI>
#
@@ -7252,6 +7264,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
<U0D13> <mlvw-o>;<BAS>;<MIN>;IGNORE
<U0D14> <mlvw-au>;<BAS>;<MIN>;IGNORE
<ml-chillu-k> "<ml-ka><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D7F> "<ml-ka><ml-virama>";<ml-atomic-chillu-k>;<MIN>;IGNORE
<U0D15> "<ml-ka><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-kh> "<ml-kha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D16> "<ml-kha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
@@ -7280,6 +7293,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
<ml-dh> "<ml-dha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D22> "<ml-dha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-chillu-nn> "<ml-nna><ml-virama>";<BAS>;<MIN>;IGNORE # ണ്‍ = ണ + ് + zwj
<U0D7A> "<ml-nna><ml-virama>";<ml-atomic-chillu-nn>;<MIN>;IGNORE
<U0D23> "<ml-nna><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ണ = ണ + ് + അ
<ml-th> "<ml-tha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D24> "<ml-tha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
@@ -7290,6 +7304,7 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
<ml-ddh> "<ml-ddha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D27> "<ml-ddha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-chillu-n> "<ml-na><ml-virama>";<BAS>;<MIN>;IGNORE # ന്‍= ന + ് + zwj
<U0D7B> "<ml-na><ml-virama>";<ml-atomic-chillu-n>;<MIN>;IGNORE
<U0D28> "<ml-na><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE #ന = ന + ് + അ
<ml-p> "<ml-pa><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D2A> "<ml-pa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
@@ -7305,20 +7320,23 @@ order_start <MALAYALAM>;forward;forward;forward;forward,position
<ml-y> "<ml-ya><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D2F> "<ml-ya><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-chillu-r> "<ml-ra><ml-virama>";<BAS>;<MIN>;IGNORE # ര = ര + ് + zwj
<U0D7C> "<ml-ra><ml-virama>";<ml-atomic-chillu-r>;<MIN>;IGNORE
<U0D30> "<ml-ra><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ര = ര + ് + അ
<ml-chillu-l> <ml-la>;<BAS>;<MIN>;IGNORE # ല്‍ = ല + ് + zwj
<U0D7D> "<ml-la><ml-virama>";<ml-atomic-chillu-l>;<MIN>;IGNORE
<U0D32> "<ml-la><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ല = ല + ് + അ
<ml-v> "<ml-va><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D35> "<ml-va><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-ss> "<ml-ssa><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D37> "<ml-ssa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<U0D36> "<ml-ssa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-sh> "<ml-sha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D36> "<ml-sha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<U0D37> "<ml-sha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-s> "<ml-sa><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D38> "<ml-sa><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-h> "<ml-ha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D39> "<ml-ha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
<ml-chillu-ll> "<ml-lla><ml-virama>";<BAS>;<MIN>;IGNORE # ള്‍ = ള + ് + zwj
<U0D7E> "<ml-lla><ml-virama>";<ml-atomic-chillu-ll>;<MIN>;IGNORE
<U0D33> "<ml-lla><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE # ള = ള + ് + അ
<ml-zh> "<ml-zha><ml-virama>";<BAS>;<MIN>;IGNORE
<U0D34> "<ml-zha><ml-virama><mlvw-shorta>";<BAS>;<MIN>;IGNORE
+29 -2
View File
@@ -33,6 +33,11 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tst-mallocfork2 \
tst-interpose-nothread \
tst-interpose-thread \
tst-dynarray \
tst-dynarray-fail \
tst-dynarray-at-fail \
tst-alloc_buffer \
tst-malloc-too-large \
tests-static := \
tst-interpose-static-nothread \
@@ -45,11 +50,21 @@ tests-static += tst-malloc-usable-static-tunables
endif
tests += $(tests-static)
test-srcs = tst-mtrace
test-srcs = tst-mtrace tst-dynarray tst-dynarray-fail
routines = malloc morecore mcheck mtrace obstack \
scratch_buffer_grow scratch_buffer_grow_preserve \
scratch_buffer_set_array_size
scratch_buffer_set_array_size \
dynarray_at_failure \
dynarray_emplace_enlarge \
dynarray_finalize \
dynarray_resize \
dynarray_resize_clear \
alloc_buffer_alloc_array \
alloc_buffer_allocate \
alloc_buffer_copy_bytes \
alloc_buffer_copy_string \
alloc_buffer_create_failure \
install-lib := libmcheck.a
non-lib.a := libmcheck.a
@@ -135,6 +150,8 @@ ifeq ($(run-built-tests),yes)
ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += $(objpfx)tst-mtrace.out
tests-special += $(objpfx)tst-dynarray-mem.out
tests-special += $(objpfx)tst-dynarray-fail-mem.out
endif
endif
endif
@@ -206,3 +223,13 @@ $(objpfx)tst-interpose-thread: \
$(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o
$(objpfx)tst-interpose-static-thread: \
$(objpfx)tst-interpose-aux-thread.o $(static-thread-library)
tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace
$(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \
$(evaluate-test)
tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace
$(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \
$(evaluate-test)
+14
View File
@@ -72,5 +72,19 @@ libc {
__libc_scratch_buffer_grow;
__libc_scratch_buffer_grow_preserve;
__libc_scratch_buffer_set_array_size;
# dynarray support
__libc_dynarray_at_failure;
__libc_dynarray_emplace_enlarge;
__libc_dynarray_finalize;
__libc_dynarray_resize;
__libc_dynarray_resize_clear;
# struct alloc_buffer support
__libc_alloc_buffer_alloc_array;
__libc_alloc_buffer_allocate;
__libc_alloc_buffer_copy_bytes;
__libc_alloc_buffer_copy_string;
__libc_alloc_buffer_create_failure;
}
}
+47
View File
@@ -0,0 +1,47 @@
/* Array allocation from a fixed-size buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <alloc_buffer.h>
#include <malloc-internal.h>
#include <libc-pointer-arith.h>
void *
__libc_alloc_buffer_alloc_array (struct alloc_buffer *buf, size_t element_size,
size_t align, size_t count)
{
size_t current = buf->__alloc_buffer_current;
/* The caller asserts that align is a power of two. */
size_t aligned = ALIGN_UP (current, align);
size_t size;
bool overflow = check_mul_overflow_size_t (element_size, count, &size);
size_t new_current = aligned + size;
if (!overflow /* Multiplication did not overflow. */
&& aligned >= current /* No overflow in align step. */
&& new_current >= size /* No overflow in size computation. */
&& new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
{
buf->__alloc_buffer_current = new_current;
return (void *) aligned;
}
else
{
alloc_buffer_mark_failed (buf);
return NULL;
}
}
libc_hidden_def (__libc_alloc_buffer_alloc_array)
+36
View File
@@ -0,0 +1,36 @@
/* Allocate a fixed-size allocation buffer using malloc.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <alloc_buffer.h>
#include <stdlib.h>
struct alloc_buffer
__libc_alloc_buffer_allocate (size_t size, void **pptr)
{
*pptr = malloc (size);
if (*pptr == NULL)
return (struct alloc_buffer)
{
.__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
.__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
};
else
return alloc_buffer_create (*pptr, size);
}
libc_hidden_def (__libc_alloc_buffer_allocate)
+34
View File
@@ -0,0 +1,34 @@
/* Copy an array of bytes into the buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <alloc_buffer.h>
#include <string.h>
/* This function works on a copy of the buffer object, so that it can
remain non-addressable in the caller. */
struct alloc_buffer
__libc_alloc_buffer_copy_bytes (struct alloc_buffer buf,
const void *src, size_t len)
{
void *ptr = alloc_buffer_alloc_bytes (&buf, len);
if (ptr != NULL)
memcpy (ptr, src, len);
return buf;
}
libc_hidden_def (__libc_alloc_buffer_copy_bytes)
+30
View File
@@ -0,0 +1,30 @@
/* Copy a string into the allocation buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <alloc_buffer.h>
#include <string.h>
/* This function works on a copy of the buffer object, so that it can
remain non-addressable in the caller. */
struct alloc_buffer
__libc_alloc_buffer_copy_string (struct alloc_buffer buf, const char *src)
{
return __libc_alloc_buffer_copy_bytes (buf, src, strlen (src) + 1);
}
libc_hidden_def (__libc_alloc_buffer_copy_string)
+31
View File
@@ -0,0 +1,31 @@
/* Terminate the process as the result of an invalid allocation buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <alloc_buffer.h>
#include <stdio.h>
void
__libc_alloc_buffer_create_failure (void *start, size_t size)
{
char buf[200];
__snprintf (buf, sizeof (buf), "Fatal glibc error: "
"invalid allocation buffer of size %zu\n",
size);
__libc_fatal (buf);
}
libc_hidden_def (__libc_alloc_buffer_create_failure)
+4 -4
View File
@@ -212,9 +212,9 @@ __malloc_fork_unlock_child (void)
#if HAVE_TUNABLES
static inline int do_set_mallopt_check (int32_t value);
void
DL_TUNABLE_CALLBACK (set_mallopt_check) (void *valp)
DL_TUNABLE_CALLBACK (set_mallopt_check) (tunable_val_t *valp)
{
int32_t value = *(int32_t *) valp;
int32_t value = (int32_t) valp->numval;
do_set_mallopt_check (value);
if (check_action != 0)
__malloc_check_init ();
@@ -223,9 +223,9 @@ DL_TUNABLE_CALLBACK (set_mallopt_check) (void *valp)
# define DL_TUNABLE_CALLBACK_FNDECL(__name, __type) \
static inline int do_ ## __name (__type value); \
void \
DL_TUNABLE_CALLBACK (__name) (void *valp) \
DL_TUNABLE_CALLBACK (__name) (tunable_val_t *valp) \
{ \
__type value = *(__type *) valp; \
__type value = (__type) (valp)->numval; \
do_ ## __name (value); \
}
+521
View File
@@ -0,0 +1,521 @@
/* Type-safe arrays which grow dynamically.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Pre-processor macros which act as parameters:
DYNARRAY_STRUCT
The struct tag of dynamic array to be defined.
DYNARRAY_ELEMENT
The type name of the element type. Elements are copied
as if by memcpy, and can change address as the dynamic
array grows.
DYNARRAY_PREFIX
The prefix of the functions which are defined.
The following parameters are optional:
DYNARRAY_ELEMENT_FREE
DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
contents of elements. E is of type DYNARRAY_ELEMENT *.
DYNARRAY_ELEMENT_INIT
DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
element. E is of type DYNARRAY_ELEMENT *.
If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
defined, new elements are automatically zero-initialized.
Otherwise, new elements have undefined contents.
DYNARRAY_INITIAL_SIZE
The size of the statically allocated array (default:
at least 2, more elements if they fit into 128 bytes).
Must be a preprocessor constant. If DYNARRAY_INITIAL_SIZE is 0,
there is no statically allocated array at, and all non-empty
arrays are heap-allocated.
DYNARRAY_FINAL_TYPE
The name of the type which holds the final array. If not
defined, is PREFIX##finalize not provided. DYNARRAY_FINAL_TYPE
must be a struct type, with members of type DYNARRAY_ELEMENT and
size_t at the start (in this order).
These macros are undefined after this header file has been
included.
The following types are provided (their members are private to the
dynarray implementation):
struct DYNARRAY_STRUCT
The following functions are provided:
void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *);
void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *);
bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *);
void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *);
size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *);
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *);
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *);
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t);
void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT);
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *);
bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t);
void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *);
void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *);
The following functions are provided are provided if the
prerequisites are met:
bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
DYNARRAY_FINAL_TYPE *);
(if DYNARRAY_FINAL_TYPE is defined)
DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
size_t *);
(if DYNARRAY_FINAL_TYPE is not defined)
*/
#include <malloc/dynarray.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifndef DYNARRAY_STRUCT
# error "DYNARRAY_STRUCT must be defined"
#endif
#ifndef DYNARRAY_ELEMENT
# error "DYNARRAY_ELEMENT must be defined"
#endif
#ifndef DYNARRAY_PREFIX
# error "DYNARRAY_PREFIX must be defined"
#endif
#ifdef DYNARRAY_INITIAL_SIZE
# if DYNARRAY_INITIAL_SIZE < 0
# error "DYNARRAY_INITIAL_SIZE must be non-negative"
# endif
# if DYNARRAY_INITIAL_SIZE > 0
# define DYNARRAY_HAVE_SCRATCH 1
# else
# define DYNARRAY_HAVE_SCRATCH 0
# endif
#else
/* Provide a reasonable default which limits the size of
DYNARRAY_STRUCT. */
# define DYNARRAY_INITIAL_SIZE \
(sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT))
# define DYNARRAY_HAVE_SCRATCH 1
#endif
/* Public type definitions. */
/* All fields of this struct are private to the implementation. */
struct DYNARRAY_STRUCT
{
union
{
struct dynarray_header dynarray_abstract;
struct
{
/* These fields must match struct dynarray_header. */
size_t used;
size_t allocated;
DYNARRAY_ELEMENT *array;
} dynarray_header;
};
#if DYNARRAY_HAVE_SCRATCH
/* Initial inline allocation. */
DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE];
#endif
};
/* Internal use only: Helper macros. */
/* Ensure macro-expansion of DYNARRAY_PREFIX. */
#define DYNARRAY_CONCAT0(prefix, name) prefix##name
#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name)
#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name)
/* Address of the scratch buffer if any. */
#if DYNARRAY_HAVE_SCRATCH
# define DYNARRAY_SCRATCH(list) (list)->scratch
#else
# define DYNARRAY_SCRATCH(list) NULL
#endif
/* Internal use only: Helper functions. */
/* Internal function. Call DYNARRAY_ELEMENT_FREE with the array
elements. Name mangling needed due to the DYNARRAY_ELEMENT_FREE
macro expansion. */
static inline void
DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array,
size_t __dynarray_used)
{
#ifdef DYNARRAY_ELEMENT_FREE
for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i)
DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]);
#endif /* DYNARRAY_ELEMENT_FREE */
}
/* Internal function. Free the non-scratch array allocation. */
static inline void
DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list)
{
#if DYNARRAY_HAVE_SCRATCH
if (list->dynarray_header.array != list->scratch)
free (list->dynarray_header.array);
#else
free (list->dynarray_header.array);
#endif
}
/* Public functions. */
/* Initialize a dynamic array object. This must be called before any
use of the object. */
__attribute__ ((nonnull (1)))
static void
DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list)
{
list->dynarray_header.used = 0;
list->dynarray_header.allocated = DYNARRAY_INITIAL_SIZE;
list->dynarray_header.array = DYNARRAY_SCRATCH (list);
}
/* Deallocate the dynamic array and its elements. */
__attribute__ ((unused, nonnull (1)))
static void
DYNARRAY_NAME (free) (struct DYNARRAY_STRUCT *list)
{
DYNARRAY_NAME (free__elements__)
(list->dynarray_header.array, list->dynarray_header.used);
DYNARRAY_NAME (free__array__) (list);
DYNARRAY_NAME (init) (list);
}
/* Return true if the dynamic array is in an error state. */
__attribute__ ((nonnull (1)))
static inline bool
DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list)
{
return list->dynarray_header.allocated == __dynarray_error_marker ();
}
/* Mark the dynamic array as failed. All elements are deallocated as
a side effect. */
__attribute__ ((nonnull (1)))
static void
DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list)
{
DYNARRAY_NAME (free__elements__)
(list->dynarray_header.array, list->dynarray_header.used);
DYNARRAY_NAME (free__array__) (list);
list->dynarray_header.array = DYNARRAY_SCRATCH (list);
list->dynarray_header.used = 0;
list->dynarray_header.allocated = __dynarray_error_marker ();
}
/* Return the number of elements which have been added to the dynamic
array. */
__attribute__ ((nonnull (1)))
static inline size_t
DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list)
{
return list->dynarray_header.used;
}
/* Return a pointer to the array element at INDEX. Terminate the
process if INDEX is out of bounds. */
__attribute__ ((nonnull (1)))
static inline DYNARRAY_ELEMENT *
DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index)
{
if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list)))
__libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index);
return list->dynarray_header.array + index;
}
/* Return a pointer to the first array element, if any. For a
zero-length array, the pointer can be NULL even though the dynamic
array has not entered the failure state. */
__attribute__ ((nonnull (1)))
static inline DYNARRAY_ELEMENT *
DYNARRAY_NAME (begin) (struct DYNARRAY_STRUCT *list)
{
return list->dynarray_header.array;
}
/* Return a pointer one element past the last array element. For a
zero-length array, the pointer can be NULL even though the dynamic
array has not entered the failure state. */
__attribute__ ((nonnull (1)))
static inline DYNARRAY_ELEMENT *
DYNARRAY_NAME (end) (struct DYNARRAY_STRUCT *list)
{
return list->dynarray_header.array + list->dynarray_header.used;
}
/* Internal function. Slow path for the add function below. */
static void
DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
{
if (__glibc_unlikely
(!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT))))
{
DYNARRAY_NAME (mark_failed) (list);
return;
}
/* Copy the new element and increase the array length. */
list->dynarray_header.array[list->dynarray_header.used++] = item;
}
/* Add ITEM at the end of the array, enlarging it by one element.
Mark *LIST as failed if the dynamic array allocation size cannot be
increased. */
__attribute__ ((unused, nonnull (1)))
static inline void
DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
{
/* Do nothing in case of previous error. */
if (DYNARRAY_NAME (has_failed) (list))
return;
/* Enlarge the array if necessary. */
if (__glibc_unlikely (list->dynarray_header.used
== list->dynarray_header.allocated))
{
DYNARRAY_NAME (add__) (list, item);
return;
}
/* Copy the new element and increase the array length. */
list->dynarray_header.array[list->dynarray_header.used++] = item;
}
/* Internal function. Building block for the emplace functions below.
Assumes space for one more element in *LIST. */
static inline DYNARRAY_ELEMENT *
DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list)
{
DYNARRAY_ELEMENT *result
= &list->dynarray_header.array[list->dynarray_header.used];
++list->dynarray_header.used;
#if defined (DYNARRAY_ELEMENT_INIT)
DYNARRAY_ELEMENT_INIT (result);
#elif defined (DYNARRAY_ELEMENT_FREE)
memset (result, 0, sizeof (*result));
#endif
return result;
}
/* Internal function. Slow path for the emplace function below. */
static DYNARRAY_ELEMENT *
DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list)
{
if (__glibc_unlikely
(!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT))))
{
DYNARRAY_NAME (mark_failed) (list);
return NULL;
}
return DYNARRAY_NAME (emplace__tail__) (list);
}
/* Allocate a place for a new element in *LIST and return a pointer to
it. The pointer can be NULL if the dynamic array cannot be
enlarged due to a memory allocation failure. */
__attribute__ ((unused, warn_unused_result, nonnull (1)))
static
/* Avoid inlining with the larger initialization code. */
#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE))
inline
#endif
DYNARRAY_ELEMENT *
DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list)
{
/* Do nothing in case of previous error. */
if (DYNARRAY_NAME (has_failed) (list))
return NULL;
/* Enlarge the array if necessary. */
if (__glibc_unlikely (list->dynarray_header.used
== list->dynarray_header.allocated))
return (DYNARRAY_NAME (emplace__) (list));
return DYNARRAY_NAME (emplace__tail__) (list);
}
/* Change the size of *LIST to SIZE. If SIZE is larger than the
existing size, new elements are added (which can be initialized).
Otherwise, the list is truncated, and elements are freed. Return
false on memory allocation failure (and mark *LIST as failed). */
__attribute__ ((unused, nonnull (1)))
static bool
DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size)
{
if (size > list->dynarray_header.used)
{
bool ok;
#if defined (DYNARRAY_ELEMENT_INIT)
/* The new elements have to be initialized. */
size_t old_size = list->dynarray_header.used;
ok = __libc_dynarray_resize (&list->dynarray_abstract,
size, DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT));
if (ok)
for (size_t i = old_size; i < size; ++i)
{
DYNARRAY_ELEMENT_INIT (&list->dynarray_header.array[i]);
}
#elif defined (DYNARRAY_ELEMENT_FREE)
/* Zero initialization is needed so that the elements can be
safely freed. */
ok = __libc_dynarray_resize_clear
(&list->dynarray_abstract, size,
DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT));
#else
ok = __libc_dynarray_resize (&list->dynarray_abstract,
size, DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT));
#endif
if (__glibc_unlikely (!ok))
DYNARRAY_NAME (mark_failed) (list);
return ok;
}
else
{
/* The list has shrunk in size. Free the removed elements. */
DYNARRAY_NAME (free__elements__)
(list->dynarray_header.array + size,
list->dynarray_header.used - size);
list->dynarray_header.used = size;
return true;
}
}
/* Remove the last element of LIST if it is present. */
__attribute__ ((unused, nonnull (1)))
static void
DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list)
{
/* used > 0 implies that the array is the non-failed state. */
if (list->dynarray_header.used > 0)
{
size_t new_length = list->dynarray_header.used - 1;
#ifdef DYNARRAY_ELEMENT_FREE
DYNARRAY_ELEMENT_FREE (&list->dynarray_header.array[new_length]);
#endif
list->dynarray_header.used = new_length;
}
}
/* Remove all elements from the list. The elements are freed, but the
list itself is not. */
__attribute__ ((unused, nonnull (1)))
static void
DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list)
{
/* free__elements__ does nothing if the list is in the failed
state. */
DYNARRAY_NAME (free__elements__)
(list->dynarray_header.array, list->dynarray_header.used);
list->dynarray_header.used = 0;
}
#ifdef DYNARRAY_FINAL_TYPE
/* Transfer the dynamic array to a permanent location at *RESULT.
Returns true on success on false on allocation failure. In either
case, *LIST is re-initialized and can be reused. A NULL pointer is
stored in *RESULT if LIST refers to an empty list. On success, the
pointer in *RESULT is heap-allocated and must be deallocated using
free. */
__attribute__ ((unused, warn_unused_result, nonnull (1, 2)))
static bool
DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list,
DYNARRAY_FINAL_TYPE *result)
{
struct dynarray_finalize_result res;
if (__libc_dynarray_finalize (&list->dynarray_abstract,
DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT), &res))
{
/* On success, the result owns all the data. */
DYNARRAY_NAME (init) (list);
*result = (DYNARRAY_FINAL_TYPE) { res.array, res.length };
return true;
}
else
{
/* On error, we need to free all data. */
DYNARRAY_NAME (free) (list);
errno = ENOMEM;
return false;
}
}
#else /* !DYNARRAY_FINAL_TYPE */
/* Transfer the dynamic array to a heap-allocated array and return a
pointer to it. The pointer is NULL if memory allocation fails, or
if the array is empty, so this function should be used only for
arrays which are known not be empty (usually because they always
have a sentinel at the end). If LENGTHP is not NULL, the array
length is written to *LENGTHP. *LIST is re-initialized and can be
reused. */
__attribute__ ((unused, warn_unused_result, nonnull (1)))
static DYNARRAY_ELEMENT *
DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp)
{
struct dynarray_finalize_result res;
if (__libc_dynarray_finalize (&list->dynarray_abstract,
DYNARRAY_SCRATCH (list),
sizeof (DYNARRAY_ELEMENT), &res))
{
/* On success, the result owns all the data. */
DYNARRAY_NAME (init) (list);
if (lengthp != NULL)
*lengthp = res.length;
return res.array;
}
else
{
/* On error, we need to free all data. */
DYNARRAY_NAME (free) (list);
errno = ENOMEM;
return NULL;
}
}
#endif /* !DYNARRAY_FINAL_TYPE */
/* Undo macro definitions. */
#undef DYNARRAY_CONCAT0
#undef DYNARRAY_CONCAT1
#undef DYNARRAY_NAME
#undef DYNARRAY_SCRATCH
#undef DYNARRAY_HAVE_SCRATCH
#undef DYNARRAY_STRUCT
#undef DYNARRAY_ELEMENT
#undef DYNARRAY_PREFIX
#undef DYNARRAY_ELEMENT_FREE
#undef DYNARRAY_ELEMENT_INIT
#undef DYNARRAY_INITIAL_SIZE
#undef DYNARRAY_FINAL_TYPE
+176
View File
@@ -0,0 +1,176 @@
/* Type-safe arrays which grow dynamically. Shared definitions.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* To use the dynarray facility, you need to include
<malloc/dynarray-skeleton.c> and define the parameter macros
documented in that file.
A minimal example which provides a growing list of integers can be
defined like this:
struct int_array
{
// Pointer to result array followed by its length,
// as required by DYNARRAY_FINAL_TYPE.
int *array;
size_t length;
};
#define DYNARRAY_STRUCT dynarray_int
#define DYNARRAY_ELEMENT int
#define DYNARRAY_PREFIX dynarray_int_
#define DYNARRAY_FINAL_TYPE struct int_array
#include <malloc/dynarray-skeleton.c>
To create a three-element array with elements 1, 2, 3, use this
code:
struct dynarray_int dyn;
dynarray_int_init (&dyn);
for (int i = 1; i <= 3; ++i)
{
int *place = dynarray_int_emplace (&dyn);
assert (place != NULL);
*place = i;
}
struct int_array result;
bool ok = dynarray_int_finalize (&dyn, &result);
assert (ok);
assert (result.length == 3);
assert (result.array[0] == 1);
assert (result.array[1] == 2);
assert (result.array[2] == 3);
free (result.array);
If the elements contain resources which must be freed, define
DYNARRAY_ELEMENT_FREE appropriately, like this:
struct str_array
{
char **array;
size_t length;
};
#define DYNARRAY_STRUCT dynarray_str
#define DYNARRAY_ELEMENT char *
#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
#define DYNARRAY_PREFIX dynarray_str_
#define DYNARRAY_FINAL_TYPE struct str_array
#include <malloc/dynarray-skeleton.c>
Compared to scratch buffers, dynamic arrays have the following
features:
- They have an element type, and are not just an untyped buffer of
bytes.
- When growing, previously stored elements are preserved. (It is
expected that scratch_buffer_grow_preserve and
scratch_buffer_set_array_size eventually go away because all
current users are moved to dynamic arrays.)
- Scratch buffers have a more aggressive growth policy because
growing them typically means a retry of an operation (across an
NSS service module boundary), which is expensive.
- For the same reason, scratch buffers have a much larger initial
stack allocation. */
#ifndef _DYNARRAY_H
#define _DYNARRAY_H
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
struct dynarray_header
{
size_t used;
size_t allocated;
void *array;
};
/* Marker used in the allocated member to indicate that an error was
encountered. */
static inline size_t
__dynarray_error_marker (void)
{
return -1;
}
/* Internal function. See the has_failed function in
dynarray-skeleton.c. */
static inline bool
__dynarray_error (struct dynarray_header *list)
{
return list->allocated == __dynarray_error_marker ();
}
/* Internal function. Enlarge the dynamically allocated area of the
array to make room for one more element. SCRATCH is a pointer to
the scratch area (which is not heap-allocated and must not be
freed). ELEMENT_SIZE is the size, in bytes, of one element.
Return false on failure, true on success. */
bool __libc_dynarray_emplace_enlarge (struct dynarray_header *,
void *scratch, size_t element_size);
libc_hidden_proto (__libc_dynarray_emplace_enlarge)
/* Internal function. Enlarge the dynamically allocated area of the
array to make room for at least SIZE elements (which must be larger
than the existing used part of the dynamic array). SCRATCH is a
pointer to the scratch area (which is not heap-allocated and must
not be freed). ELEMENT_SIZE is the size, in bytes, of one element.
Return false on failure, true on success. */
bool __libc_dynarray_resize (struct dynarray_header *, size_t size,
void *scratch, size_t element_size);
libc_hidden_proto (__libc_dynarray_resize)
/* Internal function. Like __libc_dynarray_resize, but clear the new
part of the dynamic array. */
bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size,
void *scratch, size_t element_size);
libc_hidden_proto (__libc_dynarray_resize_clear)
/* Internal type. */
struct dynarray_finalize_result
{
void *array;
size_t length;
};
/* Internal function. Copy the dynamically-allocated area to an
explicitly-sized heap allocation. SCRATCH is a pointer to the
embedded scratch space. ELEMENT_SIZE is the size, in bytes, of the
element type. On success, true is returned, and pointer and length
are written to *RESULT. On failure, false is returned. The caller
has to take care of some of the memory management; this function is
expected to be called from dynarray-skeleton.c. */
bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
size_t element_size,
struct dynarray_finalize_result *result);
libc_hidden_proto (__libc_dynarray_finalize)
/* Internal function. Terminate the process after an index error.
SIZE is the number of elements of the dynamic array. INDEX is the
lookup index which triggered the failure. */
void __libc_dynarray_at_failure (size_t size, size_t index)
__attribute__ ((noreturn));
libc_hidden_proto (__libc_dynarray_at_failure)
#endif /* _DYNARRAY_H */
+31
View File
@@ -0,0 +1,31 @@
/* Report an dynamic array index out of bounds condition.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dynarray.h>
#include <stdio.h>
void
__libc_dynarray_at_failure (size_t size, size_t index)
{
char buf[200];
__snprintf (buf, sizeof (buf), "Fatal glibc error: "
"array index %zu not less than array length %zu\n",
index, size);
__libc_fatal (buf);
}
libc_hidden_def (__libc_dynarray_at_failure)
+73
View File
@@ -0,0 +1,73 @@
/* Increase the size of a dynamic array in preparation of an emplace operation.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dynarray.h>
#include <errno.h>
#include <malloc-internal.h>
#include <stdlib.h>
#include <string.h>
bool
__libc_dynarray_emplace_enlarge (struct dynarray_header *list,
void *scratch, size_t element_size)
{
size_t new_allocated;
if (list->allocated == 0)
{
/* No scratch buffer provided. Choose a reasonable default
size. */
if (element_size < 4)
new_allocated = 16;
else if (element_size < 8)
new_allocated = 8;
else
new_allocated = 4;
}
else
/* Increase the allocated size, using an exponential growth
policy. */
{
new_allocated = list->allocated + list->allocated / 2 + 1;
if (new_allocated <= list->allocated)
{
/* Overflow. */
__set_errno (ENOMEM);
return false;
}
}
size_t new_size;
if (check_mul_overflow_size_t (new_allocated, element_size, &new_size))
return false;
void *new_array;
if (list->array == scratch)
{
/* The previous array was not heap-allocated. */
new_array = malloc (new_size);
if (new_array != NULL && list->array != NULL)
memcpy (new_array, list->array, list->used * element_size);
}
else
new_array = realloc (list->array, new_size);
if (new_array == NULL)
return false;
list->array = new_array;
list->allocated = new_allocated;
return true;
}
libc_hidden_def (__libc_dynarray_emplace_enlarge)
+62
View File
@@ -0,0 +1,62 @@
/* Copy the dynamically-allocated area to an explicitly-sized heap allocation.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dynarray.h>
#include <stdlib.h>
#include <string.h>
bool
__libc_dynarray_finalize (struct dynarray_header *list,
void *scratch, size_t element_size,
struct dynarray_finalize_result *result)
{
if (__dynarray_error (list))
/* The caller will reported the deferred error. */
return false;
size_t used = list->used;
/* Empty list. */
if (used == 0)
{
/* An empty list could still be backed by a heap-allocated
array. Free it if necessary. */
if (list->array != scratch)
free (list->array);
*result = (struct dynarray_finalize_result) { NULL, 0 };
return true;
}
size_t allocation_size = used * element_size;
void *heap_array = malloc (allocation_size);
if (heap_array != NULL)
{
/* The new array takes ownership of the strings. */
if (list->array != NULL)
memcpy (heap_array, list->array, allocation_size);
if (list->array != scratch)
free (list->array);
*result = (struct dynarray_finalize_result)
{ .array = heap_array, .length = used };
return true;
}
else
/* The caller will perform the freeing operation. */
return false;
}
libc_hidden_def (__libc_dynarray_finalize)
+64
View File
@@ -0,0 +1,64 @@
/* Increase the size of a dynamic array.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dynarray.h>
#include <errno.h>
#include <malloc-internal.h>
#include <stdlib.h>
#include <string.h>
bool
__libc_dynarray_resize (struct dynarray_header *list, size_t size,
void *scratch, size_t element_size)
{
/* The existing allocation provides sufficient room. */
if (size <= list->allocated)
{
list->used = size;
return true;
}
/* Otherwise, use size as the new allocation size. The caller is
expected to provide the final size of the array, so there is no
over-allocation here. */
size_t new_size_bytes;
if (check_mul_overflow_size_t (size, element_size, &new_size_bytes))
{
/* Overflow. */
__set_errno (ENOMEM);
return false;
}
void *new_array;
if (list->array == scratch)
{
/* The previous array was not heap-allocated. */
new_array = malloc (new_size_bytes);
if (new_array != NULL && list->array != NULL)
memcpy (new_array, list->array, list->used * element_size);
}
else
new_array = realloc (list->array, new_size_bytes);
if (new_array == NULL)
return false;
list->array = new_array;
list->allocated = size;
list->used = size;
return true;
}
libc_hidden_def (__libc_dynarray_resize)
+35
View File
@@ -0,0 +1,35 @@
/* Increase the size of a dynamic array and clear the new part.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dynarray.h>
#include <stdlib.h>
#include <string.h>
bool
__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size,
void *scratch, size_t element_size)
{
size_t old_size = list->used;
if (!__libc_dynarray_resize (list, size, scratch, element_size))
return false;
/* __libc_dynarray_resize already checked for overflow. */
memset (list->array + (old_size * element_size), 0,
(size - old_size) * element_size);
return true;
}
libc_hidden_def (__libc_dynarray_resize_clear)
+19
View File
@@ -81,5 +81,24 @@ void __malloc_fork_unlock_parent (void) internal_function attribute_hidden;
/* Called in the child process after a fork. */
void __malloc_fork_unlock_child (void) internal_function attribute_hidden;
/* Set *RESULT to LEFT * RIGHT. Return true if the multiplication
overflowed. */
static inline bool
check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
{
#if __GNUC__ >= 5
return __builtin_mul_overflow (left, right, result);
#else
/* size_t is unsigned so the behavior on overflow is defined. */
*result = left * right;
size_t half_size_t = ((size_t) 1) << (8 * sizeof (size_t) / 2);
if (__glibc_unlikely ((left | right) >= half_size_t))
{
if (__glibc_unlikely (right != 0 && *result / right != left))
return true;
}
return false;
#endif
}
#endif /* _MALLOC_INTERNAL_H */
+24 -51
View File
@@ -1202,14 +1202,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* Same, except also perform argument check */
#define checked_request2size(req, sz) \
if (REQUEST_OUT_OF_RANGE (req)) { \
__set_errno (ENOMEM); \
return 0; \
} \
(sz) = request2size (req);
/* Same, except also perform an argument and result check. First, we check
that the padding done by request2size didn't result in an integer
overflow. Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
size isn't so large that a later alignment would lead to another integer
overflow. */
#define checked_request2size(req, sz) \
({ \
(sz) = request2size (req); \
if (((sz) < (req)) \
|| REQUEST_OUT_OF_RANGE (sz)) \
{ \
__set_errno (ENOMEM); \
return 0; \
} \
})
/*
--------------- Physical chunk operations ---------------
@@ -4243,11 +4250,6 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
mchunkptr bck; /* misc temp for linking */
mchunkptr fwd; /* misc temp for linking */
unsigned long copysize; /* bytes to copy */
unsigned int ncopies; /* INTERNAL_SIZE_T words to copy */
INTERNAL_SIZE_T* s; /* copy source */
INTERNAL_SIZE_T* d; /* copy destination */
const char *errstr = NULL;
/* oldmem size */
@@ -4325,43 +4327,7 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
}
else
{
/*
Unroll copy of <= 36 bytes (72 if 8byte sizes)
We know that contents have an odd number of
INTERNAL_SIZE_T-sized words; minimally 3.
*/
copysize = oldsize - SIZE_SZ;
s = (INTERNAL_SIZE_T *) (chunk2mem (oldp));
d = (INTERNAL_SIZE_T *) (newmem);
ncopies = copysize / sizeof (INTERNAL_SIZE_T);
assert (ncopies >= 3);
if (ncopies > 9)
memcpy (d, s, copysize);
else
{
*(d + 0) = *(s + 0);
*(d + 1) = *(s + 1);
*(d + 2) = *(s + 2);
if (ncopies > 4)
{
*(d + 3) = *(s + 3);
*(d + 4) = *(s + 4);
if (ncopies > 6)
{
*(d + 5) = *(s + 5);
*(d + 6) = *(s + 6);
if (ncopies > 8)
{
*(d + 7) = *(s + 7);
*(d + 8) = *(s + 8);
}
}
}
}
memcpy (newmem, chunk2mem (oldp), oldsize - SIZE_SZ);
_int_free (av, oldp, 1);
check_inuse_chunk (av, newp);
return chunk2mem (newp);
@@ -4423,6 +4389,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
*/
/* Check for overflow. */
if (nb > SIZE_MAX - alignment - MINSIZE)
{
__set_errno (ENOMEM);
return 0;
}
/* Call malloc with worst case padding to hit alignment. */
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
@@ -4902,7 +4875,7 @@ __libc_mallopt (int param_number, int value)
case M_ARENA_MAX:
if (value > 0)
do_set_arena_test (value);
do_set_arena_max (value);
break;
}
__libc_lock_unlock (av->mutex);
+665
View File
@@ -0,0 +1,665 @@
/* Tests for struct alloc_buffer.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <arpa/inet.h>
#include <alloc_buffer.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/support.h>
#include <support/test-driver.h>
/* Return true if PTR is sufficiently aligned for TYPE. */
#define IS_ALIGNED(ptr, type) \
((((uintptr_t) ptr) & (__alloc_buffer_assert_align (__alignof (type)) - 1)) \
== 0)
/* Structure with non-power-of-two size. */
struct twelve
{
uint32_t buffer[3] __attribute__ ((aligned (4)));
};
_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
_Static_assert (__alignof__ (struct twelve) == 4, "struct twelve");
/* Check for success obtaining empty arrays. Does not assume the
buffer is empty. */
static void
test_empty_array (struct alloc_buffer refbuf)
{
bool refbuf_failed = alloc_buffer_has_failed (&refbuf);
if (test_verbose)
printf ("info: %s: current=0x%llx end=0x%llx refbuf_failed=%d\n",
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
(unsigned long long) refbuf.__alloc_buffer_end, refbuf_failed);
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY ((alloc_buffer_alloc_bytes (&buf, 0) == NULL)
== refbuf_failed);
TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY ((alloc_buffer_alloc_array (&buf, char, 0) == NULL)
== refbuf_failed);
TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
}
/* The following tests can fail due to the need for aligning the
returned pointer. */
{
struct alloc_buffer buf = refbuf;
bool expect_failure = refbuf_failed
|| !IS_ALIGNED (alloc_buffer_next (&buf, void), double);
double *ptr = alloc_buffer_alloc_array (&buf, double, 0);
TEST_VERIFY (IS_ALIGNED (ptr, double));
TEST_VERIFY ((ptr == NULL) == expect_failure);
TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
}
{
struct alloc_buffer buf = refbuf;
bool expect_failure = refbuf_failed
|| !IS_ALIGNED (alloc_buffer_next (&buf, void), struct twelve);
struct twelve *ptr = alloc_buffer_alloc_array (&buf, struct twelve, 0);
TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
TEST_VERIFY ((ptr == NULL) == expect_failure);
TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
}
}
/* Test allocation of impossibly large arrays. */
static void
test_impossible_array (struct alloc_buffer refbuf)
{
if (test_verbose)
printf ("info: %s: current=0x%llx end=0x%llx\n",
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
(unsigned long long) refbuf.__alloc_buffer_end);
static const size_t counts[] =
{ SIZE_MAX, SIZE_MAX - 1, SIZE_MAX - 2, SIZE_MAX - 3, SIZE_MAX - 4,
SIZE_MAX / 2, SIZE_MAX / 2 + 1, SIZE_MAX / 2 - 1, 0};
for (int i = 0; counts[i] != 0; ++i)
{
size_t count = counts[i];
if (test_verbose)
printf ("info: %s: count=%zu\n", __func__, count);
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
== NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
}
}
/* Check for failure to obtain anything from a failed buffer. */
static void
test_after_failure (struct alloc_buffer refbuf)
{
if (test_verbose)
printf ("info: %s: current=0x%llx end=0x%llx\n",
__func__, (unsigned long long) refbuf.__alloc_buffer_current,
(unsigned long long) refbuf.__alloc_buffer_end);
TEST_VERIFY (alloc_buffer_has_failed (&refbuf));
{
struct alloc_buffer buf = refbuf;
alloc_buffer_add_byte (&buf, 17);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
test_impossible_array (refbuf);
for (int count = 0; count <= 4; ++count)
{
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
== NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
}
}
static void
test_empty (struct alloc_buffer refbuf)
{
TEST_VERIFY (alloc_buffer_size (&refbuf) == 0);
if (alloc_buffer_next (&refbuf, void) != NULL)
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
test_empty_array (refbuf);
test_impossible_array (refbuf);
/* Failure to obtain non-empty objects. */
{
struct alloc_buffer buf = refbuf;
alloc_buffer_add_byte (&buf, 17);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, 1) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
test_after_failure (buf);
}
}
static void
test_size_1 (struct alloc_buffer refbuf)
{
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
TEST_VERIFY (alloc_buffer_size (&refbuf) == 1);
test_empty_array (refbuf);
test_impossible_array (refbuf);
/* Success adding a single byte. */
{
struct alloc_buffer buf = refbuf;
alloc_buffer_add_byte (&buf, 17);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x11", 1) == 0);
{
struct alloc_buffer buf = refbuf;
signed char *ptr = alloc_buffer_alloc (&buf, signed char);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = 126;
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\176", 1) == 0);
{
struct alloc_buffer buf = refbuf;
char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = (char) 253;
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\xfd", 1) == 0);
/* Failure with larger objects. */
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, short) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, 1) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
test_after_failure (buf);
}
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
test_after_failure (buf);
}
}
static void
test_size_2 (struct alloc_buffer refbuf)
{
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
TEST_VERIFY (alloc_buffer_size (&refbuf) == 2);
TEST_VERIFY (IS_ALIGNED (alloc_buffer_next (&refbuf, void), short));
test_empty_array (refbuf);
test_impossible_array (refbuf);
/* Success adding two bytes. */
{
struct alloc_buffer buf = refbuf;
alloc_buffer_add_byte (&buf, '@');
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
test_size_1 (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "@\xfd", 2) == 0);
{
struct alloc_buffer buf = refbuf;
signed char *ptr = alloc_buffer_alloc (&buf, signed char);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = 'A';
test_size_1 (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "A\xfd", 2) == 0);
{
struct alloc_buffer buf = refbuf;
char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = 'B';
test_size_1 (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "B\xfd", 2) == 0);
{
struct alloc_buffer buf = refbuf;
unsigned short *ptr = alloc_buffer_alloc (&buf, unsigned short);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = htons (0x12f4);
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x12\xf4", 2) == 0);
{
struct alloc_buffer buf = refbuf;
unsigned short *ptr = alloc_buffer_alloc_array (&buf, unsigned short, 1);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
*ptr = htons (0x13f5);
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x13\xf5", 2) == 0);
{
struct alloc_buffer buf = refbuf;
char *ptr = alloc_buffer_alloc_array (&buf, char, 2);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
memcpy (ptr, "12", 2);
test_empty (buf);
}
TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "12", 2) == 0);
}
static void
test_misaligned (char pad)
{
enum { SIZE = 23 };
char *backing = xmalloc (SIZE + 2);
backing[0] = ~pad;
backing[SIZE + 1] = pad;
struct alloc_buffer refbuf = alloc_buffer_create (backing + 1, SIZE);
{
struct alloc_buffer buf = refbuf;
short *ptr = alloc_buffer_alloc_array (&buf, short, SIZE / sizeof (short));
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, short));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
for (int i = 0; i < SIZE / sizeof (short); ++i)
ptr[i] = htons (0xff01 + i);
TEST_VERIFY (memcmp (ptr,
"\xff\x01\xff\x02\xff\x03\xff\x04"
"\xff\x05\xff\x06\xff\x07\xff\x08"
"\xff\x09\xff\x0a\xff\x0b", 22) == 0);
}
{
struct alloc_buffer buf = refbuf;
uint32_t *ptr = alloc_buffer_alloc_array
(&buf, uint32_t, SIZE / sizeof (uint32_t));
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
for (int i = 0; i < SIZE / sizeof (uint32_t); ++i)
ptr[i] = htonl (0xf1e2d301 + i);
TEST_VERIFY (memcmp (ptr,
"\xf1\xe2\xd3\x01\xf1\xe2\xd3\x02"
"\xf1\xe2\xd3\x03\xf1\xe2\xd3\x04"
"\xf1\xe2\xd3\x05", 20) == 0);
}
{
struct alloc_buffer buf = refbuf;
struct twelve *ptr = alloc_buffer_alloc (&buf, struct twelve);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
ptr->buffer[0] = htonl (0x11223344);
ptr->buffer[1] = htonl (0x55667788);
ptr->buffer[2] = htonl (0x99aabbcc);
TEST_VERIFY (memcmp (ptr,
"\x11\x22\x33\x44"
"\x55\x66\x77\x88"
"\x99\xaa\xbb\xcc", 12) == 0);
}
{
static const double nums[] = { 1, 2 };
struct alloc_buffer buf = refbuf;
double *ptr = alloc_buffer_alloc_array (&buf, double, 2);
TEST_VERIFY_EXIT (ptr != NULL);
TEST_VERIFY (IS_ALIGNED (ptr, double));
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
ptr[0] = nums[0];
ptr[1] = nums[1];
TEST_VERIFY (memcmp (ptr, nums, sizeof (nums)) == 0);
}
/* Verify that padding was not overwritten. */
TEST_VERIFY (backing[0] == ~pad);
TEST_VERIFY (backing[SIZE + 1] == pad);
free (backing);
}
/* Check that overflow during alignment is handled properly. */
static void
test_large_misaligned (void)
{
uintptr_t minus1 = -1;
uintptr_t start = minus1 & ~0xfe;
struct alloc_buffer refbuf = alloc_buffer_create ((void *) start, 16);
TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
struct __attribute__ ((aligned (256))) align256
{
int dymmy;
};
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc (&buf, struct align256) == NULL);
test_after_failure (buf);
}
for (int count = 0; count < 3; ++count)
{
struct alloc_buffer buf = refbuf;
TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct align256, count)
== NULL);
test_after_failure (buf);
}
}
/* Check behavior of large allocations. */
static void
test_large (void)
{
{
/* Allocation which wraps around. */
struct alloc_buffer buf = { 1, SIZE_MAX };
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, SIZE_MAX) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
/* Successful very large allocation. */
struct alloc_buffer buf = { 1, SIZE_MAX };
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
(&buf, char, SIZE_MAX - 1);
TEST_VERIFY (val == 1);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
test_empty (buf);
}
{
typedef char __attribute__ ((aligned (2))) char2;
/* Overflow in array size computation. */
struct alloc_buffer buf = { 1, SIZE_MAX };
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, SIZE_MAX - 1) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
/* Successful allocation after alignment. */
buf = (struct alloc_buffer) { 1, SIZE_MAX };
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
(&buf, char2, SIZE_MAX - 2);
TEST_VERIFY (val == 2);
test_empty (buf);
/* Alignment behavior near the top of the address space. */
buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
TEST_VERIFY (alloc_buffer_next (&buf, char2) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, 0) == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
}
{
typedef short __attribute__ ((aligned (2))) short2;
/* Test overflow in size computation. */
struct alloc_buffer buf = { 1, SIZE_MAX };
TEST_VERIFY (alloc_buffer_alloc_array (&buf, short2, SIZE_MAX / 2)
== NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
/* A slightly smaller array fits within the allocation. */
buf = (struct alloc_buffer) { 2, SIZE_MAX - 1 };
uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
(&buf, short2, SIZE_MAX / 2 - 1);
TEST_VERIFY (val == 2);
test_empty (buf);
}
}
static void
test_copy_bytes (void)
{
char backing[4];
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
alloc_buffer_copy_bytes (&buf, "1", 1);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 3);
TEST_VERIFY (memcmp (backing, "1@@@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
alloc_buffer_copy_bytes (&buf, "12", 3);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 1);
TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
alloc_buffer_copy_bytes (&buf, "1234", 4);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 0);
TEST_VERIFY (memcmp (backing, "1234", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
alloc_buffer_copy_bytes (&buf, "1234", 5);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
alloc_buffer_copy_bytes (&buf, "1234", -1);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
}
}
static void
test_copy_string (void)
{
char backing[4];
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
const char *p = alloc_buffer_copy_string (&buf, "");
TEST_VERIFY (p == backing);
TEST_VERIFY (strcmp (p, "") == 0);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 3);
TEST_VERIFY (memcmp (backing, "\0@@@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
const char *p = alloc_buffer_copy_string (&buf, "1");
TEST_VERIFY (p == backing);
TEST_VERIFY (strcmp (p, "1") == 0);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 2);
TEST_VERIFY (memcmp (backing, "1\0@@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
const char *p = alloc_buffer_copy_string (&buf, "12");
TEST_VERIFY (p == backing);
TEST_VERIFY (strcmp (p, "12") == 0);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 1);
TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
const char *p = alloc_buffer_copy_string (&buf, "123");
TEST_VERIFY (p == backing);
TEST_VERIFY (strcmp (p, "123") == 0);
TEST_VERIFY (!alloc_buffer_has_failed (&buf));
TEST_VERIFY (alloc_buffer_size (&buf) == 0);
TEST_VERIFY (memcmp (backing, "123", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
TEST_VERIFY (alloc_buffer_copy_string (&buf, "1234") == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
}
{
memset (backing, '@', sizeof (backing));
struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
TEST_VERIFY (alloc_buffer_copy_string (&buf, "12345") == NULL);
TEST_VERIFY (alloc_buffer_has_failed (&buf));
TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
}
}
static int
do_test (void)
{
test_empty (alloc_buffer_create (NULL, 0));
test_empty (alloc_buffer_create ((char *) "", 0));
test_empty (alloc_buffer_create ((void *) 1, 0));
{
void *ptr = (void *) ""; /* Cannot be freed. */
struct alloc_buffer buf = alloc_buffer_allocate (1, &ptr);
test_size_1 (buf);
free (ptr); /* Should have been overwritten. */
}
{
void *ptr= (void *) ""; /* Cannot be freed. */
struct alloc_buffer buf = alloc_buffer_allocate (2, &ptr);
test_size_2 (buf);
free (ptr); /* Should have been overwritten. */
}
test_misaligned (0);
test_misaligned (0xc7);
test_misaligned (0xff);
test_large_misaligned ();
test_large ();
test_copy_bytes ();
test_copy_string ();
return 0;
}
#include <support/test-driver.c>
+125
View File
@@ -0,0 +1,125 @@
/* Test reporting of out-of-bounds access for dynamic arrays.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include "tst-dynarray-shared.h"
#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
/* Run CALLBACK and check that the data on standard error equals
EXPECTED. */
static void
check (const char *test, void (*callback) (void *), size_t index,
const char *expected)
{
struct support_capture_subprocess result
= support_capture_subprocess (callback, &index);
if (strcmp (result.err.buffer, expected) != 0)
{
support_record_failure ();
printf ("error: test %s (%zu) unexpected standard error data\n"
" expected: %s\n"
" actual: %s\n",
test, index, expected, result.err.buffer);
}
TEST_VERIFY (strlen (result.out.buffer) == 0);
TEST_VERIFY (WIFSIGNALED (result.status));
if (WIFSIGNALED (result.status))
TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
support_capture_subprocess_free (&result);
}
/* Try indexing an empty array. */
static void
test_empty (void *closure)
{
size_t *pindex = closure;
struct dynarray_int dyn;
dynarray_int_init (&dyn);
dynarray_int_at (&dyn, *pindex);
}
/* Try indexing a one-element array. */
static void
test_one (void *closure)
{
size_t *pindex = closure;
struct dynarray_int dyn;
dynarray_int_init (&dyn);
TEST_VERIFY (dynarray_int_resize (&dyn, 1));
dynarray_int_at (&dyn, *pindex);
}
/* Try indexing a longer array. */
static void
test_many (void *closure)
{
size_t *pindex = closure;
struct dynarray_int dyn;
dynarray_int_init (&dyn);
TEST_VERIFY (dynarray_int_resize (&dyn, 5371));
dynarray_int_at (&dyn, *pindex);
}
/* (size_t) -1 for use in string literals. */
#if SIZE_WIDTH == 32
# define MINUS_1 "4294967295"
#elif SIZE_WIDTH == 64
# define MINUS_1 "18446744073709551615"
#else
# error "unknown value for SIZE_WIDTH"
#endif
static int
do_test (void)
{
TEST_VERIFY (setenv ("LIBC_FATAL_STDERR_", "1", 1) == 0);
check ("test_empty", test_empty, 0,
"Fatal glibc error: array index 0 not less than array length 0\n");
check ("test_empty", test_empty, 1,
"Fatal glibc error: array index 1 not less than array length 0\n");
check ("test_empty", test_empty, -1,
"Fatal glibc error: array index " MINUS_1
" not less than array length 0\n");
check ("test_one", test_one, 1,
"Fatal glibc error: array index 1 not less than array length 1\n");
check ("test_one", test_one, 2,
"Fatal glibc error: array index 2 not less than array length 1\n");
check ("test_one", test_one, -1,
"Fatal glibc error: array index " MINUS_1
" not less than array length 1\n");
check ("test_many", test_many, 5371,
"Fatal glibc error: array index 5371"
" not less than array length 5371\n");
check ("test_many", test_many, 5372,
"Fatal glibc error: array index 5372"
" not less than array length 5371\n");
check ("test_many", test_many, -1,
"Fatal glibc error: array index " MINUS_1
" not less than array length 5371\n");
return 0;
}
#include <support/test-driver.c>
+418
View File
@@ -0,0 +1,418 @@
/* Test allocation failures with dynamic arrays.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* This test is separate from tst-dynarray because it cannot run under
valgrind. */
#include "tst-dynarray-shared.h"
#include <mcheck.h>
#include <stdio.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xunistd.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <unistd.h>
/* Data structure to fill up the heap. */
struct heap_filler
{
struct heap_filler *next;
};
/* Allocate objects until the heap is full. */
static struct heap_filler *
fill_heap (void)
{
size_t pad = 4096;
struct heap_filler *head = NULL;
while (true)
{
struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
if (new_head == NULL)
{
if (pad > 0)
{
/* Try again with smaller allocations. */
pad = 0;
continue;
}
else
break;
}
new_head->next = head;
head = new_head;
}
return head;
}
/* Free the heap-filling allocations, so that we can continue testing
and detect memory leaks elsewhere. */
static void
free_fill_heap (struct heap_filler *head)
{
while (head != NULL)
{
struct heap_filler *next = head->next;
free (head);
head = next;
}
}
/* Check allocation failures for int arrays (without an element free
function). */
static void
test_int_fail (void)
{
/* Exercise failure in add/emplace.
do_add: Use emplace (false) or add (true) to add elements.
do_finalize: Perform finalization at the end (instead of free). */
for (int do_add = 0; do_add < 2; ++do_add)
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
{
struct dynarray_int dyn;
dynarray_int_init (&dyn);
size_t count = 0;
while (true)
{
if (do_add)
{
dynarray_int_add (&dyn, 0);
if (dynarray_int_has_failed (&dyn))
break;
}
else
{
int *place = dynarray_int_emplace (&dyn);
if (place == NULL)
break;
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
*place = 0;
}
++count;
}
printf ("info: %s: failure after %zu elements\n", __func__, count);
TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
if (do_finalize)
{
struct int_array result = { (int *) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
TEST_VERIFY_EXIT (result.length == (size_t) -1);
}
else
dynarray_int_free (&dyn);
CHECK_INIT_STATE (int, &dyn);
}
/* Exercise failure in finalize. */
for (int do_add = 0; do_add < 2; ++do_add)
{
struct dynarray_int dyn;
dynarray_int_init (&dyn);
for (unsigned int i = 0; i < 10000; ++i)
{
if (do_add)
{
dynarray_int_add (&dyn, i);
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
}
else
{
int *place = dynarray_int_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
*place = i;
}
}
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
struct heap_filler *heap_filler = fill_heap ();
struct int_array result = { (int *) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
TEST_VERIFY_EXIT (result.length == (size_t) -1);
CHECK_INIT_STATE (int, &dyn);
free_fill_heap (heap_filler);
}
/* Exercise failure in resize. */
{
struct dynarray_int dyn;
dynarray_int_init (&dyn);
struct heap_filler *heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
TEST_VERIFY (dynarray_int_has_failed (&dyn));
free_fill_heap (heap_filler);
dynarray_int_init (&dyn);
TEST_VERIFY (dynarray_int_resize (&dyn, 1));
heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
TEST_VERIFY (dynarray_int_has_failed (&dyn));
free_fill_heap (heap_filler);
dynarray_int_init (&dyn);
TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
TEST_VERIFY (dynarray_int_has_failed (&dyn));
free_fill_heap (heap_filler);
}
}
/* Check allocation failures for char * arrays (which automatically
free the pointed-to strings). */
static void
test_str_fail (void)
{
/* Exercise failure in add/emplace.
do_add: Use emplace (false) or add (true) to add elements.
do_finalize: Perform finalization at the end (instead of free). */
for (int do_add = 0; do_add < 2; ++do_add)
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
{
struct dynarray_str dyn;
dynarray_str_init (&dyn);
size_t count = 0;
while (true)
{
char **place;
if (do_add)
{
dynarray_str_add (&dyn, NULL);
if (dynarray_str_has_failed (&dyn))
break;
else
place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
}
else
{
place = dynarray_str_emplace (&dyn);
if (place == NULL)
break;
}
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
TEST_VERIFY_EXIT (*place == NULL);
*place = strdup ("placeholder");
if (*place == NULL)
{
/* Second loop to wait for failure of
dynarray_str_emplace. */
while (true)
{
if (do_add)
{
dynarray_str_add (&dyn, NULL);
if (dynarray_str_has_failed (&dyn))
break;
}
else
{
char **place = dynarray_str_emplace (&dyn);
if (place == NULL)
break;
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
*place = NULL;
}
++count;
}
break;
}
++count;
}
printf ("info: %s: failure after %zu elements\n", __func__, count);
TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
if (do_finalize)
{
struct str_array result = { (char **) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
TEST_VERIFY_EXIT (result.length == (size_t) -1);
}
else
dynarray_str_free (&dyn);
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
}
/* Exercise failure in finalize. */
for (int do_add = 0; do_add < 2; ++do_add)
{
struct dynarray_str dyn;
dynarray_str_init (&dyn);
for (unsigned int i = 0; i < 1000; ++i)
{
if (do_add)
dynarray_str_add (&dyn, xstrdup ("placeholder"));
else
{
char **place = dynarray_str_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
TEST_VERIFY_EXIT (*place == NULL);
*place = xstrdup ("placeholder");
}
}
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
struct heap_filler *heap_filler = fill_heap ();
struct str_array result = { (char **) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
TEST_VERIFY_EXIT (result.length == (size_t) -1);
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
free_fill_heap (heap_filler);
}
/* Exercise failure in resize. */
{
struct dynarray_str dyn;
dynarray_str_init (&dyn);
struct heap_filler *heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
TEST_VERIFY (dynarray_str_has_failed (&dyn));
free_fill_heap (heap_filler);
dynarray_str_init (&dyn);
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
TEST_VERIFY (dynarray_str_has_failed (&dyn));
free_fill_heap (heap_filler);
dynarray_str_init (&dyn);
TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
heap_filler = fill_heap ();
TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
TEST_VERIFY (dynarray_str_has_failed (&dyn));
free_fill_heap (heap_filler);
}
}
/* Test if mmap can allocate a page. This is necessary because
setrlimit does not fail even if it reduces the RLIMIT_AS limit
below what is currently needed by the process. */
static bool
mmap_works (void)
{
void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED)
return false;
xmunmap (ptr, 1);
return true;
}
/* Set the RLIMIT_AS limit to the value in *LIMIT. */
static void
xsetrlimit_as (const struct rlimit *limit)
{
if (setrlimit (RLIMIT_AS, limit) != 0)
FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
(unsigned long) limit->rlim_cur);
}
/* Approximately this many bytes can be allocated after
reduce_rlimit_as has run. */
enum { as_limit_reserve = 2 * 1024 * 1024 };
/* Limit the size of the process, so that memory allocation in
allocate_thread will eventually fail, without impacting the entire
system. By default, a dynamic limit which leaves room for 2 MiB is
activated. The TEST_RLIMIT_AS environment variable overrides
it. */
static void
reduce_rlimit_as (void)
{
struct rlimit limit;
if (getrlimit (RLIMIT_AS, &limit) != 0)
FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
/* Use the TEST_RLIMIT_AS setting if available. */
{
long target = 0;
const char *variable = "TEST_RLIMIT_AS";
const char *target_str = getenv (variable);
if (target_str != NULL)
{
target = atoi (target_str);
if (target <= 0)
FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
target *= 1024 * 1024; /* Convert to megabytes. */
limit.rlim_cur = target;
xsetrlimit_as (&limit);
return;
}
}
/* Otherwise, try to find the limit with a binary search. */
unsigned long low = 1 << 20;
limit.rlim_cur = low;
xsetrlimit_as (&limit);
/* Find working upper limit. */
unsigned long high = 1 << 30;
while (true)
{
limit.rlim_cur = high;
xsetrlimit_as (&limit);
if (mmap_works ())
break;
if (2 * high < high)
FAIL_EXIT1 ("cannot find upper AS limit");
high *= 2;
}
/* Perform binary search. */
while ((high - low) > 128 * 1024)
{
unsigned long middle = (low + high) / 2;
limit.rlim_cur = middle;
xsetrlimit_as (&limit);
if (mmap_works ())
high = middle;
else
low = middle;
}
unsigned long target = high + as_limit_reserve;
limit.rlim_cur = target;
xsetrlimit_as (&limit);
printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
}
static int
do_test (void)
{
mtrace ();
reduce_rlimit_as ();
test_int_fail ();
test_str_fail ();
return 0;
}
#define TIMEOUT 90
#include <support/test-driver.c>
+80
View File
@@ -0,0 +1,80 @@
/* Shared definitions for dynarray tests.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <stddef.h>
struct int_array
{
int *array;
size_t length;
};
#define DYNARRAY_STRUCT dynarray_int
#define DYNARRAY_ELEMENT int
#define DYNARRAY_PREFIX dynarray_int_
#define DYNARRAY_FINAL_TYPE struct int_array
#include <malloc/dynarray-skeleton.c>
struct str_array
{
char **array;
size_t length;
};
#define DYNARRAY_STRUCT dynarray_str
#define DYNARRAY_ELEMENT char *
#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
#define DYNARRAY_PREFIX dynarray_str_
#define DYNARRAY_FINAL_TYPE struct str_array
#include <malloc/dynarray-skeleton.c>
/* Check that *DYN is equivalent to its initial state. */
#define CHECK_INIT_STATE(type, dyn) \
({ \
TEST_VERIFY_EXIT (!dynarray_##type##_has_failed (dyn)); \
TEST_VERIFY_EXIT (dynarray_##type##_size (dyn) == 0); \
TEST_VERIFY_EXIT ((dyn)->dynarray_header.array \
== (dyn)->scratch); \
TEST_VERIFY_EXIT ((dyn)->dynarray_header.allocated > 0); \
(void) 0; \
})
/* Check that *DYN behaves as if it is in its initial state. */
#define CHECK_EMPTY(type, dyn) \
({ \
CHECK_INIT_STATE (type, (dyn)); \
dynarray_##type##_free (dyn); \
CHECK_INIT_STATE (type, (dyn)); \
dynarray_##type##_clear (dyn); \
CHECK_INIT_STATE (type, (dyn)); \
dynarray_##type##_remove_last (dyn); \
CHECK_INIT_STATE (type, (dyn)); \
dynarray_##type##_mark_failed (dyn); \
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
dynarray_##type##_clear (dyn); \
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
dynarray_##type##_remove_last (dyn); \
TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
TEST_VERIFY_EXIT (dynarray_##type##_emplace (dyn) == NULL); \
dynarray_##type##_free (dyn); \
CHECK_INIT_STATE (type, (dyn)); \
/* These functions should not assert. */ \
dynarray_##type##_begin (dyn); \
dynarray_##type##_end (dyn); \
(void) 0; \
})
+574
View File
@@ -0,0 +1,574 @@
/* Test for dynamic arrays.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include "tst-dynarray-shared.h"
#include <errno.h>
#include <stdint.h>
#define DYNARRAY_STRUCT dynarray_long
#define DYNARRAY_ELEMENT long
#define DYNARRAY_PREFIX dynarray_long_
#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 17)
#include <malloc/dynarray-skeleton.c>
struct long_array
{
long *array;
size_t length;
};
#define DYNARRAY_STRUCT dynarray_long_noscratch
#define DYNARRAY_ELEMENT long
#define DYNARRAY_PREFIX dynarray_long_noscratch_
#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 23)
#define DYNARRAY_FINAL_TYPE struct long_array
#define DYNARRAY_INITIAL_SIZE 0
#include <malloc/dynarray-skeleton.c>
#define DYNARRAY_STRUCT zstr
#define DYNARRAY_ELEMENT char
#define DYNARRAY_PREFIX zstr_
#define DYNARRAY_INITIAL_SIZE 128
#include <malloc/dynarray-skeleton.c>
#include <malloc.h>
#include <mcheck.h>
#include <stdint.h>
#include <support/check.h>
#include <support/support.h>
enum { max_count = 20 };
/* Test dynamic arrays with int elements (no automatic deallocation
for elements). */
static void
test_int (void)
{
/* Empty array. */
{
struct dynarray_int dyn;
dynarray_int_init (&dyn);
CHECK_EMPTY (int, &dyn);
}
/* Empty array with finalization. */
{
struct dynarray_int dyn;
dynarray_int_init (&dyn);
CHECK_INIT_STATE (int, &dyn);
struct int_array result = { (int *) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
CHECK_INIT_STATE (int, &dyn);
TEST_VERIFY_EXIT (result.array == NULL);
TEST_VERIFY_EXIT (result.length == 0);
}
/* Non-empty array tests.
do_add: Switch between emplace (false) and add (true).
do_finalize: Perform finalize call at the end.
do_clear: Perform clear call at the end.
do_remove_last: Perform remove_last call after adding elements.
count: Number of elements added to the array. */
for (int do_add = 0; do_add < 2; ++do_add)
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
for (int do_clear = 0; do_clear < 2; ++do_clear)
for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
for (unsigned int count = 0; count < max_count; ++count)
{
if (do_remove_last && count == 0)
continue;
unsigned int base = count * count;
struct dynarray_int dyn;
dynarray_int_init (&dyn);
for (unsigned int i = 0; i < count; ++i)
{
if (do_add)
dynarray_int_add (&dyn, base + i);
else
{
int *place = dynarray_int_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
*place = base + i;
}
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == i + 1);
TEST_VERIFY_EXIT (dynarray_int_size (&dyn)
<= dyn.dynarray_header.allocated);
}
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == count);
TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
if (count > 0)
{
TEST_VERIFY (dynarray_int_begin (&dyn)
== dynarray_int_at (&dyn, 0));
TEST_VERIFY (dynarray_int_end (&dyn)
== dynarray_int_at (&dyn, count - 1) + 1);
}
unsigned final_count;
bool heap_array = dyn.dynarray_header.array != dyn.scratch;
if (do_remove_last)
{
dynarray_int_remove_last (&dyn);
if (count == 0)
final_count = 0;
else
final_count = count - 1;
}
else
final_count = count;
if (final_count > 0)
{
TEST_VERIFY (dynarray_int_begin (&dyn)
== dynarray_int_at (&dyn, 0));
TEST_VERIFY (dynarray_int_end (&dyn)
== dynarray_int_at (&dyn, final_count - 1) + 1);
}
if (do_clear)
{
dynarray_int_clear (&dyn);
final_count = 0;
}
TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
== heap_array);
TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == final_count);
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
if (!do_clear)
for (unsigned int i = 0; i < final_count; ++i)
TEST_VERIFY_EXIT (*dynarray_int_at (&dyn, i) == base + i);
if (do_finalize)
{
struct int_array result = { (int *) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
CHECK_INIT_STATE (int, &dyn);
TEST_VERIFY_EXIT (result.length == final_count);
if (final_count == 0)
TEST_VERIFY_EXIT (result.array == NULL);
else
{
TEST_VERIFY_EXIT (result.array != NULL);
TEST_VERIFY_EXIT (result.array != (int *) (uintptr_t) -1);
TEST_VERIFY_EXIT
(malloc_usable_size (result.array)
>= final_count * sizeof (result.array[0]));
for (unsigned int i = 0; i < final_count; ++i)
TEST_VERIFY_EXIT (result.array[i] == base + i);
free (result.array);
}
}
else /* !do_finalize */
{
dynarray_int_free (&dyn);
CHECK_INIT_STATE (int, &dyn);
}
}
}
/* Test dynamic arrays with char * elements (with automatic
deallocation of the pointed-to strings). */
static void
test_str (void)
{
/* Empty array. */
{
struct dynarray_str dyn;
dynarray_str_init (&dyn);
CHECK_EMPTY (str, &dyn);
}
/* Empty array with finalization. */
{
struct dynarray_str dyn;
dynarray_str_init (&dyn);
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
struct str_array result = { (char **) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
CHECK_INIT_STATE (str, &dyn);
TEST_VERIFY_EXIT (result.array == NULL);
TEST_VERIFY_EXIT (result.length == 0);
}
/* Non-empty array tests.
do_add: Switch between emplace (false) and add (true).
do_finalize: Perform finalize call at the end.
do_clear: Perform clear call at the end.
do_remove_last: Perform remove_last call after adding elements.
count: Number of elements added to the array. */
for (int do_add = 0; do_add < 2; ++do_add)
for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
for (int do_clear = 0; do_clear < 2; ++do_clear)
for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
for (unsigned int count = 0; count < max_count; ++count)
{
if (do_remove_last && count == 0)
continue;
unsigned int base = count * count;
struct dynarray_str dyn;
dynarray_str_init (&dyn);
for (unsigned int i = 0; i < count; ++i)
{
char *item = xasprintf ("%d", base + i);
if (do_add)
dynarray_str_add (&dyn, item);
else
{
char **place = dynarray_str_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
TEST_VERIFY_EXIT (*place == NULL);
*place = item;
}
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == i + 1);
TEST_VERIFY_EXIT (dynarray_str_size (&dyn)
<= dyn.dynarray_header.allocated);
}
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == count);
TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
if (count > 0)
{
TEST_VERIFY (dynarray_str_begin (&dyn)
== dynarray_str_at (&dyn, 0));
TEST_VERIFY (dynarray_str_end (&dyn)
== dynarray_str_at (&dyn, count - 1) + 1);
}
unsigned final_count;
bool heap_array = dyn.dynarray_header.array != dyn.scratch;
if (do_remove_last)
{
dynarray_str_remove_last (&dyn);
if (count == 0)
final_count = 0;
else
final_count = count - 1;
}
else
final_count = count;
if (final_count > 0)
{
TEST_VERIFY (dynarray_str_begin (&dyn)
== dynarray_str_at (&dyn, 0));
TEST_VERIFY (dynarray_str_end (&dyn)
== dynarray_str_at (&dyn, final_count - 1) + 1);
}
if (do_clear)
{
dynarray_str_clear (&dyn);
final_count = 0;
}
TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
== heap_array);
TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == final_count);
TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
if (!do_clear)
for (unsigned int i = 0; i < count - do_remove_last; ++i)
{
char *expected = xasprintf ("%d", base + i);
const char *actual = *dynarray_str_at (&dyn, i);
TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
free (expected);
}
if (do_finalize)
{
struct str_array result = { (char **) (uintptr_t) -1, -1 };
TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
CHECK_INIT_STATE (str, &dyn);
TEST_VERIFY_EXIT (result.length == final_count);
if (final_count == 0)
TEST_VERIFY_EXIT (result.array == NULL);
else
{
TEST_VERIFY_EXIT (result.array != NULL);
TEST_VERIFY_EXIT (result.array
!= (char **) (uintptr_t) -1);
TEST_VERIFY_EXIT (result.length
== count - do_remove_last);
TEST_VERIFY_EXIT
(malloc_usable_size (result.array)
>= final_count * sizeof (result.array[0]));
for (unsigned int i = 0; i < count - do_remove_last; ++i)
{
char *expected = xasprintf ("%d", base + i);
char *actual = result.array[i];
TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
free (expected);
free (actual);
}
free (result.array);
}
}
else /* !do_finalize */
{
dynarray_str_free (&dyn);
CHECK_INIT_STATE (str, &dyn);
}
}
/* Test resizing. */
{
enum { count = 2131 };
struct dynarray_str dyn;
dynarray_str_init (&dyn);
/* From length 0 to length 1. */
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
TEST_VERIFY (dynarray_str_size (&dyn) == 1);
TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
dynarray_str_free (&dyn);
/* From length 0 to length 1 and 2. */
TEST_VERIFY (dynarray_str_resize (&dyn, 1));
TEST_VERIFY (dynarray_str_size (&dyn) == 1);
TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
*dynarray_str_at (&dyn, 0) = xstrdup ("allocated0");
TEST_VERIFY (dynarray_str_resize (&dyn, 2));
TEST_VERIFY (dynarray_str_size (&dyn) == 2);
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
TEST_VERIFY (*dynarray_str_at (&dyn, 1) == NULL);
*dynarray_str_at (&dyn, 1) = xstrdup ("allocated1");
TEST_VERIFY (dynarray_str_resize (&dyn, count));
TEST_VERIFY (dynarray_str_size (&dyn) == count);
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
for (int i = 2; i < count; ++i)
TEST_VERIFY (*dynarray_str_at (&dyn, i) == NULL);
*dynarray_str_at (&dyn, count - 1) = xstrdup ("allocated2");
TEST_VERIFY (dynarray_str_resize (&dyn, 3));
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
TEST_VERIFY (*dynarray_str_at (&dyn, 2) == NULL);
dynarray_str_free (&dyn);
}
}
/* Verify that DYNARRAY_ELEMENT_INIT has an effect. */
static void
test_long_init (void)
{
enum { count = 2131 };
{
struct dynarray_long dyn;
dynarray_long_init (&dyn);
for (int i = 0; i < count; ++i)
{
long *place = dynarray_long_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
TEST_VERIFY (*place == 17);
}
TEST_VERIFY (dynarray_long_size (&dyn) == count);
for (int i = 0; i < count; ++i)
TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
dynarray_long_free (&dyn);
TEST_VERIFY (dynarray_long_resize (&dyn, 1));
TEST_VERIFY (dynarray_long_size (&dyn) == 1);
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
*dynarray_long_at (&dyn, 0) = 18;
dynarray_long_free (&dyn);
TEST_VERIFY (dynarray_long_resize (&dyn, 1));
TEST_VERIFY (dynarray_long_size (&dyn) == 1);
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
TEST_VERIFY (dynarray_long_resize (&dyn, 2));
TEST_VERIFY (dynarray_long_size (&dyn) == 2);
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
TEST_VERIFY (*dynarray_long_at (&dyn, 1) == 17);
*dynarray_long_at (&dyn, 0) = 18;
TEST_VERIFY (dynarray_long_resize (&dyn, count));
TEST_VERIFY (dynarray_long_size (&dyn) == count);
TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 18);
for (int i = 1; i < count; ++i)
TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
dynarray_long_free (&dyn);
}
/* Similar, but without an on-stack scratch region
(DYNARRAY_INITIAL_SIZE is 0). */
{
struct dynarray_long_noscratch dyn;
dynarray_long_noscratch_init (&dyn);
struct long_array result;
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
TEST_VERIFY (result.array == NULL);
TEST_VERIFY (result.length == 0);
/* Test with one element. */
{
long *place = dynarray_long_noscratch_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
TEST_VERIFY (*place == 23);
}
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array != NULL);
TEST_VERIFY (result.length == 1);
TEST_VERIFY (result.array[0] == 23);
free (result.array);
for (int i = 0; i < count; ++i)
{
long *place = dynarray_long_noscratch_emplace (&dyn);
TEST_VERIFY_EXIT (place != NULL);
TEST_VERIFY (*place == 23);
if (i == 0)
*place = 29;
}
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 29);
for (int i = 1; i < count; ++i)
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
TEST_VERIFY_EXIT (result.array != NULL);
TEST_VERIFY (result.length == count);
TEST_VERIFY (result.array[0] == 29);
for (int i = 1; i < count; ++i)
TEST_VERIFY (result.array[i] == 23);
free (result.array);
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
*dynarray_long_noscratch_at (&dyn, 0) = 24;
dynarray_long_noscratch_free (&dyn);
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 2));
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 2);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 1) == 23);
*dynarray_long_noscratch_at (&dyn, 0) = 24;
TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, count));
TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 24);
for (int i = 1; i < count; ++i)
TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
dynarray_long_noscratch_free (&dyn);
}
}
/* Test overflow in resize. */
static void
test_long_overflow (void)
{
{
struct dynarray_long dyn;
dynarray_long_init (&dyn);
errno = EINVAL;
TEST_VERIFY (!dynarray_long_resize
(&dyn, (SIZE_MAX / sizeof (long)) + 1));
TEST_VERIFY (errno == ENOMEM);
TEST_VERIFY (dynarray_long_has_failed (&dyn));
}
{
struct dynarray_long_noscratch dyn;
dynarray_long_noscratch_init (&dyn);
errno = EINVAL;
TEST_VERIFY (!dynarray_long_noscratch_resize
(&dyn, (SIZE_MAX / sizeof (long)) + 1));
TEST_VERIFY (errno == ENOMEM);
TEST_VERIFY (dynarray_long_noscratch_has_failed (&dyn));
}
}
/* Test NUL-terminated string construction with the add function and
the simple finalize function. */
static void
test_zstr (void)
{
/* Totally empty string (no NUL termination). */
{
struct zstr s;
zstr_init (&s);
char *result = zstr_finalize (&s, NULL);
TEST_VERIFY (result == NULL);
TEST_VERIFY (zstr_size (&s) == 0);
size_t length = 1;
result = zstr_finalize (&s, &length);
TEST_VERIFY (result == NULL);
TEST_VERIFY (length == 0);
TEST_VERIFY (zstr_size (&s) == 0);
}
/* Empty string. */
{
struct zstr s;
zstr_init (&s);
zstr_add (&s, '\0');
char *result = zstr_finalize (&s, NULL);
TEST_VERIFY_EXIT (result != NULL);
TEST_VERIFY (*result == '\0');
TEST_VERIFY (zstr_size (&s) == 0);
free (result);
zstr_add (&s, '\0');
size_t length = 1;
result = zstr_finalize (&s, &length);
TEST_VERIFY_EXIT (result != NULL);
TEST_VERIFY (*result == '\0');
TEST_VERIFY (length == 1);
TEST_VERIFY (zstr_size (&s) == 0);
free (result);
}
/* A few characters. */
{
struct zstr s;
zstr_init (&s);
zstr_add (&s, 'A');
zstr_add (&s, 'b');
zstr_add (&s, 'c');
zstr_add (&s, '\0');
char *result = zstr_finalize (&s, NULL);
TEST_VERIFY_EXIT (result != NULL);
TEST_VERIFY (strcmp (result, "Abc") == 0);
TEST_VERIFY (zstr_size (&s) == 0);
free (result);
zstr_add (&s, 'X');
zstr_add (&s, 'y');
zstr_add (&s, 'z');
zstr_add (&s, '\0');
size_t length = 1;
result = zstr_finalize (&s, &length);
TEST_VERIFY_EXIT (result != NULL);
TEST_VERIFY (strcmp (result, "Xyz") == 0);
TEST_VERIFY (length == 4);
TEST_VERIFY (zstr_size (&s) == 0);
free (result);
}
}
static int
do_test (void)
{
mtrace ();
test_int ();
test_str ();
test_long_init ();
test_long_overflow ();
test_zstr ();
return 0;
}
#include <support/test-driver.c>
+237
View File
@@ -0,0 +1,237 @@
/* Test and verify that too-large memory allocations fail with ENOMEM.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Bug 22375 reported a regression in malloc where if after malloc'ing then
free'ing a small block of memory, malloc is then called with a really
large size argument (close to SIZE_MAX): instead of returning NULL and
setting errno to ENOMEM, malloc incorrectly returns the previously
allocated block instead. Bug 22343 reported a similar case where
posix_memalign incorrectly returns successfully when called with an with
a really large size argument.
Both of these were caused by integer overflows in the allocator when it
was trying to pad the requested size to allow for book-keeping or
alignment. This test guards against such bugs by repeatedly allocating
and freeing small blocks of memory then trying to allocate various block
sizes larger than the memory bus width of 64-bit targets, or almost
as large as SIZE_MAX on 32-bit targets supported by glibc. In each case,
it verifies that such impossibly large allocations correctly fail. */
#include <stdlib.h>
#include <malloc.h>
#include <errno.h>
#include <stdint.h>
#include <sys/resource.h>
#include <libc-internal.h>
#include <support/check.h>
#include <unistd.h>
#include <sys/param.h>
/* This function prepares for each 'too-large memory allocation' test by
performing a small successful malloc/free and resetting errno prior to
the actual test. */
static void
test_setup (void)
{
void *volatile ptr = malloc (16);
TEST_VERIFY_EXIT (ptr != NULL);
free (ptr);
errno = 0;
}
/* This function tests each of:
- malloc (SIZE)
- realloc (PTR_FOR_REALLOC, SIZE)
- for various values of NMEMB:
- calloc (NMEMB, SIZE/NMEMB)
- calloc (SIZE/NMEMB, NMEMB)
and precedes each of these tests with a small malloc/free before it. */
static void
test_large_allocations (size_t size)
{
void * ptr_to_realloc;
test_setup ();
TEST_VERIFY (malloc (size) == NULL);
TEST_VERIFY (errno == ENOMEM);
ptr_to_realloc = malloc (16);
TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
test_setup ();
TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
TEST_VERIFY (errno == ENOMEM);
free (ptr_to_realloc);
for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
if ((size % nmemb) == 0)
{
test_setup ();
TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
TEST_VERIFY (errno == ENOMEM);
test_setup ();
TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
TEST_VERIFY (errno == ENOMEM);
}
else
break;
}
static long pagesize;
/* This function tests the following aligned memory allocation functions
using several valid alignments and precedes each allocation test with a
small malloc/free before it:
memalign, posix_memalign, aligned_alloc, valloc, pvalloc. */
static void
test_large_aligned_allocations (size_t size)
{
/* ptr stores the result of posix_memalign but since all those calls
should fail, posix_memalign should never change ptr. We set it to
NULL here and later on we check that it remains NULL after each
posix_memalign call. */
void * ptr = NULL;
size_t align;
/* All aligned memory allocation functions expect an alignment that is a
power of 2. Given this, we test each of them with every valid
alignment from 1 thru PAGESIZE. */
for (align = 1; align <= pagesize; align *= 2)
{
test_setup ();
TEST_VERIFY (memalign (align, size) == NULL);
TEST_VERIFY (errno == ENOMEM);
/* posix_memalign expects an alignment that is a power of 2 *and* a
multiple of sizeof (void *). */
if ((align % sizeof (void *)) == 0)
{
test_setup ();
TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
TEST_VERIFY (ptr == NULL);
}
/* aligned_alloc expects a size that is a multiple of alignment. */
if ((size % align) == 0)
{
test_setup ();
TEST_VERIFY (aligned_alloc (align, size) == NULL);
TEST_VERIFY (errno == ENOMEM);
}
}
/* Both valloc and pvalloc return page-aligned memory. */
test_setup ();
TEST_VERIFY (valloc (size) == NULL);
TEST_VERIFY (errno == ENOMEM);
test_setup ();
TEST_VERIFY (pvalloc (size) == NULL);
TEST_VERIFY (errno == ENOMEM);
}
#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
#define FIFTY_ON_BITS ((1UL << 50) - 1)
static int
do_test (void)
{
#if __WORDSIZE >= 64
/* This test assumes that none of the supported targets have an address
bus wider than 50 bits, and that therefore allocations for sizes wider
than 50 bits will fail. Here, we ensure that the assumption continues
to be true in the future when we might have address buses wider than 50
bits. */
struct rlimit alloc_size_limit
= {
.rlim_cur = FIFTY_ON_BITS,
.rlim_max = FIFTY_ON_BITS
};
setrlimit (RLIMIT_AS, &alloc_size_limit);
#endif /* __WORDSIZE >= 64 */
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* Aligned memory allocation functions need to be tested up to alignment
size equivalent to page size, which should be a power of 2. */
pagesize = sysconf (_SC_PAGESIZE);
TEST_VERIFY_EXIT (powerof2 (pagesize));
/* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
We can expect that this range of allocation sizes will always lead to
an allocation failure on both 64 and 32 bit targets, because:
1. no currently supported 64-bit target has an address bus wider than
50 bits -- and (2^64 - 2^14) is much wider than that;
2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
addressable, glibc itself is more than 2^14 bytes in size, and
therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
available. */
for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
{
test_large_allocations (SIZE_MAX - i);
test_large_aligned_allocations (SIZE_MAX - i);
}
#if __WORDSIZE >= 64
/* On 64-bit targets, we need to test a much wider range of too-large
sizes, so we test at intervals of (1 << 50) that allocation sizes
ranging from SIZE_MAX down to (1 << 50) fail:
The 14 MSBs are decremented starting from "all ON" going down to 1,
the 50 LSBs are "all ON" and then "all OFF" during every iteration. */
for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
{
size_t size = (msbs << 50) | FIFTY_ON_BITS;
test_large_allocations (size);
test_large_aligned_allocations (size);
size = msbs << 50;
test_large_allocations (size);
test_large_aligned_allocations (size);
}
#endif /* __WORDSIZE >= 64 */
DIAG_POP_NEEDS_COMMENT;
return 0;
}
#include <support/test-driver.c>
+6
View File
@@ -175,6 +175,12 @@ options to detect stack overruns. Only the dynamic linker and a small
number of routines called directly from assembler are excluded from this
protection.
@item --enable-bind-now
Disable lazy binding for installed shared objects. This provides
additional security hardening because it enables full RELRO and a
read-only global offset table (GOT), at the cost of slightly increased
program load times.
@pindex pt_chown
@findex grantpt
@item --enable-pt_chown
+5 -4
View File
@@ -29,14 +29,15 @@
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_23)
/* Define the variables used for the interface. */
char *loc1;
char *loc2;
/* Define the variables used for the interface. Avoid .symver on common
symbol, which just creates a new common symbol, not an alias. */
char *loc1 __attribute__ ((nocommon));
char *loc2 __attribute__ ((nocommon));
compat_symbol (libc, loc1, loc1, GLIBC_2_0);
compat_symbol (libc, loc2, loc2, GLIBC_2_0);
/* Although we do not support the use we define this variable as well. */
char *locs;
char *locs __attribute__ ((nocommon));
compat_symbol (libc, locs, locs, GLIBC_2_0);
+16 -5
View File
@@ -219,11 +219,16 @@ CFLAGS-pt-system.c = -fexceptions
LDLIBS-tst-once5 = -lstdc++
CFLAGS-tst-thread_local1.o = -std=gnu++11
LDLIBS-tst-thread_local1 = -lstdc++
CFLAGS-tst-thread-exit-clobber.o = -std=gnu++11
LDLIBS-tst-thread-exit-clobber = -lstdc++
CFLAGS-tst-minstack-throw.o = -std=gnu++11
LDLIBS-tst-minstack-throw = -lstdc++
tests = tst-typesizes \
tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
tst-mutex1 tst-mutex2 tst-mutex3 tst-mutex4 tst-mutex5 tst-mutex6 \
tst-mutex7 tst-mutex8 tst-mutex9 tst-mutex5a tst-mutex7a \
tst-mutex7robust \
tst-mutexpi1 tst-mutexpi2 tst-mutexpi3 tst-mutexpi4 tst-mutexpi5 \
tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a tst-mutexpi8 \
tst-mutexpi9 \
@@ -241,7 +246,7 @@ tests = tst-typesizes \
tst-rwlock4 tst-rwlock5 tst-rwlock6 tst-rwlock7 tst-rwlock8 \
tst-rwlock9 tst-rwlock10 tst-rwlock11 tst-rwlock12 tst-rwlock13 \
tst-rwlock14 tst-rwlock15 tst-rwlock16 tst-rwlock17 tst-rwlock18 \
tst-rwlock19 \
tst-rwlock19 tst-rwlock20 \
tst-once1 tst-once2 tst-once3 tst-once4 tst-once5 \
tst-key1 tst-key2 tst-key3 tst-key4 \
tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 tst-sem6 tst-sem7 \
@@ -296,7 +301,10 @@ tests = tst-typesizes \
c89 gnu89 c99 gnu99 c11 gnu11) \
tst-bad-schedattr \
tst-thread_local1 tst-mutex-errorcheck tst-robust10 \
tst-robust-fork tst-create-detached
tst-robust-fork tst-create-detached \
tst-thread-exit-clobber tst-minstack-cancel tst-minstack-exit \
tst-minstack-throw
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
test-srcs = tst-oddstacklimit
@@ -355,7 +363,7 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
tst-oncex3 tst-oncex4
ifeq ($(build-shared),yes)
tests += tst-atfork2 tst-tls3 tst-tls3-malloc tst-tls4 tst-tls5 tst-_res1 \
tst-fini1 tst-stackguard1
tst-fini1 tst-stackguard1 tst-compat-forwarder
tests-nolibpthread += tst-fini1
ifeq ($(have-z-execstack),yes)
tests += tst-execstack
@@ -366,7 +374,7 @@ modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
tst-join7mod
tst-join7mod tst-compat-forwarder-mod
extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
tst-cleanup4aux.o tst-cleanupx4aux.o
test-extras += $(modules-names) tst-cleanup4aux tst-cleanupx4aux
@@ -440,7 +448,8 @@ endif
ifeq (,$(CXX))
# These tests require a C++ compiler and runtime.
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5
tests-unsupported += tst-cancel24 tst-cancel24-static tst-once5 \
tst-thread-exit-clobber tst-minstack-throw
endif
# These tests require a C++ compiler and runtime with thread_local support.
ifneq ($(have-cxx-thread_local),yes)
@@ -704,6 +713,8 @@ $(objpfx)tst-oddstacklimit.out: $(objpfx)tst-oddstacklimit $(objpfx)tst-basic1
$(evaluate-test)
endif
$(objpfx)tst-compat-forwarder: $(objpfx)tst-compat-forwarder-mod.so
# The tests here better do not run in parallel
ifneq ($(filter %tests,$(MAKECMDGOALS)),)
.NOTPARALLEL:
+12 -2
View File
@@ -482,6 +482,10 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
/* Make sure the size of the stack is enough for the guard and
eventually the thread descriptor. */
guardsize = (attr->guardsize + pagesize_m1) & ~pagesize_m1;
if (guardsize < attr->guardsize || size + guardsize < guardsize)
/* Arithmetic overflow. */
return EINVAL;
size += guardsize;
if (__builtin_expect (size < ((guardsize + __static_tls_size
+ MINIMAL_REST_STACK + pagesize_m1)
& ~pagesize_m1),
@@ -683,8 +687,14 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
prot) != 0)
goto mprot_error;
#elif _STACK_GROWS_UP
if (mprotect ((char *) pd - pd->guardsize,
pd->guardsize - guardsize, prot) != 0)
char *new_guard = (char *)(((uintptr_t) pd - guardsize)
& ~pagesize_m1);
char *old_guard = (char *)(((uintptr_t) pd - pd->guardsize)
& ~pagesize_m1);
/* The guard size difference might be > 0, but once rounded
to the nearest page the size difference might be zero. */
if (new_guard > old_guard
&& mprotect (old_guard, new_guard - old_guard, prot) != 0)
goto mprot_error;
#endif
+2 -2
View File
@@ -382,9 +382,9 @@ struct pthread
/* Machine-specific unwind info. */
struct _Unwind_Exception exc;
/* If nonzero pointer to area allocated for the stack and its
size. */
/* If nonzero, pointer to the area allocated for the stack and guard. */
void *stackblock;
/* Size of the stackblock area including the guard. */
size_t stackblock_size;
/* Size of the included guard area. */
size_t guardsize;
+1 -4
View File
@@ -473,8 +473,5 @@ strong_alias (__pthread_initialize_minimal_internal,
size_t
__pthread_get_minstack (const pthread_attr_t *attr)
{
struct pthread_attr *iattr = (struct pthread_attr *) attr;
return (GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN
+ iattr->guardsize);
return GLRO(dl_pagesize) + __static_tls_size + PTHREAD_STACK_MIN;
}
+10 -21
View File
@@ -25,36 +25,25 @@
symbol in libpthread, but the historical ABI requires it. For static
linking, there is no need to provide anything here--the libc version
will be linked in. For shared library ABI compatibility, there must be
longjmp and siglongjmp symbols in libpthread.so; so we define them using
IFUNC to redirect to the libc function. */
longjmp and siglongjmp symbols in libpthread.so.
With an IFUNC resolver, it would be possible to avoid the indirection,
but the IFUNC resolver might run before the __libc_longjmp symbol has
been relocated, in which case the IFUNC resolver would not be able to
provide the correct address. */
#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22)
# if HAVE_IFUNC
# undef INIT_ARCH
# define INIT_ARCH()
# define DEFINE_LONGJMP(name) libc_ifunc (name, &__libc_longjmp)
extern __typeof(longjmp) longjmp_ifunc;
extern __typeof(siglongjmp) siglongjmp_ifunc;
# else /* !HAVE_IFUNC */
static void __attribute__ ((noreturn, used))
longjmp_compat (jmp_buf env, int val)
{
__libc_longjmp (env, val);
}
# define DEFINE_LONGJMP(name) strong_alias (longjmp_compat, name)
strong_alias (longjmp_compat, longjmp_alias)
compat_symbol (libpthread, longjmp_alias, longjmp, GLIBC_2_0);
# endif /* HAVE_IFUNC */
DEFINE_LONGJMP (longjmp_ifunc)
compat_symbol (libpthread, longjmp_ifunc, longjmp, GLIBC_2_0);
strong_alias (longjmp_ifunc, siglongjmp_ifunc)
compat_symbol (libpthread, siglongjmp_ifunc, siglongjmp, GLIBC_2_0);
strong_alias (longjmp_alias, siglongjmp_alias)
compat_symbol (libpthread, siglongjmp_alias, siglongjmp, GLIBC_2_0);
#endif
+8 -16
View File
@@ -25,29 +25,21 @@
libpthread, but the historical ABI requires it. For static linking,
there is no need to provide anything here--the libc version will be
linked in. For shared library ABI compatibility, there must be a
'system' symbol in libpthread.so; so we define it using IFUNC to
redirect to the libc function. */
'system' symbol in libpthread.so.
With an IFUNC resolver, it would be possible to avoid the indirection,
but the IFUNC resolver might run before the __libc_system symbol has
been relocated, in which case the IFUNC resolver would not be able to
provide the correct address. */
#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_22)
# if HAVE_IFUNC
extern __typeof(system) system_ifunc;
# undef INIT_ARCH
# define INIT_ARCH()
libc_ifunc (system_ifunc, &__libc_system)
# else /* !HAVE_IFUNC */
static int __attribute__ ((used))
system_compat (const char *line)
{
return __libc_system (line);
}
strong_alias (system_compat, system_ifunc)
# endif /* HAVE_IFUNC */
compat_symbol (libpthread, system_ifunc, system, GLIBC_2_0);
strong_alias (system_compat, system_alias)
compat_symbol (libpthread, system_alias, system, GLIBC_2_0);
#endif
+6 -2
View File
@@ -406,8 +406,12 @@ __condvar_quiesce_and_switch_g1 (pthread_cond_t *cond, uint64_t wseq,
{
/* There is still a waiter after spinning. Set the wake-request
flag and block. Relaxed MO is fine because this is just about
this futex word. */
r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1);
this futex word.
Update r to include the set wake-request flag so that the upcoming
futex_wait only blocks if the flag is still set (otherwise, we'd
violate the basic client-side futex protocol). */
r = atomic_fetch_or_relaxed (cond->__data.__g_refs + g1, 1) | 1;
if ((r >> 1) > 0)
futex_wait_simple (cond->__data.__g_refs + g1, r, private);
+5 -2
View File
@@ -57,9 +57,12 @@ pthread_getattr_np (pthread_t thread_id, pthread_attr_t *attr)
/* The sizes are subject to alignment. */
if (__glibc_likely (thread->stackblock != NULL))
{
iattr->stacksize = thread->stackblock_size;
/* The stack size reported to the user should not include the
guard size. */
iattr->stacksize = thread->stackblock_size - thread->guardsize;
#if _STACK_GROWS_DOWN
iattr->stackaddr = (char *) thread->stackblock + iattr->stacksize;
iattr->stackaddr = (char *) thread->stackblock
+ thread->stackblock_size;
#else
iattr->stackaddr = (char *) thread->stackblock;
#endif
+8 -5
View File
@@ -197,11 +197,14 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
{
/* Try to acquire the lock through a CAS from 0 (not acquired) to
our TID | assume_other_futex_waiters. */
if (__glibc_likely ((oldval == 0)
&& (atomic_compare_and_exchange_bool_acq
(&mutex->__data.__lock,
id | assume_other_futex_waiters, 0) == 0)))
break;
if (__glibc_likely (oldval == 0))
{
oldval
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
id | assume_other_futex_waiters, 0);
if (__glibc_likely (oldval == 0))
break;
}
if ((oldval & FUTEX_OWNER_DIED) != 0)
{
+8 -5
View File
@@ -154,11 +154,14 @@ pthread_mutex_timedlock (pthread_mutex_t *mutex,
{
/* Try to acquire the lock through a CAS from 0 (not acquired) to
our TID | assume_other_futex_waiters. */
if (__glibc_likely ((oldval == 0)
&& (atomic_compare_and_exchange_bool_acq
(&mutex->__data.__lock,
id | assume_other_futex_waiters, 0) == 0)))
break;
if (__glibc_likely (oldval == 0))
{
oldval
= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
id | assume_other_futex_waiters, 0);
if (__glibc_likely (oldval == 0))
break;
}
if ((oldval & FUTEX_OWNER_DIED) != 0)
{
+53 -4
View File
@@ -92,6 +92,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
&mutex->__data.__list.__next);
/* We need to set op_pending before starting the operation. Also
see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
oldval = mutex->__data.__lock;
do
@@ -117,7 +120,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
/* But it is inconsistent unless marked otherwise. */
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
/* We must not enqueue the mutex before we have acquired it.
Also see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
ENQUEUE_MUTEX (mutex);
/* We need to clear op_pending after we enqueue the mutex. */
__asm ("" ::: "memory");
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
/* Note that we deliberately exist here. If we fall
@@ -133,6 +141,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
int kind = PTHREAD_MUTEX_TYPE (mutex);
if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
{
/* We do not need to ensure ordering wrt another memory
access. Also see comments at ENQUEUE_MUTEX. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
NULL);
return EDEADLK;
@@ -140,6 +150,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
{
/* We do not need to ensure ordering wrt another memory
access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
NULL);
@@ -158,6 +170,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
id, 0);
if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
{
/* We haven't acquired the lock as it is already acquired by
another owner. We do not need to ensure ordering wrt another
memory access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return EBUSY;
@@ -171,13 +186,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
if (oldval == id)
lll_unlock (mutex->__data.__lock,
PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
/* FIXME This violates the mutex destruction requirements. See
__pthread_mutex_unlock_full. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return ENOTRECOVERABLE;
}
}
while ((oldval & FUTEX_OWNER_DIED) != 0);
/* We must not enqueue the mutex before we have acquired it.
Also see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
ENQUEUE_MUTEX (mutex);
/* We need to clear op_pending after we enqueue the mutex. */
__asm ("" ::: "memory");
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
mutex->__data.__owner = id;
@@ -203,10 +225,15 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
int robust = mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP;
if (robust)
/* Note: robust PI futexes are signaled by setting bit 0. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
(void *) (((uintptr_t) &mutex->__data.__list.__next)
| 1));
{
/* Note: robust PI futexes are signaled by setting bit 0. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
(void *) (((uintptr_t) &mutex->__data.__list.__next)
| 1));
/* We need to set op_pending before starting the operation. Also
see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
}
oldval = mutex->__data.__lock;
@@ -215,12 +242,16 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
{
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
{
/* We do not need to ensure ordering wrt another memory
access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return EDEADLK;
}
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
{
/* We do not need to ensure ordering wrt another memory
access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
/* Just bump the counter. */
@@ -242,6 +273,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
{
if ((oldval & FUTEX_OWNER_DIED) == 0)
{
/* We haven't acquired the lock as it is already acquired by
another owner. We do not need to ensure ordering wrt another
memory access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return EBUSY;
@@ -262,6 +296,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
if (INTERNAL_SYSCALL_ERROR_P (e, __err)
&& INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK)
{
/* The kernel has not yet finished the mutex owner death.
We do not need to ensure ordering wrt another memory
access. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return EBUSY;
@@ -279,7 +316,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
/* But it is inconsistent unless marked otherwise. */
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
/* We must not enqueue the mutex before we have acquired it.
Also see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
ENQUEUE_MUTEX (mutex);
/* We need to clear op_pending after we enqueue the mutex. */
__asm ("" ::: "memory");
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
/* Note that we deliberately exit here. If we fall
@@ -302,13 +344,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
0, 0);
/* To the kernel, this will be visible after the kernel has
acquired the mutex in the syscall. */
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
return ENOTRECOVERABLE;
}
if (robust)
{
/* We must not enqueue the mutex before we have acquired it.
Also see comments at ENQUEUE_MUTEX. */
__asm ("" ::: "memory");
ENQUEUE_MUTEX_PI (mutex);
/* We need to clear op_pending after we enqueue the mutex. */
__asm ("" ::: "memory");
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
}
+242 -240
View File
@@ -55,7 +55,6 @@
lock acquisition attempts, so that new incoming readers do not prolong a
phase in which readers have acquired the lock.
The main components of the rwlock are a writer-only lock that allows only
one of the concurrent writers to be the primary writer, and a
single-writer-multiple-readers lock that decides between read phases, in
@@ -70,15 +69,16 @@
---------------------------
#1 0 0 0 0 Lock is idle (and in a read phase).
#2 0 0 >0 0 Readers have acquired the lock.
#3 0 1 0 0 Lock is not acquired; a writer is waiting for a write
phase to start or will try to start one.
#3 0 1 0 0 Lock is not acquired; a writer will try to start a
write phase.
#4 0 1 >0 0 Readers have acquired the lock; a writer is waiting
and explicit hand-over to the writer is required.
#4a 0 1 >0 1 Same as #4 except that there are further readers
waiting because the writer is to be preferred.
#5 1 0 0 0 Lock is idle (and in a write phase).
#6 1 0 >0 0 Write phase; readers are waiting for a read phase to
start or will try to start one.
#6 1 0 >0 0 Write phase; readers will try to start a read phase
(requires explicit hand-over to all readers that
do not start the read phase).
#7 1 1 0 0 Lock is acquired by a writer.
#8 1 1 >0 0 Lock acquired by a writer and readers are waiting;
explicit hand-over to the readers is required.
@@ -375,9 +375,9 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
complexity. */
if (__glibc_likely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
return 0;
/* If there is no primary writer but we are in a write phase, we can try
to install a read phase ourself. */
/* Otherwise, if we were in a write phase (states #6 or #8), we must wait
for explicit hand-over of the read phase; the only exception is if we
can start a read phase if there is no primary writer currently. */
while (((r & PTHREAD_RWLOCK_WRPHASE) != 0)
&& ((r & PTHREAD_RWLOCK_WRLOCKED) == 0))
{
@@ -390,15 +390,18 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
{
/* We started the read phase, so we are also responsible for
updating the write-phase futex. Relaxed MO is sufficient.
Note that there can be no other reader that we have to wake
because all other readers will see the read phase started by us
(or they will try to start it themselves); if a writer started
the read phase, we cannot have started it. Furthermore, we
cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
overwrite the value set by the most recent writer (or the readers
before it in case of explicit hand-over) and we know that there
are no waiting readers. */
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
We have to do the same steps as a writer would when handing
over the read phase to us because other readers cannot
distinguish between us and the writer; this includes
explicit hand-over and potentially having to wake other readers
(but we can pretend to do the setting and unsetting of WRLOCKED
atomically, and thus can skip this step). */
if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
& PTHREAD_RWLOCK_FUTEX_USED) != 0)
{
int private = __pthread_rwlock_get_private (rwlock);
futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
}
return 0;
}
else
@@ -407,102 +410,98 @@ __pthread_rwlock_rdlock_full (pthread_rwlock_t *rwlock,
}
}
if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
/* We were in a write phase but did not install the read phase. We cannot
distinguish between a writer and another reader starting the read phase,
so we must wait for explicit hand-over via __wrphase_futex.
However, __wrphase_futex might not have been set to 1 yet (either
because explicit hand-over to the writer is still ongoing, or because
the writer has started the write phase but has not yet updated
__wrphase_futex). The least recent value of __wrphase_futex we can
read from here is the modification of the last read phase (because
we synchronize with the last reader in this read phase through
__readers; see the use of acquire MO on the fetch_add above).
Therefore, if we observe a value of 0 for __wrphase_futex, we need
to subsequently check that __readers now indicates a read phase; we
need to use acquire MO for this so that if we observe a read phase,
we will also see the modification of __wrphase_futex by the previous
writer. We then need to load __wrphase_futex again and continue to
wait if it is not 0, so that we do not skip explicit hand-over.
Relaxed MO is sufficient for the load from __wrphase_futex because
we just use it as an indicator for when we can proceed; we use
__readers and the acquire MO accesses to it to eventually read from
the proper stores to __wrphase_futex. */
unsigned int wpf;
bool ready = false;
for (;;)
{
/* We are in a write phase, and there must be a primary writer because
of the previous loop. Block until the primary writer gives up the
write phase. This case requires explicit hand-over using
__wrphase_futex.
However, __wrphase_futex might not have been set to 1 yet (either
because explicit hand-over to the writer is still ongoing, or because
the writer has started the write phase but does not yet have updated
__wrphase_futex). The least recent value of __wrphase_futex we can
read from here is the modification of the last read phase (because
we synchronize with the last reader in this read phase through
__readers; see the use of acquire MO on the fetch_add above).
Therefore, if we observe a value of 0 for __wrphase_futex, we need
to subsequently check that __readers now indicates a read phase; we
need to use acquire MO for this so that if we observe a read phase,
we will also see the modification of __wrphase_futex by the previous
writer. We then need to load __wrphase_futex again and continue to
wait if it is not 0, so that we do not skip explicit hand-over.
Relaxed MO is sufficient for the load from __wrphase_futex because
we just use it as an indicator for when we can proceed; we use
__readers and the acquire MO accesses to it to eventually read from
the proper stores to __wrphase_futex. */
unsigned int wpf;
bool ready = false;
for (;;)
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
| PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
{
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
| PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
int private = __pthread_rwlock_get_private (rwlock);
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
&& !atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__wrphase_futex,
&wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
continue;
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
if (err == ETIMEDOUT)
{
int private = __pthread_rwlock_get_private (rwlock);
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
&& !atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__wrphase_futex,
&wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED))
continue;
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
1 | PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
if (err == ETIMEDOUT)
/* If we timed out, we need to unregister. If no read phase
has been installed while we waited, we can just decrement
the number of readers. Otherwise, we just acquire the
lock, which is allowed because we give no precise timing
guarantees, and because the timeout is only required to
be in effect if we would have had to wait for other
threads (e.g., if futex_wait would time-out immediately
because the given absolute time is in the past). */
r = atomic_load_relaxed (&rwlock->__data.__readers);
while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
{
/* If we timed out, we need to unregister. If no read phase
has been installed while we waited, we can just decrement
the number of readers. Otherwise, we just acquire the
lock, which is allowed because we give no precise timing
guarantees, and because the timeout is only required to
be in effect if we would have had to wait for other
threads (e.g., if futex_wait would time-out immediately
because the given absolute time is in the past). */
r = atomic_load_relaxed (&rwlock->__data.__readers);
while ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
{
/* We don't need to make anything else visible to
others besides unregistering, so relaxed MO is
sufficient. */
if (atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__readers, &r,
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
return ETIMEDOUT;
/* TODO Back-off. */
}
/* Use the acquire MO fence to mirror the steps taken in the
non-timeout case. Note that the read can happen both
in the atomic_load above as well as in the failure case
of the CAS operation. */
atomic_thread_fence_acquire ();
/* We still need to wait for explicit hand-over, but we must
not use futex_wait anymore because we would just time out
in this case and thus make the spin-waiting we need
unnecessarily expensive. */
while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
| PTHREAD_RWLOCK_FUTEX_USED)
== (1 | PTHREAD_RWLOCK_FUTEX_USED))
{
/* TODO Back-off? */
}
ready = true;
break;
/* We don't need to make anything else visible to
others besides unregistering, so relaxed MO is
sufficient. */
if (atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__readers, &r,
r - (1 << PTHREAD_RWLOCK_READER_SHIFT)))
return ETIMEDOUT;
/* TODO Back-off. */
}
/* If we got interrupted (EINTR) or the futex word does not have the
expected value (EAGAIN), retry. */
/* Use the acquire MO fence to mirror the steps taken in the
non-timeout case. Note that the read can happen both
in the atomic_load above as well as in the failure case
of the CAS operation. */
atomic_thread_fence_acquire ();
/* We still need to wait for explicit hand-over, but we must
not use futex_wait anymore because we would just time out
in this case and thus make the spin-waiting we need
unnecessarily expensive. */
while ((atomic_load_relaxed (&rwlock->__data.__wrphase_futex)
| PTHREAD_RWLOCK_FUTEX_USED)
== (1 | PTHREAD_RWLOCK_FUTEX_USED))
{
/* TODO Back-off? */
}
ready = true;
break;
}
if (ready)
/* See below. */
break;
/* We need acquire MO here so that we synchronize with the lock
release of the writer, and so that we observe a recent value of
__wrphase_futex (see below). */
if ((atomic_load_acquire (&rwlock->__data.__readers)
& PTHREAD_RWLOCK_WRPHASE) == 0)
/* We are in a read phase now, so the least recent modification of
__wrphase_futex we can read from is the store by the writer
with value 1. Thus, only now we can assume that if we observe
a value of 0, explicit hand-over is finished. Retry the loop
above one more time. */
ready = true;
/* If we got interrupted (EINTR) or the futex word does not have the
expected value (EAGAIN), retry. */
}
if (ready)
/* See below. */
break;
/* We need acquire MO here so that we synchronize with the lock
release of the writer, and so that we observe a recent value of
__wrphase_futex (see below). */
if ((atomic_load_acquire (&rwlock->__data.__readers)
& PTHREAD_RWLOCK_WRPHASE) == 0)
/* We are in a read phase now, so the least recent modification of
__wrphase_futex we can read from is the store by the writer
with value 1. Thus, only now we can assume that if we observe
a value of 0, explicit hand-over is finished. Retry the loop
above one more time. */
ready = true;
}
return 0;
@@ -741,10 +740,23 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
r = atomic_load_relaxed (&rwlock->__data.__readers);
}
/* Our snapshot of __readers is up-to-date at this point because we
either set WRLOCKED using a CAS or were handed over WRLOCKED from
either set WRLOCKED using a CAS (and update r accordingly below,
which was used as expected value for the CAS) or got WRLOCKED from
another writer whose snapshot of __readers we inherit. */
r |= PTHREAD_RWLOCK_WRLOCKED;
}
/* We are the primary writer; enable blocking on __writers_futex. Relaxed
MO is sufficient for futex words; acquire MO on the previous
modifications of __readers ensures that this store happens after the
store of value 0 by the previous primary writer. */
atomic_store_relaxed (&rwlock->__data.__writers_futex,
1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
/* If we are in a write phase, we have acquired the lock. */
if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
goto done;
/* If we are in a read phase and there are no readers, try to start a write
phase. */
while (((r & PTHREAD_RWLOCK_WRPHASE) == 0)
@@ -758,166 +770,156 @@ __pthread_rwlock_wrlock_full (pthread_rwlock_t *rwlock,
&r, r | PTHREAD_RWLOCK_WRPHASE))
{
/* We have started a write phase, so need to enable readers to wait.
See the similar case in__pthread_rwlock_rdlock_full. */
See the similar case in __pthread_rwlock_rdlock_full. Unlike in
that similar case, we are the (only) primary writer and so do
not need to wake another writer. */
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
/* Make sure we fall through to the end of the function. */
r |= PTHREAD_RWLOCK_WRPHASE;
break;
goto done;
}
/* TODO Back-off. */
}
/* We are the primary writer; enable blocking on __writers_futex. Relaxed
MO is sufficient for futex words; acquire MO on the previous
modifications of __readers ensures that this store happens after the
store of value 0 by the previous primary writer. */
atomic_store_relaxed (&rwlock->__data.__writers_futex,
1 | (may_share_futex_used_flag ? PTHREAD_RWLOCK_FUTEX_USED : 0));
if (__glibc_unlikely ((r & PTHREAD_RWLOCK_WRPHASE) == 0))
/* We became the primary writer in a read phase and there were readers when
we did (because of the previous loop). Thus, we have to wait for
explicit hand-over from one of these readers.
We basically do the same steps as for the similar case in
__pthread_rwlock_rdlock_full, except that we additionally might try
to directly hand over to another writer and need to wake up
other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */
unsigned int wpf;
bool ready = false;
for (;;)
{
/* We are not in a read phase and there are readers (because of the
previous loop). Thus, we have to wait for explicit hand-over from
one of these readers.
We basically do the same steps as for the similar case in
__pthread_rwlock_rdlock_full, except that we additionally might try
to directly hand over to another writer and need to wake up
other writers or waiting readers (i.e., PTHREAD_RWLOCK_RWAITING). */
unsigned int wpf;
bool ready = false;
for (;;)
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
| PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
{
while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
| PTHREAD_RWLOCK_FUTEX_USED) == PTHREAD_RWLOCK_FUTEX_USED)
int private = __pthread_rwlock_get_private (rwlock);
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
&& !atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__wrphase_futex, &wpf,
PTHREAD_RWLOCK_FUTEX_USED))
continue;
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
if (err == ETIMEDOUT)
{
int private = __pthread_rwlock_get_private (rwlock);
if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
&& !atomic_compare_exchange_weak_relaxed
(&rwlock->__data.__wrphase_futex, &wpf,
PTHREAD_RWLOCK_FUTEX_USED))
continue;
int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
PTHREAD_RWLOCK_FUTEX_USED, abstime, private);
if (err == ETIMEDOUT)
if (rwlock->__data.__flags
!= PTHREAD_RWLOCK_PREFER_READER_NP)
{
if (rwlock->__data.__flags
!= PTHREAD_RWLOCK_PREFER_READER_NP)
/* We try writer--writer hand-over. */
unsigned int w = atomic_load_relaxed
(&rwlock->__data.__writers);
if (w != 0)
{
/* We try writer--writer hand-over. */
unsigned int w = atomic_load_relaxed
(&rwlock->__data.__writers);
if (w != 0)
{
/* We are about to hand over WRLOCKED, so we must
release __writers_futex too; otherwise, we'd have
a pending store, which could at least prevent
other threads from waiting using the futex
because it could interleave with the stores
by subsequent writers. In turn, this means that
we have to clean up when we do not hand over
WRLOCKED.
Release MO so that another writer that gets
WRLOCKED from us can take over our view of
__readers. */
unsigned int wf = atomic_exchange_relaxed
(&rwlock->__data.__writers_futex, 0);
while (w != 0)
{
if (atomic_compare_exchange_weak_release
(&rwlock->__data.__writers, &w,
w | PTHREAD_RWLOCK_WRHANDOVER))
{
/* Wake other writers. */
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
futex_wake
(&rwlock->__data.__writers_futex, 1,
private);
return ETIMEDOUT;
}
/* TODO Back-off. */
}
/* We still own WRLOCKED and someone else might set
a write phase concurrently, so enable waiting
again. Make sure we don't loose the flag that
signals whether there are threads waiting on
this futex. */
atomic_store_relaxed
(&rwlock->__data.__writers_futex, wf);
}
}
/* If we timed out and we are not in a write phase, we can
just stop being a primary writer. Otherwise, we just
acquire the lock. */
r = atomic_load_relaxed (&rwlock->__data.__readers);
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
{
/* We are about to release WRLOCKED, so we must release
__writers_futex too; see the handling of
writer--writer hand-over above. */
/* We are about to hand over WRLOCKED, so we must
release __writers_futex too; otherwise, we'd have
a pending store, which could at least prevent
other threads from waiting using the futex
because it could interleave with the stores
by subsequent writers. In turn, this means that
we have to clean up when we do not hand over
WRLOCKED.
Release MO so that another writer that gets
WRLOCKED from us can take over our view of
__readers. */
unsigned int wf = atomic_exchange_relaxed
(&rwlock->__data.__writers_futex, 0);
while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
while (w != 0)
{
/* While we don't need to make anything from a
caller's critical section visible to other
threads, we need to ensure that our changes to
__writers_futex are properly ordered.
Therefore, use release MO to synchronize with
subsequent primary writers. Also wake up any
waiting readers as they are waiting because of
us. */
if (atomic_compare_exchange_weak_release
(&rwlock->__data.__readers, &r,
(r ^ PTHREAD_RWLOCK_WRLOCKED)
& ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
(&rwlock->__data.__writers, &w,
w | PTHREAD_RWLOCK_WRHANDOVER))
{
/* Wake other writers. */
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
futex_wake (&rwlock->__data.__writers_futex,
1, private);
/* Wake waiting readers. */
if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
futex_wake (&rwlock->__data.__readers,
INT_MAX, private);
1, private);
return ETIMEDOUT;
}
/* TODO Back-off. */
}
/* We still own WRLOCKED and someone else might set a
write phase concurrently, so enable waiting again.
Make sure we don't loose the flag that signals
whether there are threads waiting on this futex. */
atomic_store_relaxed (&rwlock->__data.__writers_futex,
wf);
/* We still own WRLOCKED and someone else might set
a write phase concurrently, so enable waiting
again. Make sure we don't loose the flag that
signals whether there are threads waiting on
this futex. */
atomic_store_relaxed
(&rwlock->__data.__writers_futex, wf);
}
/* Use the acquire MO fence to mirror the steps taken in the
non-timeout case. Note that the read can happen both
in the atomic_load above as well as in the failure case
of the CAS operation. */
atomic_thread_fence_acquire ();
/* We still need to wait for explicit hand-over, but we must
not use futex_wait anymore. */
while ((atomic_load_relaxed
(&rwlock->__data.__wrphase_futex)
| PTHREAD_RWLOCK_FUTEX_USED)
== PTHREAD_RWLOCK_FUTEX_USED)
{
/* TODO Back-off. */
}
ready = true;
break;
}
/* If we got interrupted (EINTR) or the futex word does not have
the expected value (EAGAIN), retry. */
/* If we timed out and we are not in a write phase, we can
just stop being a primary writer. Otherwise, we just
acquire the lock. */
r = atomic_load_relaxed (&rwlock->__data.__readers);
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
{
/* We are about to release WRLOCKED, so we must release
__writers_futex too; see the handling of
writer--writer hand-over above. */
unsigned int wf = atomic_exchange_relaxed
(&rwlock->__data.__writers_futex, 0);
while ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
{
/* While we don't need to make anything from a
caller's critical section visible to other
threads, we need to ensure that our changes to
__writers_futex are properly ordered.
Therefore, use release MO to synchronize with
subsequent primary writers. Also wake up any
waiting readers as they are waiting because of
us. */
if (atomic_compare_exchange_weak_release
(&rwlock->__data.__readers, &r,
(r ^ PTHREAD_RWLOCK_WRLOCKED)
& ~(unsigned int) PTHREAD_RWLOCK_RWAITING))
{
/* Wake other writers. */
if ((wf & PTHREAD_RWLOCK_FUTEX_USED) != 0)
futex_wake (&rwlock->__data.__writers_futex,
1, private);
/* Wake waiting readers. */
if ((r & PTHREAD_RWLOCK_RWAITING) != 0)
futex_wake (&rwlock->__data.__readers,
INT_MAX, private);
return ETIMEDOUT;
}
}
/* We still own WRLOCKED and someone else might set a
write phase concurrently, so enable waiting again.
Make sure we don't loose the flag that signals
whether there are threads waiting on this futex. */
atomic_store_relaxed (&rwlock->__data.__writers_futex, wf);
}
/* Use the acquire MO fence to mirror the steps taken in the
non-timeout case. Note that the read can happen both
in the atomic_load above as well as in the failure case
of the CAS operation. */
atomic_thread_fence_acquire ();
/* We still need to wait for explicit hand-over, but we must
not use futex_wait anymore. */
while ((atomic_load_relaxed
(&rwlock->__data.__wrphase_futex)
| PTHREAD_RWLOCK_FUTEX_USED)
== PTHREAD_RWLOCK_FUTEX_USED)
{
/* TODO Back-off. */
}
ready = true;
break;
}
/* See pthread_rwlock_rdlock_full. */
if (ready)
break;
if ((atomic_load_acquire (&rwlock->__data.__readers)
& PTHREAD_RWLOCK_WRPHASE) != 0)
ready = true;
/* If we got interrupted (EINTR) or the futex word does not have
the expected value (EAGAIN), retry. */
}
/* See pthread_rwlock_rdlock_full. */
if (ready)
break;
if ((atomic_load_acquire (&rwlock->__data.__readers)
& PTHREAD_RWLOCK_WRPHASE) != 0)
ready = true;
}
done:
atomic_store_relaxed (&rwlock->__data.__cur_writer,
THREAD_GETMEM (THREAD_SELF, tid));
return 0;
+28
View File
@@ -0,0 +1,28 @@
/* Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Call the function system through a statically initialized pointer. */
#include <stdlib.h>
int (*system_function) (const char *) = system;
void
call_system (void)
{
system_function (NULL);
}
+35
View File
@@ -0,0 +1,35 @@
/* Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Test that the compat forwaders in libpthread work correctly. */
#include <support/test-driver.h>
extern void call_system (void);
int
do_test (void)
{
/* Calling the system function from a shared library that is not linked
against libpthread, when the main program is linked against
libpthread, should not crash. */
call_system ();
return 0;
}
#include <support/test-driver.c>
+48
View File
@@ -0,0 +1,48 @@
/* Test cancellation with a minimal stack size.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Note: This test is similar to tst-minstack-exit, but is separate to
avoid spurious test passes due to warm-up effects. */
#include <limits.h>
#include <unistd.h>
#include <support/check.h>
#include <support/xthread.h>
static void *
threadfunc (void *closure)
{
while (1)
pause ();
return NULL;
}
static int
do_test (void)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
xpthread_cancel (thr);
TEST_VERIFY (xpthread_join (thr) == PTHREAD_CANCELED);
xpthread_attr_destroy (&attr);
return 0;
}
#include <support/test-driver.c>
+46
View File
@@ -0,0 +1,46 @@
/* Test that pthread_exit works with the minimum stack size.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Note: This test is similar to tst-minstack-cancel, but is separate
to avoid spurious test passes due to warm-up effects. */
#include <limits.h>
#include <unistd.h>
#include <support/check.h>
#include <support/xthread.h>
static void *
threadfunc (void *closure)
{
pthread_exit (threadfunc);
return NULL;
}
static int
do_test (void)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
TEST_VERIFY (xpthread_join (thr) == threadfunc);
xpthread_attr_destroy (&attr);
return 0;
}
#include <support/test-driver.c>
+87
View File
@@ -0,0 +1,87 @@
/* Test that throwing C++ exceptions works with the minimum stack size.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <stdexcept>
#include <limits.h>
#include <string.h>
#include <support/check.h>
#include <support/xthread.h>
/* Throw a std::runtime_exception. */
__attribute__ ((noinline, noclone, weak))
void
do_throw_exception ()
{
throw std::runtime_error ("test exception");
}
/* Class with a destructor, to trigger unwind handling. */
struct class_with_destructor
{
class_with_destructor ();
~class_with_destructor ();
};
__attribute__ ((noinline, noclone, weak))
class_with_destructor::class_with_destructor ()
{
}
__attribute__ ((noinline, noclone, weak))
class_with_destructor::~class_with_destructor ()
{
}
__attribute__ ((noinline, noclone, weak))
void
function_with_destructed_object ()
{
class_with_destructor obj;
do_throw_exception ();
}
static void *
threadfunc (void *closure)
{
try
{
function_with_destructed_object ();
FAIL_EXIT1 ("no exception thrown");
}
catch (std::exception &e)
{
TEST_COMPARE (strcmp (e.what (), "test exception"), 0);
return reinterpret_cast<void *> (threadfunc);
}
FAIL_EXIT1 ("no exception caught");
}
static int
do_test (void)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
pthread_t thr = xpthread_create (&attr, threadfunc, NULL);
TEST_VERIFY (xpthread_join (thr) == threadfunc);
xpthread_attr_destroy (&attr);
return 0;
}
#include <support/test-driver.c>
+35 -10
View File
@@ -22,25 +22,41 @@
#include <stdlib.h>
#include <time.h>
/* This test is a template for other tests to use. Other tests define
the following macros to change the behaviour of the template test.
The test is very simple, it configures N threads given the parameters
below and then proceeds to go through mutex lock and unlock
operations in each thread as described before for the thread
function. */
#ifndef TYPE
# define TYPE PTHREAD_MUTEX_DEFAULT
#endif
#ifndef ROBUST
# define ROBUST PTHREAD_MUTEX_STALLED
#endif
#ifndef DELAY_NSEC
# define DELAY_NSEC 11000
#endif
#ifndef ROUNDS
# define ROUNDS 1000
#endif
#ifndef N
# define N 100
#endif
static pthread_mutex_t lock;
#define ROUNDS 1000
#define N 100
/* Each thread locks and the subsequently unlocks the lock, yielding
the smallest critical section possible. After the unlock the thread
waits DELAY_NSEC nanoseconds before doing the lock and unlock again.
Every thread does this ROUNDS times. The lock and unlock are
checked for errors. */
static void *
tf (void *arg)
{
int nr = (long int) arg;
int cnt;
struct timespec ts = { .tv_sec = 0, .tv_nsec = 11000 };
struct timespec ts = { .tv_sec = 0, .tv_nsec = DELAY_NSEC };
for (cnt = 0; cnt < ROUNDS; ++cnt)
{
@@ -56,13 +72,16 @@ tf (void *arg)
return (void *) 1l;
}
nanosleep (&ts, NULL);
if ((ts.tv_sec > 0) || (ts.tv_nsec > 0))
nanosleep (&ts, NULL);
}
return NULL;
}
/* Setup and run N threads, where each thread does as described
in the above thread function. The threads are given a minimal 1MiB
stack since they don't do anything between the lock and unlock. */
static int
do_test (void)
{
@@ -80,6 +99,12 @@ do_test (void)
exit (1);
}
if (pthread_mutexattr_setrobust (&a, ROBUST) != 0)
{
puts ("mutexattr_setrobust failed");
exit (1);
}
#ifdef ENABLE_PI
if (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT) != 0)
{
+7
View File
@@ -0,0 +1,7 @@
/* Bug 21778: Fix oversight in robust mutex lock acquisition. */
#define TYPE PTHREAD_MUTEX_NORMAL
#define ROBUST PTHREAD_MUTEX_ROBUST
#define DELAY_NSEC 0
#define ROUNDS 1000
#define N 32
#include "tst-mutex7.c"
+116
View File
@@ -0,0 +1,116 @@
/* Test program for a read-phase / write-phase explicit hand-over.
Copyright (C) 2017 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, see <http://www.gnu.org/licenses/>. */
#include <errno.h>
#include <error.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
#include <atomic.h>
#include <support/xthread.h>
/* We realy want to set threads to 2 to reproduce this issue. The goal
is to have one primary writer and a single reader, and to hit the
bug that happens in the interleaving of those two phase transitions.
However, on most hardware, adding a second writer seems to help the
interleaving happen slightly more often, say 20% of the time. On a
16 core ppc64 machine this fails 100% of the time with an unpatched
glibc. On a 8 core x86_64 machine this fails ~93% of the time, but
it doesn't fail at all on a 4 core system, so having available
unloaded cores makes a big difference in reproducibility. On an 8
core qemu/kvm guest the reproducer reliability drops to ~10%. */
#define THREADS 3
#define KIND PTHREAD_RWLOCK_PREFER_READER_NP
static pthread_rwlock_t lock;
static int done = 0;
static void*
tf (void* arg)
{
while (atomic_load_relaxed (&done) == 0)
{
int rcnt = 0;
int wcnt = 100;
if ((uintptr_t) arg == 0)
{
rcnt = 1;
wcnt = 1;
}
do
{
if (wcnt)
{
xpthread_rwlock_wrlock (&lock);
xpthread_rwlock_unlock (&lock);
wcnt--;
}
if (rcnt)
{
xpthread_rwlock_rdlock (&lock);
xpthread_rwlock_unlock (&lock);
rcnt--;
}
}
while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0));
}
return NULL;
}
static int
do_test (void)
{
pthread_t thr[THREADS];
int n;
pthread_rwlockattr_t attr;
xpthread_rwlockattr_init (&attr);
xpthread_rwlockattr_setkind_np (&attr, KIND);
xpthread_rwlock_init (&lock, &attr);
/* Make standard error the same as standard output. */
dup2 (1, 2);
/* Make sure we see all message, even those on stdout. */
setvbuf (stdout, NULL, _IONBF, 0);
for (n = 0; n < THREADS; ++n)
thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n);
struct timespec delay;
delay.tv_sec = 10;
delay.tv_nsec = 0;
nanosleep (&delay, NULL);
atomic_store_relaxed (&done, 1);
/* Wait for all the threads. */
for (n = 0; n < THREADS; ++n)
xpthread_join (thr[n]);
return 0;
}
#include <support/test-driver.c>
+243
View File
@@ -0,0 +1,243 @@
/* Test that pthread_exit does not clobber callee-saved registers.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <support/check.h>
#include <support/xthread.h>
/* This test attempts to check that callee-saved registers are
restored to their original values when destructors are run after
pthread_exit is called. GCC PR 83641 causes this test to fail.
The constants have been chosen randomly and are magic values which
are used to detect whether registers have been clobbered. The idea
is that these values are hidden behind a compiler barrier and only
present in .rodata initially, so that it is less likely that they
are in a register by accident.
The checker class can be stored in registers, and the magic values
are directly loaded into these registers. The checker destructor
is eventually invoked by pthread_exit and calls one of the
check_magic functions to verify that the class contents (that is,
register value) is correct.
These tests are performed both for unsigned int and double values,
to cover different calling conventions. */
template <class T>
struct values
{
T v0;
T v1;
T v2;
T v3;
T v4;
};
static const values<unsigned int> magic_values =
{
0x57f7fc72,
0xe582daba,
0x5f6ac994,
0x35efddb7,
0x1fbf5a74,
};
static const values<double> magic_values_double =
{
0.6764041905675465,
0.9533336788140494,
0.6091161359041452,
0.7668653957125336,
0.010374520235509666,
};
/* Special index value which tells check_magic that no check should be
performed. */
enum { no_check = -1 };
/* Check that VALUE is the magic value for INDEX, behind a compiler
barrier. */
__attribute__ ((noinline, noclone, weak))
void
check_magic (int index, unsigned int value)
{
switch (index)
{
case 0:
TEST_COMPARE (value, magic_values.v0);
break;
case 1:
TEST_COMPARE (value, magic_values.v1);
break;
case 2:
TEST_COMPARE (value, magic_values.v2);
break;
case 3:
TEST_COMPARE (value, magic_values.v3);
break;
case 4:
TEST_COMPARE (value, magic_values.v4);
break;
case no_check:
break;
default:
FAIL_EXIT1 ("invalid magic value index %d", index);
}
}
/* Check that VALUE is the magic value for INDEX, behind a compiler
barrier. Double variant. */
__attribute__ ((noinline, noclone, weak))
void
check_magic (int index, double value)
{
switch (index)
{
case 0:
TEST_VERIFY (value == magic_values_double.v0);
break;
case 1:
TEST_VERIFY (value == magic_values_double.v1);
break;
case 2:
TEST_VERIFY (value == magic_values_double.v2);
break;
case 3:
TEST_VERIFY (value == magic_values_double.v3);
break;
case 4:
TEST_VERIFY (value == magic_values_double.v4);
break;
case no_check:
break;
default:
FAIL_EXIT1 ("invalid magic value index %d", index);
}
}
/* Store a magic value and check, via the destructor, that it has the
expected value. */
template <class T, int I>
struct checker
{
T value;
checker (T v)
: value (v)
{
}
~checker ()
{
check_magic (I, value);
}
};
/* The functions call_pthread_exit_0, call_pthread_exit_1,
call_pthread_exit are used to call pthread_exit indirectly, with
the intent of clobbering the register values. */
__attribute__ ((noinline, noclone, weak))
void
call_pthread_exit_0 (const values<unsigned int> *pvalues)
{
checker<unsigned int, no_check> c0 (pvalues->v0);
checker<unsigned int, no_check> c1 (pvalues->v1);
checker<unsigned int, no_check> c2 (pvalues->v2);
checker<unsigned int, no_check> c3 (pvalues->v3);
checker<unsigned int, no_check> c4 (pvalues->v4);
pthread_exit (NULL);
}
__attribute__ ((noinline, noclone, weak))
void
call_pthread_exit_1 (const values<double> *pvalues)
{
checker<double, no_check> c0 (pvalues->v0);
checker<double, no_check> c1 (pvalues->v1);
checker<double, no_check> c2 (pvalues->v2);
checker<double, no_check> c3 (pvalues->v3);
checker<double, no_check> c4 (pvalues->v4);
values<unsigned int> other_values = { 0, };
call_pthread_exit_0 (&other_values);
}
__attribute__ ((noinline, noclone, weak))
void
call_pthread_exit ()
{
values<double> other_values = { 0, };
call_pthread_exit_1 (&other_values);
}
/* Create on-stack objects and check that their values are restored by
pthread_exit. If Nested is true, call pthread_exit indirectly via
call_pthread_exit. */
template <class T, bool Nested>
__attribute__ ((noinline, noclone, weak))
void *
threadfunc (void *closure)
{
const values<T> *pvalues = static_cast<const values<T> *> (closure);
checker<T, 0> c0 (pvalues->v0);
checker<T, 1> c1 (pvalues->v1);
checker<T, 2> c2 (pvalues->v2);
checker<T, 3> c3 (pvalues->v3);
checker<T, 4> c4 (pvalues->v4);
if (Nested)
call_pthread_exit ();
else
pthread_exit (NULL);
/* This should not be reached. */
return const_cast<char *> ("");
}
static int
do_test ()
{
puts ("info: unsigned int, direct pthread_exit call");
pthread_t thr
= xpthread_create (NULL, &threadfunc<unsigned int, false>,
const_cast<values<unsigned int> *> (&magic_values));
TEST_VERIFY (xpthread_join (thr) == NULL);
puts ("info: double, direct pthread_exit call");
thr = xpthread_create (NULL, &threadfunc<double, false>,
const_cast<values<double> *> (&magic_values_double));
TEST_VERIFY (xpthread_join (thr) == NULL);
puts ("info: unsigned int, indirect pthread_exit call");
thr = xpthread_create (NULL, &threadfunc<unsigned int, true>,
const_cast<values<unsigned int> *> (&magic_values));
TEST_VERIFY (xpthread_join (thr) == NULL);
puts ("info: double, indirect pthread_exit call");
thr = xpthread_create (NULL, &threadfunc<double, true>,
const_cast<values<double> *> (&magic_values_double));
TEST_VERIFY (xpthread_join (thr) == NULL);
return 0;
}
#include <support/test-driver.c>
+9
View File
@@ -54,6 +54,12 @@ tests = test-netdb tst-nss-test1 test-digits-dots \
$(tests-static)
xtests = bug-erange
# Tests which need libdl
ifeq (yes,$(build-shared))
tests += tst-nss-files-hosts-erange
tests += tst-nss-files-hosts-multi
endif
# If we have a thread library then we can test cancellation against
# some routines like getpwuid_r.
ifeq (yes,$(have-thread-library))
@@ -135,3 +141,6 @@ $(objpfx)tst-nss-test1.out: $(objpfx)/libnss_test1.so$(libnss_test1.so-version)
ifeq (yes,$(have-thread-library))
$(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
endif
$(objpfx)tst-nss-files-hosts-erange: $(libdl)
$(objpfx)tst-nss-files-hosts-multi: $(libdl)
+217 -193
View File
@@ -22,6 +22,8 @@
#include <arpa/nameser.h>
#include <netdb.h>
#include <resolv/resolv-internal.h>
#include <scratch_buffer.h>
#include <alloc_buffer.h>
/* Get implementation for some internal functions. */
@@ -115,6 +117,219 @@ DB_LOOKUP (hostbyaddr, ,,,
}, const void *addr, socklen_t len, int af)
#undef EXTRA_ARGS_VALUE
/* Type of the address and alias arrays. */
#define DYNARRAY_STRUCT array
#define DYNARRAY_ELEMENT char *
#define DYNARRAY_PREFIX array_
#include <malloc/dynarray-skeleton.c>
static enum nss_status
gethostbyname3_multi (FILE * stream, const char *name, int af,
struct hostent *result, char *buffer, size_t buflen,
int *errnop, int *herrnop, int flags)
{
assert (af == AF_INET || af == AF_INET6);
/* We have to get all host entries from the file. */
struct scratch_buffer tmp_buffer;
scratch_buffer_init (&tmp_buffer);
struct hostent tmp_result_buf;
struct array addresses;
array_init (&addresses);
struct array aliases;
array_init (&aliases);
enum nss_status status;
/* Preserve the addresses and aliases encountered so far. */
for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)
array_add (&addresses, result->h_addr_list[i]);
for (size_t i = 0; result->h_aliases[i] != NULL; ++i)
array_add (&aliases, result->h_aliases[i]);
/* The output buffer re-uses now-unused space at the end of the
buffer, starting with the aliases array. It comes last in the
data produced by internal_getent. (The alias names themselves
are still located in the line read in internal_getent, which is
stored at the beginning of the buffer.) */
struct alloc_buffer outbuf;
{
char *bufferend = (char *) result->h_aliases;
outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);
}
while (true)
{
status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,
tmp_buffer.length, errnop, herrnop, af,
flags);
/* Enlarge the buffer if necessary. */
if (status == NSS_STATUS_TRYAGAIN && *herrnop == NETDB_INTERNAL
&& *errnop == ERANGE)
{
if (!scratch_buffer_grow (&tmp_buffer))
{
*errnop = ENOMEM;
/* *herrnop and status already have the right value. */
break;
}
/* Loop around and retry with a larger buffer. */
}
else if (status == NSS_STATUS_SUCCESS)
{
/* A line was read. Check that it matches the search
criteria. */
int matches = 1;
struct hostent *old_result = result;
result = &tmp_result_buf;
/* The following piece is a bit clumsy but we want to use
the `LOOKUP_NAME_CASE' value. The optimizer should do
its job. */
do
{
LOOKUP_NAME_CASE (h_name, h_aliases)
result = old_result;
}
while ((matches = 0));
/* If the line matches, we need to copy the addresses and
aliases, so that we can reuse tmp_buffer for the next
line. */
if (matches)
{
/* Record the addresses. */
for (size_t i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)
{
/* Allocate the target space in the output buffer,
depending on the address family. */
void *target;
if (af == AF_INET)
{
assert (tmp_result_buf.h_length == 4);
target = alloc_buffer_alloc (&outbuf, struct in_addr);
}
else if (af == AF_INET6)
{
assert (tmp_result_buf.h_length == 16);
target = alloc_buffer_alloc (&outbuf, struct in6_addr);
}
else
__builtin_unreachable ();
if (target == NULL)
{
/* Request a larger output buffer. */
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
break;
}
memcpy (target, tmp_result_buf.h_addr_list[i],
tmp_result_buf.h_length);
array_add (&addresses, target);
}
/* Record the aliases. */
for (size_t i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)
{
char *alias = tmp_result_buf.h_aliases[i];
array_add (&aliases,
alloc_buffer_copy_string (&outbuf, alias));
}
/* If the real name is different add, it also to the
aliases. This means that there is a duplication in
the alias list but this is really the user's
problem. */
{
char *new_name = tmp_result_buf.h_name;
if (strcmp (old_result->h_name, new_name) != 0)
array_add (&aliases,
alloc_buffer_copy_string (&outbuf, new_name));
}
/* Report memory allocation failures during the
expansion of the temporary arrays. */
if (array_has_failed (&addresses) || array_has_failed (&aliases))
{
*errnop = ENOMEM;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_UNAVAIL;
break;
}
/* Request a larger output buffer if we ran out of room. */
if (alloc_buffer_has_failed (&outbuf))
{
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
break;
}
result = old_result;
} /* If match was found. */
/* If no match is found, loop around and fetch another
line. */
} /* status == NSS_STATUS_SUCCESS. */
else
/* internal_getent returned an error. */
break;
} /* while (true) */
/* Propagate the NSS_STATUS_TRYAGAIN error to the caller. It means
that we may not have loaded the complete result.
NSS_STATUS_NOTFOUND, however, means that we reached the end of
the file successfully. */
if (status != NSS_STATUS_TRYAGAIN)
status = NSS_STATUS_SUCCESS;
if (status == NSS_STATUS_SUCCESS)
{
/* Copy the address and alias arrays into the output buffer and
add NULL terminators. The pointed-to elements were directly
written into the output buffer above and do not need to be
copied again. */
size_t addresses_count = array_size (&addresses);
size_t aliases_count = array_size (&aliases);
char **out_addresses = alloc_buffer_alloc_array
(&outbuf, char *, addresses_count + 1);
char **out_aliases = alloc_buffer_alloc_array
(&outbuf, char *, aliases_count + 1);
if (out_addresses == NULL || out_aliases == NULL)
{
/* The output buffer is not large enough. */
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
/* Fall through to function exit. */
}
else
{
/* Everything is allocated in place. Make the copies and
adjust the array pointers. */
memcpy (out_addresses, array_begin (&addresses),
addresses_count * sizeof (char *));
out_addresses[addresses_count] = NULL;
memcpy (out_aliases, array_begin (&aliases),
aliases_count * sizeof (char *));
out_aliases[aliases_count] = NULL;
result->h_addr_list = out_addresses;
result->h_aliases = out_aliases;
status = NSS_STATUS_SUCCESS;
}
}
scratch_buffer_free (&tmp_buffer);
array_free (&addresses);
array_free (&aliases);
return status;
}
enum nss_status
_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen, int *errnop,
@@ -143,199 +358,8 @@ _nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
if (status == NSS_STATUS_SUCCESS
&& _res_hconf.flags & HCONF_FLAG_MULTI)
{
/* We have to get all host entries from the file. */
size_t tmp_buflen = MIN (buflen, 4096);
char tmp_buffer_stack[tmp_buflen]
__attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
char *tmp_buffer = tmp_buffer_stack;
struct hostent tmp_result_buf;
int naddrs = 1;
int naliases = 0;
char *bufferend;
bool tmp_buffer_malloced = false;
while (result->h_aliases[naliases] != NULL)
++naliases;
bufferend = (char *) &result->h_aliases[naliases + 1];
again:
while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer,
tmp_buflen, errnop, herrnop, af,
flags))
== NSS_STATUS_SUCCESS)
{
int matches = 1;
struct hostent *old_result = result;
result = &tmp_result_buf;
/* The following piece is a bit clumsy but we want to use the
`LOOKUP_NAME_CASE' value. The optimizer should do its
job. */
do
{
LOOKUP_NAME_CASE (h_name, h_aliases)
result = old_result;
}
while ((matches = 0));
if (matches)
{
/* We could be very clever and try to recycle a few bytes
in the buffer instead of generating new arrays. But
we are not doing this here since it's more work than
it's worth. Simply let the user provide a bit bigger
buffer. */
char **new_h_addr_list;
char **new_h_aliases;
int newaliases = 0;
size_t newstrlen = 0;
int cnt;
/* Count the new aliases and the length of the strings. */
while (tmp_result_buf.h_aliases[newaliases] != NULL)
{
char *cp = tmp_result_buf.h_aliases[newaliases];
++newaliases;
newstrlen += strlen (cp) + 1;
}
/* If the real name is different add it also to the
aliases. This means that there is a duplication
in the alias list but this is really the user's
problem. */
if (strcmp (old_result->h_name,
tmp_result_buf.h_name) != 0)
{
++newaliases;
newstrlen += strlen (tmp_result_buf.h_name) + 1;
}
/* Make sure bufferend is aligned. */
assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
/* Now we can check whether the buffer is large enough.
16 is the maximal size of the IP address. */
if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
+ roundup (newstrlen, sizeof (char *))
+ (naliases + newaliases + 1) * sizeof (char *)
>= buffer + buflen)
{
*errnop = ERANGE;
*herrnop = NETDB_INTERNAL;
status = NSS_STATUS_TRYAGAIN;
goto out;
}
new_h_addr_list =
(char **) (bufferend
+ roundup (newstrlen, sizeof (char *))
+ 16);
new_h_aliases =
(char **) ((char *) new_h_addr_list
+ (naddrs + 2) * sizeof (char *));
/* Copy the old data in the new arrays. */
for (cnt = 0; cnt < naddrs; ++cnt)
new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
for (cnt = 0; cnt < naliases; ++cnt)
new_h_aliases[cnt] = old_result->h_aliases[cnt];
/* Store the new strings. */
cnt = 0;
while (tmp_result_buf.h_aliases[cnt] != NULL)
{
new_h_aliases[naliases++] = bufferend;
bufferend = (__stpcpy (bufferend,
tmp_result_buf.h_aliases[cnt])
+ 1);
++cnt;
}
if (cnt < newaliases)
{
new_h_aliases[naliases++] = bufferend;
bufferend = __stpcpy (bufferend,
tmp_result_buf.h_name) + 1;
}
/* Final NULL pointer. */
new_h_aliases[naliases] = NULL;
/* Round up the buffer end address. */
bufferend += (sizeof (char *)
- ((bufferend - (char *) 0)
% sizeof (char *))) % sizeof (char *);
/* Now the new address. */
new_h_addr_list[naddrs++] =
memcpy (bufferend, tmp_result_buf.h_addr,
tmp_result_buf.h_length);
/* Also here a final NULL pointer. */
new_h_addr_list[naddrs] = NULL;
/* Store the new array pointers. */
old_result->h_aliases = new_h_aliases;
old_result->h_addr_list = new_h_addr_list;
/* Compute the new buffer end. */
bufferend = (char *) &new_h_aliases[naliases + 1];
assert (bufferend <= buffer + buflen);
result = old_result;
}
}
if (status == NSS_STATUS_TRYAGAIN)
{
size_t newsize = 2 * tmp_buflen;
if (tmp_buffer_malloced)
{
char *newp = realloc (tmp_buffer, newsize);
if (newp != NULL)
{
assert ((((uintptr_t) newp)
& (__alignof__ (struct hostent_data) - 1))
== 0);
tmp_buffer = newp;
tmp_buflen = newsize;
goto again;
}
}
else if (!__libc_use_alloca (buflen + newsize))
{
tmp_buffer = malloc (newsize);
if (tmp_buffer != NULL)
{
assert ((((uintptr_t) tmp_buffer)
& (__alignof__ (struct hostent_data) - 1))
== 0);
tmp_buffer_malloced = true;
tmp_buflen = newsize;
goto again;
}
}
else
{
tmp_buffer
= extend_alloca (tmp_buffer, tmp_buflen,
newsize
+ __alignof__ (struct hostent_data));
tmp_buffer = (char *) (((uintptr_t) tmp_buffer
+ __alignof__ (struct hostent_data)
- 1)
& ~(__alignof__ (struct hostent_data)
- 1));
goto again;
}
}
else
status = NSS_STATUS_SUCCESS;
out:
if (tmp_buffer_malloced)
free (tmp_buffer);
}
status = gethostbyname3_multi
(stream, name, af, result, buffer, buflen, errnop, herrnop, flags);
internal_endent (&stream);
}
+109
View File
@@ -0,0 +1,109 @@
/* Parse /etc/hosts in multi mode with a trailing long line (bug 21915).
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <errno.h>
#include <gnu/lib-names.h>
#include <netdb.h>
#include <nss.h>
#include <support/check.h>
#include <support/check_nss.h>
#include <support/namespace.h>
#include <support/test-driver.h>
#include <support/xunistd.h>
struct support_chroot *chroot_env;
#define X10 "XXXXXXXXXX"
#define X100 X10 X10 X10 X10 X10 X10 X10 X10 X10 X10
#define X1000 X100 X100 X100 X100 X100 X100 X100 X100 X100 X100
static void
prepare (int argc, char **argv)
{
chroot_env = support_chroot_create
((struct support_chroot_configuration)
{
.resolv_conf = "",
.hosts =
"127.0.0.1 localhost localhost.localdomain\n"
"::1 localhost localhost.localdomain\n"
"192.0.2.1 example.com\n"
"#" X1000 X100 "\n",
.host_conf = "multi on\n",
});
}
static int
do_test (void)
{
support_become_root ();
if (!support_can_chroot ())
return EXIT_UNSUPPORTED;
__nss_configure_lookup ("hosts", "files");
if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
xchroot (chroot_env->path_chroot);
errno = ERANGE;
h_errno = NETDB_INTERNAL;
check_hostent ("gethostbyname example.com",
gethostbyname ("example.com"),
"name: example.com\n"
"address: 192.0.2.1\n");
errno = ERANGE;
h_errno = NETDB_INTERNAL;
check_hostent ("gethostbyname2 AF_INET example.com",
gethostbyname2 ("example.com", AF_INET),
"name: example.com\n"
"address: 192.0.2.1\n");
{
struct addrinfo hints =
{
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
errno = ERANGE;
h_errno = NETDB_INTERNAL;
struct addrinfo *ai;
int ret = getaddrinfo ("example.com", "80", &hints, &ai);
check_addrinfo ("example.com AF_UNSPEC", ai, ret,
"address: STREAM/TCP 192.0.2.1 80\n");
if (ret == 0)
freeaddrinfo (ai);
hints.ai_family = AF_INET;
errno = ERANGE;
h_errno = NETDB_INTERNAL;
ret = getaddrinfo ("example.com", "80", &hints, &ai);
check_addrinfo ("example.com AF_INET", ai, ret,
"address: STREAM/TCP 192.0.2.1 80\n");
if (ret == 0)
freeaddrinfo (ai);
}
support_chroot_free (chroot_env);
return 0;
}
#define PREPARE prepare
#include <support/test-driver.c>
+331
View File
@@ -0,0 +1,331 @@
/* Parse /etc/hosts in multi mode with many addresses/aliases.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <errno.h>
#include <gnu/lib-names.h>
#include <netdb.h>
#include <nss.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/check_nss.h>
#include <support/namespace.h>
#include <support/support.h>
#include <support/test-driver.h>
#include <support/test-driver.h>
#include <support/xmemstream.h>
#include <support/xstdio.h>
#include <support/xunistd.h>
#include <sys/resource.h>
struct support_chroot *chroot_env;
static void
prepare (int argc, char **argv)
{
chroot_env = support_chroot_create
((struct support_chroot_configuration)
{
.resolv_conf = "",
.hosts = "", /* See write_hosts below. */
.host_conf = "multi on\n",
});
}
/* Create the /etc/hosts file from outside the chroot. */
static void
write_hosts (int count)
{
TEST_VERIFY (count > 0 && count <= 65535);
FILE *fp = xfopen (chroot_env->path_hosts, "w");
fputs ("127.0.0.1 localhost localhost.localdomain\n"
"::1 localhost localhost.localdomain\n",
fp);
for (int i = 0; i < count; ++i)
{
fprintf (fp, "10.4.%d.%d www4.example.com\n",
(i / 256) & 0xff, i & 0xff);
fprintf (fp, "10.46.%d.%d www.example.com\n",
(i / 256) & 0xff, i & 0xff);
fprintf (fp, "192.0.2.1 alias.example.com v4-%d.example.com\n", i);
fprintf (fp, "2001:db8::6:%x www6.example.com\n", i);
fprintf (fp, "2001:db8::46:%x www.example.com\n", i);
fprintf (fp, "2001:db8::1 alias.example.com v6-%d.example.com\n", i);
}
xfclose (fp);
}
/* Parameters of a single test. */
struct test_params
{
const char *name; /* Name to query. */
const char *marker; /* Address marker for the name. */
int count; /* Number of addresses/aliases. */
int family; /* AF_INET, AF_INET_6 or AF_UNSPEC. */
bool canonname; /* True if AI_CANONNAME should be enabled. */
};
/* Expected result of gethostbyname/gethostbyname2. */
static char *
expected_ghbn (const struct test_params *params)
{
TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);
struct xmemstream expected;
xopen_memstream (&expected);
if (strcmp (params->name, "alias.example.com") == 0)
{
fprintf (expected.out, "name: %s\n", params->name);
char af;
if (params->family == AF_INET)
af = '4';
else
af = '6';
for (int i = 0; i < params->count; ++i)
fprintf (expected.out, "alias: v%c-%d.example.com\n", af, i);
for (int i = 0; i < params->count; ++i)
if (params->family == AF_INET)
fputs ("address: 192.0.2.1\n", expected.out);
else
fputs ("address: 2001:db8::1\n", expected.out);
}
else /* www/www4/www6 name. */
{
bool do_ipv4 = params->family == AF_INET
&& strncmp (params->name, "www6", 4) != 0;
bool do_ipv6 = params->family == AF_INET6
&& strncmp (params->name, "www4", 4) != 0;
if (do_ipv4 || do_ipv6)
{
fprintf (expected.out, "name: %s\n", params->name);
if (do_ipv4)
for (int i = 0; i < params->count; ++i)
fprintf (expected.out, "address: 10.%s.%d.%d\n",
params->marker, i / 256, i % 256);
if (do_ipv6)
for (int i = 0; i < params->count; ++i)
fprintf (expected.out, "address: 2001:db8::%s:%x\n",
params->marker, i);
}
else
fputs ("error: HOST_NOT_FOUND\n", expected.out);
}
xfclose_memstream (&expected);
return expected.buffer;
}
/* Expected result of getaddrinfo. */
static char *
expected_gai (const struct test_params *params)
{
bool do_ipv4 = false;
bool do_ipv6 = false;
if (params->family == AF_UNSPEC)
do_ipv4 = do_ipv6 = true;
else if (params->family == AF_INET)
do_ipv4 = true;
else if (params->family == AF_INET6)
do_ipv6 = true;
struct xmemstream expected;
xopen_memstream (&expected);
if (strcmp (params->name, "alias.example.com") == 0)
{
if (params->canonname)
fprintf (expected.out,
"flags: AI_CANONNAME\n"
"canonname: %s\n",
params->name);
if (do_ipv4)
for (int i = 0; i < params->count; ++i)
fputs ("address: STREAM/TCP 192.0.2.1 80\n", expected.out);
if (do_ipv6)
for (int i = 0; i < params->count; ++i)
fputs ("address: STREAM/TCP 2001:db8::1 80\n", expected.out);
}
else /* www/www4/www6 name. */
{
if (strncmp (params->name, "www4", 4) == 0)
do_ipv6 = false;
else if (strncmp (params->name, "www6", 4) == 0)
do_ipv4 = false;
/* Otherwise, we have www as the name, so we do both. */
if (do_ipv4 || do_ipv6)
{
if (params->canonname)
fprintf (expected.out,
"flags: AI_CANONNAME\n"
"canonname: %s\n",
params->name);
if (do_ipv4)
for (int i = 0; i < params->count; ++i)
fprintf (expected.out, "address: STREAM/TCP 10.%s.%d.%d 80\n",
params->marker, i / 256, i % 256);
if (do_ipv6)
for (int i = 0; i < params->count; ++i)
fprintf (expected.out,
"address: STREAM/TCP 2001:db8::%s:%x 80\n",
params->marker, i);
}
else
fputs ("error: Name or service not known\n", expected.out);
}
xfclose_memstream (&expected);
return expected.buffer;
}
static void
run_gbhn_gai (struct test_params *params)
{
char *ctx = xasprintf ("name=%s marker=%s count=%d family=%d",
params->name, params->marker, params->count,
params->family);
if (test_verbose > 0)
printf ("info: %s\n", ctx);
/* Check gethostbyname, gethostbyname2. */
if (params->family == AF_INET)
{
char *expected = expected_ghbn (params);
check_hostent (ctx, gethostbyname (params->name), expected);
free (expected);
}
if (params->family != AF_UNSPEC)
{
char *expected = expected_ghbn (params);
check_hostent (ctx, gethostbyname2 (params->name, params->family),
expected);
free (expected);
}
/* Check getaddrinfo. */
for (int do_canonical = 0; do_canonical < 2; ++do_canonical)
{
params->canonname = do_canonical;
char *expected = expected_gai (params);
struct addrinfo hints =
{
.ai_family = params->family,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
if (do_canonical)
hints.ai_flags |= AI_CANONNAME;
struct addrinfo *ai;
int ret = getaddrinfo (params->name, "80", &hints, &ai);
check_addrinfo (ctx, ai, ret, expected);
if (ret == 0)
freeaddrinfo (ai);
free (expected);
}
free (ctx);
}
/* Callback for the subprocess which runs the test in a chroot. */
static void
subprocess (void *closure)
{
struct test_params *params = closure;
xchroot (chroot_env->path_chroot);
static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };
static const char *const names[] =
{
"www.example.com", "www4.example.com", "www6.example.com",
"alias.example.com",
NULL
};
static const char *const names_marker[] = { "46", "4", "6", "" };
for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)
{
params->family = families[family_idx];
for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)
{
params->name = names[names_idx];
params->marker = names_marker[names_idx];
run_gbhn_gai (params);
}
}
}
/* Run the test for a specific number of addresses/aliases. */
static void
run_test (int count)
{
write_hosts (count);
struct test_params params =
{
.count = count,
};
support_isolate_in_subprocess (subprocess, &params);
}
static int
do_test (void)
{
support_become_root ();
if (!support_can_chroot ())
return EXIT_UNSUPPORTED;
/* This test should not use gigabytes of memory. */
{
struct rlimit limit;
if (getrlimit (RLIMIT_AS, &limit) != 0)
{
printf ("getrlimit (RLIMIT_AS) failed: %m\n");
return 1;
}
long target = 200 * 1024 * 1024;
if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
{
limit.rlim_cur = target;
if (setrlimit (RLIMIT_AS, &limit) != 0)
{
printf ("setrlimit (RLIMIT_AS) failed: %m\n");
return 1;
}
}
}
__nss_configure_lookup ("hosts", "files");
if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
/* Run the tests with a few different address/alias counts. */
for (int count = 1; count <= 111; ++count)
run_test (count);
run_test (1111);
run_test (22222);
support_chroot_free (chroot_env);
return 0;
}
#define PREPARE prepare
#include <support/test-driver.c>
+11 -3
View File
@@ -43,7 +43,7 @@ routines := \
getpgid setpgid getpgrp bsd-getpgrp setpgrp getsid setsid \
getresuid getresgid setresuid setresgid \
pathconf sysconf fpathconf \
glob glob64 fnmatch regex \
glob glob64 globfree globfree64 glob_pattern_p fnmatch regex \
confstr \
getopt getopt1 getopt_init \
sched_setp sched_getp sched_sets sched_gets sched_yield sched_primax \
@@ -91,7 +91,8 @@ tests := tstgetopt testfnm runtests runptests \
tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \
tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \
tst-posix_spawn-fd \
tst-posix_fadvise tst-posix_fadvise64
tst-posix_fadvise tst-posix_fadvise64 \
tst-glob-tilde
xtests := bug-ga2
ifeq (yes,$(build-shared))
test-srcs := globtest
@@ -134,7 +135,8 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \
$(objpfx)tst-rxspencer-no-utf8-mem.out $(objpfx)tst-pcre-mem.out \
$(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \
$(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \
$(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out
$(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \
$(objpfx)tst-glob-tilde-mem.out
xtests-special += $(objpfx)bug-ga2-mem.out
endif
@@ -341,6 +343,12 @@ $(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out
$(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \
$(evaluate-test)
tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace
$(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \
$(evaluate-test)
$(inst_libexecdir)/getconf: $(inst_bindir)/getconf \
$(objpfx)getconf.speclist FORCE
$(addprefix $(..)./scripts/mkinstalldirs ,\
+45
View File
@@ -0,0 +1,45 @@
/* Sizes of structs with flexible array members.
Copyright 2016-2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>.
Written by Paul Eggert. */
#include <stddef.h>
/* Nonzero multiple of alignment of TYPE, suitable for FLEXSIZEOF below.
On older platforms without _Alignof, use a pessimistic bound that is
safe in practice even if FLEXIBLE_ARRAY_MEMBER is 1.
On newer platforms, use _Alignof to get a tighter bound. */
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
# define FLEXALIGNOF(type) (sizeof (type) & ~ (sizeof (type) - 1))
#else
# define FLEXALIGNOF(type) _Alignof (type)
#endif
/* Upper bound on the size of a struct of type TYPE with a flexible
array member named MEMBER that is followed by N bytes of other data.
This is not simply sizeof (TYPE) + N, since it may require
alignment on unusually picky C11 platforms, and
FLEXIBLE_ARRAY_MEMBER may be 1 on pre-C11 platforms.
Yield a value less than N if and only if arithmetic overflow occurs. */
#define FLEXSIZEOF(type, member, n) \
((offsetof (type, member) + FLEXALIGNOF (type) - 1 + (n)) \
& ~ (FLEXALIGNOF (type) - 1))
+373 -407
View File
File diff suppressed because it is too large Load Diff
-6
View File
@@ -43,10 +43,4 @@ glob64 (const char *pattern, int flags,
}
libc_hidden_def (glob64)
void
globfree64 (glob64_t *pglob)
{
}
libc_hidden_def (globfree64)
stub_warning (glob64)
+57
View File
@@ -0,0 +1,57 @@
/* Shared definition for glob and glob_pattern_p.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef GLOB_INTERNAL_H
# define GLOB_INTERNAL_H
static inline int
__glob_pattern_type (const char *pattern, int quote)
{
const char *p;
int ret = 0;
for (p = pattern; *p != '\0'; ++p)
switch (*p)
{
case '?':
case '*':
return 1;
case '\\':
if (quote)
{
if (p[1] != '\0')
++p;
ret |= 2;
}
break;
case '[':
ret |= 4;
break;
case ']':
if (ret & 4)
return 1;
break;
}
return ret;
}
#endif /* GLOB_INTERNAL_H */
+33
View File
@@ -0,0 +1,33 @@
/* Return nonzero if PATTERN contains any metacharacters.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _LIBC
# include <config.h>
#endif
#include <glob.h>
#include "glob_internal.h"
/* Return nonzero if PATTERN contains any metacharacters.
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
int
__glob_pattern_p (const char *pattern, int quote)
{
return __glob_pattern_type (pattern, quote) == 1;
}
weak_alias (__glob_pattern_p, glob_pattern_p)
+41
View File
@@ -0,0 +1,41 @@
/* Frees the dynamically allocated storage from an earlier call to glob.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _LIBC
# include <config.h>
#endif
#include <glob.h>
#include <stdlib.h>
/* Free storage allocated in PGLOB by a previous `glob' call. */
void
globfree (glob_t *pglob)
{
if (pglob->gl_pathv != NULL)
{
size_t i;
for (i = 0; i < pglob->gl_pathc; ++i)
free (pglob->gl_pathv[pglob->gl_offs + i]);
free (pglob->gl_pathv);
pglob->gl_pathv = NULL;
}
}
#ifndef globfree
libc_hidden_def (globfree)
#endif
+31
View File
@@ -0,0 +1,31 @@
/* Frees the dynamically allocated storage from an earlier call to glob.
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _LIBC
# include <config.h>
#endif
#include <glob.h>
#include <stdlib.h>
/* Free storage allocated in PGLOB by a previous `glob' call. */
void
globfree64 (glob64_t *pglob)
{
}
libc_hidden_def (globfree64)
+6 -3
View File
@@ -47,7 +47,12 @@ testout=${common_objpfx}posix/globtest-out
rm -rf $testdir $testout
mkdir $testdir
trap 'chmod 777 $testdir/noread; rm -fr $testdir $testout' 1 2 3 15
cleanup() {
chmod 777 $testdir/noread
rm -fr $testdir $testout
}
trap cleanup 0 HUP INT QUIT TERM
echo 1 > $testdir/file1
echo 2 > $testdir/file2
@@ -811,8 +816,6 @@ if test $failed -ne 0; then
fi
if test $result -eq 0; then
chmod 777 $testdir/noread
rm -fr $testdir $testout
echo "All OK." > $logfile
fi
+143
View File
@@ -0,0 +1,143 @@
/* Check for GLOB_TIDLE heap allocation issues (bugs 22320, 22325, 22332).
Copyright (C) 2017 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <glob.h>
#include <mcheck.h>
#include <nss.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/support.h>
/* Flag which indicates whether to pass the GLOB_ONLYDIR flag. */
static int do_onlydir;
/* Flag which indicates whether to pass the GLOB_NOCHECK flag. */
static int do_nocheck;
/* Flag which indicates whether to pass the GLOB_MARK flag. */
static int do_mark;
/* Flag which indicates whether to pass the GLOB_NOESCAPE flag. */
static int do_noescape;
static void
one_test (const char *prefix, const char *middle, const char *suffix)
{
char *pattern = xasprintf ("%s%s%s", prefix, middle, suffix);
int flags = GLOB_TILDE;
if (do_onlydir)
flags |= GLOB_ONLYDIR;
if (do_nocheck)
flags |= GLOB_NOCHECK;
if (do_mark)
flags |= GLOB_MARK;
if (do_noescape)
flags |= GLOB_NOESCAPE;
glob_t gl;
/* This glob call might result in crashes or memory leaks. */
if (glob (pattern, flags, NULL, &gl) == 0)
globfree (&gl);
free (pattern);
}
enum
{
/* The largest base being tested. */
largest_base_size = 500000,
/* The actual size is the base size plus a variable whose absolute
value is not greater than this. This helps malloc to trigger
overflows. */
max_size_skew = 16,
/* The maximum string length supported by repeating_string
below. */
repeat_size = largest_base_size + max_size_skew,
};
/* Used to construct strings which repeat a single character 'x'. */
static char *repeat;
/* Return a string of SIZE characters. */
const char *
repeating_string (int size)
{
TEST_VERIFY (size >= 0);
TEST_VERIFY (size <= repeat_size);
const char *repeated_shifted = repeat + repeat_size - size;
TEST_VERIFY (strlen (repeated_shifted) == size);
return repeated_shifted;
}
static int
do_test (void)
{
/* Avoid network-based NSS modules and initialize nss_files with a
dummy lookup. This has to come before mtrace because NSS does
not free all memory. */
__nss_configure_lookup ("passwd", "files");
(void) getpwnam ("root");
mtrace ();
repeat = xmalloc (repeat_size + 1);
memset (repeat, 'x', repeat_size);
repeat[repeat_size] = '\0';
/* These numbers control the size of the user name. The values
cover the minimum (0), a typical size (8), a large
stack-allocated size (100000), and a somewhat large
heap-allocated size (largest_base_size). */
static const int base_sizes[] = { 0, 8, 100, 100000, largest_base_size, -1 };
for (do_onlydir = 0; do_onlydir < 2; ++do_onlydir)
for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck)
for (do_mark = 0; do_mark < 2; ++do_mark)
for (do_noescape = 0; do_noescape < 2; ++do_noescape)
for (int base_idx = 0; base_sizes[base_idx] >= 0; ++base_idx)
{
for (int size_skew = -max_size_skew; size_skew <= max_size_skew;
++size_skew)
{
int size = base_sizes[base_idx] + size_skew;
if (size < 0)
continue;
const char *user_name = repeating_string (size);
one_test ("~", user_name, "/a/b");
one_test ("~", user_name, "x\\x\\x////x\\a");
}
const char *user_name = repeating_string (base_sizes[base_idx]);
one_test ("~", user_name, "");
one_test ("~", user_name, "/");
one_test ("~", user_name, "/a");
one_test ("~", user_name, "/*/*");
one_test ("~", user_name, "\\/");
one_test ("/~", user_name, "");
one_test ("*/~", user_name, "/a/b");
}
free (repeat);
return 0;
}
#include <support/test-driver.c>

Some files were not shown because too many files have changed in this diff Show More