mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 11:58:23 +08:00
Compare commits
148
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f28f188021 | ||
|
|
2417ddb645 | ||
|
|
3c15600e79 | ||
|
|
6fc6b074ea | ||
|
|
3d443b8d6a | ||
|
|
b1241520a0 | ||
|
|
ba7a5e3e0f | ||
|
|
b6e9f21071 | ||
|
|
76d9e44c59 | ||
|
|
2a7a9d3ac8 | ||
|
|
d04b63770f | ||
|
|
bb7c57219d | ||
|
|
83ce856e77 | ||
|
|
f578da1020 | ||
|
|
d4386d345e | ||
|
|
66b1fe1d4f | ||
|
|
5ac4f45c1b | ||
|
|
114e3349de | ||
|
|
986e7b911f | ||
|
|
6da40102c7 | ||
|
|
f22d1cb932 | ||
|
|
b2b761bc47 | ||
|
|
5b5dd5a43d | ||
|
|
b307c13ef3 | ||
|
|
e68db8bf5a | ||
|
|
efe736ebe7 | ||
|
|
8c21bd940e | ||
|
|
9e69d744b3 | ||
|
|
428e34eab5 | ||
|
|
a486152569 | ||
|
|
28689d6255 | ||
|
|
352cb39fa0 | ||
|
|
5d94c86a47 | ||
|
|
7aac95739a | ||
|
|
a0ed5893fc | ||
|
|
ac1d6f25d6 | ||
|
|
f9fe740ec4 | ||
|
|
dd92fa3029 | ||
|
|
2599967449 | ||
|
|
ccf4e0edde | ||
|
|
3cee1ddad2 | ||
|
|
cba2fbe17f | ||
|
|
0a3b2efccc | ||
|
|
c51e9501c2 | ||
|
|
5c38877df5 | ||
|
|
8fa2afcec3 | ||
|
|
0717959eb3 | ||
|
|
3fc179e2b2 | ||
|
|
c3535cb6cd | ||
|
|
aff5eec9ce | ||
|
|
7056607e6a | ||
|
|
a54fae8df4 | ||
|
|
674137c9b0 | ||
|
|
5be8a84721 | ||
|
|
757d90ff37 | ||
|
|
9650d04fb6 | ||
|
|
2f4a98ab17 | ||
|
|
c90c70b8f9 | ||
|
|
c671b231cf | ||
|
|
be07b3e059 | ||
|
|
2f3fb944b3 | ||
|
|
be6fb78a1f | ||
|
|
646ec4ebe5 | ||
|
|
edceb7f520 | ||
|
|
2c9083f93d | ||
|
|
dcfcb1b208 | ||
|
|
1927d02d19 | ||
|
|
2c23a4d7e0 | ||
|
|
23d0e97fe5 | ||
|
|
ad25ea0c47 | ||
|
|
e9c0d7d7ff | ||
|
|
a9acd88a5d | ||
|
|
1864775abc | ||
|
|
420ade1f64 | ||
|
|
8493ba72b1 | ||
|
|
a3c78954ee | ||
|
|
28ff0f650c | ||
|
|
be3eaffd5a | ||
|
|
c969e84e0c | ||
|
|
53d501d6e9 | ||
|
|
64458aabeb | ||
|
|
58c6a7ae53 | ||
|
|
2fb2098c24 | ||
|
|
83aaa17144 | ||
|
|
0a4d3eac67 | ||
|
|
b0d3f7858c | ||
|
|
a318448f7a | ||
|
|
9aaebaf805 | ||
|
|
8e5d591b10 | ||
|
|
0474cd5de6 | ||
|
|
e0a0770bb4 | ||
|
|
ea6f2c3174 | ||
|
|
42786ee476 | ||
|
|
0b4c3e1e0b | ||
|
|
44a61d4589 | ||
|
|
5422ac2d08 | ||
|
|
2626b15e88 | ||
|
|
845278f2c6 | ||
|
|
a683450f26 | ||
|
|
1b96d1d90b | ||
|
|
4618f1ffba | ||
|
|
c6e4c3198b | ||
|
|
a0a551d259 | ||
|
|
0ad788face | ||
|
|
9a3ff995bd | ||
|
|
8646009efd | ||
|
|
a7646651f8 | ||
|
|
52a6381659 | ||
|
|
f1f24cdeba | ||
|
|
c1803823c6 | ||
|
|
5e1548a6d9 | ||
|
|
afbf970cae | ||
|
|
a132a2c305 | ||
|
|
91372f0001 | ||
|
|
1ad15e008c | ||
|
|
ef98313dd3 | ||
|
|
6d8eaf4a25 | ||
|
|
23ef51a50a | ||
|
|
2d3fefd7ce | ||
|
|
4163c382f0 | ||
|
|
53f48f845c | ||
|
|
a00dc2a18f | ||
|
|
da347f4aa3 | ||
|
|
34fb5f61d3 | ||
|
|
2ec0b166bf | ||
|
|
bd0a325b6a | ||
|
|
95d66fecaa | ||
|
|
980cb43f15 | ||
|
|
e3f828b8bd | ||
|
|
c6177be4b9 | ||
|
|
52b7cd6e9a | ||
|
|
eaea1dfbe9 | ||
|
|
f62d21a1f0 | ||
|
|
0744a268bc | ||
|
|
42dfc13abf | ||
|
|
dcd2b97dd1 | ||
|
|
0941350c20 | ||
|
|
e28ad442e7 | ||
|
|
6eb48fe80c | ||
|
|
10dd17da71 | ||
|
|
4d0b1b0f61 | ||
|
|
bc6f839fb4 | ||
|
|
067fc32968 | ||
|
|
c096b008d2 | ||
|
|
44113a8ba2 | ||
|
|
2de15ac957 | ||
|
|
726a78867b | ||
|
|
86013ef5ce |
@@ -1,3 +1,500 @@
|
||||
2019-02-03 Aurelien Jarno <aurelien@aurel32.net>
|
||||
|
||||
* stdlib/isomac.c: Include <unistd.h>.
|
||||
|
||||
2019-06-06 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
* sysdeps/unix/sysv/linux/riscv/flush-icache.c: Do not use
|
||||
internal GCC preprocessor identifier __has_include__.
|
||||
|
||||
2019-07-24 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24532]
|
||||
Linux: Use in-tree copy of SO_ constants for !__USE_MISC.
|
||||
* sysdeps/unix/sysv/linux/Makefile [$(subdir) == socket]
|
||||
(sysdep_headers): Add bits/socket-constants.h.
|
||||
(tests-special): Add tst-socket-consts.out.
|
||||
(tst-socket-consts.out): New target.
|
||||
* sysdeps/unix/sysv/linux/bits/socket.h: Remove macro tracking
|
||||
around <asm/unistd.h>.
|
||||
[__USE_MISC]: Include <bits/types/time_t.h> before <asm/socket.h>.
|
||||
[!__USE_MISC]: Include <sys/socket-constants.h> instead of
|
||||
<asm/socket.h>.
|
||||
* sysdeps/unix/sysv/linux/bits/tst-socket-consts.py: New file.
|
||||
* sysdeps/unix/sysv/linux/bits/socket-constants.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/alpha/bits/socket-constants.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/hppa/bits/socket-constants.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/mips/bits/socket-constants.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/powerpc/bits/socket-constants.h:
|
||||
Likewise.
|
||||
* sysdeps/unix/sysv/linux/sparc/bits/socket-constants.h: Likewise.
|
||||
|
||||
2019-08-15 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
* malloc/Makefile (tests): Only add tst-mxfast for
|
||||
$(have-tunables).
|
||||
* malloc/tst-mxfast.c: Fix copyright year.
|
||||
(do_test): Fix GNU style issues. Use TEST_COMPARE instead of
|
||||
assert for checks.
|
||||
|
||||
2019-08-09 DJ Delorie <dj@redhat.com>
|
||||
|
||||
* elf/dl-tunables.list: Add glibc.malloc.mxfast.
|
||||
* manual/tunables.texi: Document it.
|
||||
* malloc/malloc.c (do_set_mxfast): New.
|
||||
(__libc_mallopt): Call it.
|
||||
* malloc/arena.c: Add mxfast tunable.
|
||||
* malloc/tst-mxfast.c: New.
|
||||
* malloc/Makefile: Add it.
|
||||
|
||||
2019-08-08 Niklas Hambüchen <mail@nh2.me>
|
||||
Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
[BZ #24026]
|
||||
* malloc/malloc.c (__malloc_info): Account for top chunk.
|
||||
|
||||
2019-08-01 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24867]
|
||||
* malloc/malloc.c (__malloc_info): Remove unwanted leading
|
||||
whitespace.
|
||||
|
||||
2019-05-17 Wilco Dijkstra <wdijkstr@arm.com>
|
||||
|
||||
* malloc/malloc.c (MAX_TCACHE_COUNT): Increase to UINT16_MAX.
|
||||
(tcache_put): Remove redundant assert.
|
||||
(tcache_get): Remove redundant asserts.
|
||||
(__libc_malloc): Check tcache count is not zero.
|
||||
* manual/tunables.texi (glibc.malloc.tcache_count): Update maximum.
|
||||
|
||||
2019-02-04 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
* malloc/malloc.c (tcache_get): Compare tcache->counts[tc_idx]
|
||||
with 0, not tcache->entries[tc_idx].
|
||||
|
||||
2019-02-06 Stefan Liebler <stli@linux.ibm.com>
|
||||
|
||||
[BZ #23403]
|
||||
* nptl/allocatestack.c (allocate_stack): Align pointer pd for
|
||||
TLS_TCB_AT_TP tls variant.
|
||||
* nptl/tst-tls1.c: Migrate to support/test-driver.c.
|
||||
Add alignment checks.
|
||||
* support/Makefile (libsupport-routines): Add xposix_memalign and
|
||||
xpthread_setstack.
|
||||
* support/support.h: Add xposix_memalign.
|
||||
* support/xthread.h: Add xpthread_attr_setstack.
|
||||
* support/xposix_memalign.c: New File.
|
||||
* support/xpthread_attr_setstack.c: Likewise.
|
||||
|
||||
2019-06-18 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24323]
|
||||
* include/elf.h (DT_1_SUPPORTED_MASK): Include DF_1_PIE.
|
||||
* elf/dl-load.c (_dl_map_object_from_fd): Check for DF_1_PIE and
|
||||
fail when called from dlopen.
|
||||
* elf/Makefile [have-fpie && build-shared] (tests): Add
|
||||
tst-dlopen-pie.
|
||||
(tst-dlopen-pie): Link with -ldl.
|
||||
(tst-dlopen-pie.out): Add run-time dependency on tst-pie1.
|
||||
* elf/tst-dlopen-pie.c (do_test): New file.
|
||||
|
||||
2019-07-10 DJ Delorie <dj@redhat.com>
|
||||
Sergei Trofimovich <slyfox@inbox.ru>
|
||||
|
||||
[BZ #24696]
|
||||
[BZ #24695]
|
||||
* nss/nss_db/db-open.c (internal_endent): Protect against NULL
|
||||
mappings.
|
||||
* nss/tst-nss-db-endgrent.c: New.
|
||||
* nss/tst-nss-db-endgrent.root: New.
|
||||
* nss/tst-nss-db-endpwent.c: New.
|
||||
* nss/tst-nss-db-endpwent.root: New.
|
||||
* nss/Makefile: Add new tests.
|
||||
* support/links-dso-program-c.c: Add selinux dependency.
|
||||
* support/links-dso-program.cc: Add selinux dependency.
|
||||
* support/Makefile: Build those with -lselinux if enabled.
|
||||
|
||||
2019-05-14 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
* support/Makefile (CFLAGS-support_paths.c): Add -DBINDIR_PATH.
|
||||
* support/support.h (support_bindir_prefix): New variable.
|
||||
* support/support_paths.c [BINDIR_PATH] (support_bindir_prefix):
|
||||
|
||||
* config.make.in (bindir): New variable.
|
||||
|
||||
2019-07-01 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
[BZ #24259]
|
||||
* elf/dl-open.c (dl_open_worker): Call _dl_open_check after
|
||||
relocation.
|
||||
* sysdeps/x86/Makefile (tests): Add tst-cet-legacy-5a,
|
||||
tst-cet-legacy-5b, tst-cet-legacy-6a and tst-cet-legacy-6b.
|
||||
(modules-names): Add tst-cet-legacy-mod-5a, tst-cet-legacy-mod-5b,
|
||||
tst-cet-legacy-mod-5c, tst-cet-legacy-mod-6a, tst-cet-legacy-mod-6b
|
||||
and tst-cet-legacy-mod-6c.
|
||||
(CFLAGS-tst-cet-legacy-5a.c): New.
|
||||
(CFLAGS-tst-cet-legacy-5b.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-5a.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-5b.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-5c.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-6a.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-6b.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-6a.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-6b.c): Likewise.
|
||||
(CFLAGS-tst-cet-legacy-mod-6c.c): Likewise.
|
||||
($(objpfx)tst-cet-legacy-5a): Likewise.
|
||||
($(objpfx)tst-cet-legacy-5a.out): Likewise.
|
||||
($(objpfx)tst-cet-legacy-mod-5a.so): Likewise.
|
||||
($(objpfx)tst-cet-legacy-mod-5b.so): Likewise.
|
||||
($(objpfx)tst-cet-legacy-5b): Likewise.
|
||||
($(objpfx)tst-cet-legacy-5b.out): Likewise.
|
||||
(tst-cet-legacy-5b-ENV): Likewise.
|
||||
($(objpfx)tst-cet-legacy-6a): Likewise.
|
||||
($(objpfx)tst-cet-legacy-6a.out): Likewise.
|
||||
($(objpfx)tst-cet-legacy-mod-6a.so): Likewise.
|
||||
($(objpfx)tst-cet-legacy-mod-6b.so): Likewise.
|
||||
($(objpfx)tst-cet-legacy-6b): Likewise.
|
||||
($(objpfx)tst-cet-legacy-6b.out): Likewise.
|
||||
(tst-cet-legacy-6b-ENV): Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-5.c: New file.
|
||||
* sysdeps/x86/tst-cet-legacy-5a.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-5b.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-6.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-6a.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-6b.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-5.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-5a.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-5b.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-5c.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-6.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-6a.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-6b.c: Likewise.
|
||||
* sysdeps/x86/tst-cet-legacy-mod-6c.c: Likewise.
|
||||
|
||||
2019-09-20 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
* sysdeps/unix/sysv/linux/riscv/vfork.S: Do not include
|
||||
<linux/sched.h>.
|
||||
(CLONE_VM): New macro.
|
||||
(CLONE_VFORK): Likewise.
|
||||
|
||||
2019-09-14 Aurelien Jarno <aurelien@aurel32.net>
|
||||
|
||||
[BZ #24986]
|
||||
* sysdeps/unix/alpha/getegid.S: Move to ...
|
||||
* sysdeps/unix/sysv/linux/alpha/getegid.S: ... here.
|
||||
* sysdeps/unix/alpha/geteuid.S: Move to ...
|
||||
* sysdeps/unix/sysv/linux/alpha/geteuid.S: ... here.
|
||||
* sysdeps/unix/alpha/getppid.S: Move to ...
|
||||
* sysdeps/unix/sysv/linux/alpha/getppid.S: ... here
|
||||
|
||||
2019-09-13 Wilco Dijkstra <wdijkstr@arm.com>
|
||||
|
||||
* string/memmem.c (__memmem): Rewrite to improve performance.
|
||||
|
||||
2019-06-12 Wilco Dijkstra <wdijkstr@arm.com>
|
||||
|
||||
* string/str-two-way.h (two_way_short_needle): Add inline to avoid
|
||||
warning.
|
||||
(two_way_long_needle): Block inlining.
|
||||
* string/strstr.c (strstr2): Add new function.
|
||||
(strstr3): Likewise.
|
||||
(STRSTR): Completely rewrite strstr to improve performance.
|
||||
|
||||
2019-09-03 Aurelien Jarno <aurelien@aurel32.net>
|
||||
|
||||
* sysdeps/alpha/fpu/libm-test-ulps: Regenerated using GCC 9.2.
|
||||
|
||||
2019-08-18 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
* sysdeps/hppa/fpu/libm-test-ulps: Update.
|
||||
|
||||
2019-08-15 Richard Henderson <rth@twiddle.net>
|
||||
|
||||
* sysdeps/unix/sysv/linux/alpha/kernel-features.h (__NR_shmat):
|
||||
Do not redefine.
|
||||
* sysdeps/unix/sysv/linux/alpha/sysdep.h (__NR_osf_shmat):
|
||||
Do not redefine.
|
||||
|
||||
2019-07-15 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
[BZ #24699]
|
||||
* posix/tst-mmap-offset.c: Mention BZ #24699.
|
||||
(do_test_bz21270): Rename to do_test_large_offset and use
|
||||
mmap64_maximum_offset to check for maximum expected offset value.
|
||||
* sysdeps/generic/mmap_info.h: New file.
|
||||
* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
|
||||
* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
|
||||
__NR_mmap2 is used.
|
||||
|
||||
2019-07-12 Szabolcs Nagy <szabolcs.nagy@arm.com>
|
||||
|
||||
* sysdeps/aarch64/dl-machine.h (elf_machine_lazy_rel): Check
|
||||
STO_AARCH64_VARIANT_PCS and bind such symbols at load time.
|
||||
|
||||
2019-06-13 Szabolcs Nagy <szabolcs.nagy@arm.com>
|
||||
|
||||
* elf/elf.h (STO_AARCH64_VARIANT_PCS): Define.
|
||||
(DT_AARCH64_VARIANT_PCS): Define.
|
||||
|
||||
2019-06-28 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24744]
|
||||
io: Remove the copy_file_range emulation.
|
||||
* sysdeps/unix/sysv/linux/copy_file_range.c (copy_file_range): Do
|
||||
not define and call copy_file_range_compat.
|
||||
* io/Makefile (tests-static, tests-internal): Do not add
|
||||
tst-copy_file_range-compat.
|
||||
* io/copy_file_range-compat.c: Remove file.
|
||||
* io/copy_file_range.c (copy_file_range): Define as stub.
|
||||
* io/tst-copy_file_range-compat.c: Remove file.
|
||||
* io/tst-copy_file_range.c (xdevfile): Remove variable.
|
||||
(typical_sizes): Update comment. Remove 16K sizes.
|
||||
(maximum_offset, maximum_offset_errno, maximum_offset_hard_limit):
|
||||
Remove variables.
|
||||
(find_maximum_offset, pipe_as_source, pipe_as_destination)
|
||||
(delayed_write_failure_beginning, delayed_write_failure_end)
|
||||
(cross_device_failure, enospc_failure_1, enospc_failure)
|
||||
(oappend_failure): Remove functions.
|
||||
(tests): Adjust test case list.
|
||||
(do_test): Remove file system search code. Check for ENOSYS from
|
||||
copy_file_range. Do not free xdevfile.
|
||||
* manual/llio.texi (Copying File Data): Document ENOSYS error from
|
||||
copy_file_range. Do not document the EXDEV error, which future
|
||||
kernels may not report. Update the wording to reflect that
|
||||
further errors are possible.
|
||||
* sysdeps/unix/sysv/linux/alpha/kernel-features.h
|
||||
[__LINUX_KERNEL_VERSION < 0x040D00] (__ASSUME_COPY_FILE_RANGE): Do
|
||||
not undefine.
|
||||
* sysdeps/unix/sysv/linux/arm/kernel-features.h
|
||||
[__LINUX_KERNEL_VERSION < 0x040700] (__ASSUME_COPY_FILE_RANGE):
|
||||
Likewise.
|
||||
* sysdeps/unix/sysv/linux/kernel-features.h
|
||||
[__LINUX_KERNEL_VERSION >= 0x040500] (__ASSUME_COPY_FILE_RANGE):
|
||||
Remove definition.
|
||||
* sysdeps/unix/sysv/linux/microblaze/kernel-features.h
|
||||
[__LINUX_KERNEL_VERSION < 0x040A00] (__ASSUME_COPY_FILE_RANGE): Do
|
||||
not undefine.
|
||||
* sysdeps/unix/sysv/linux/sh/kernel-features.h
|
||||
[__LINUX_KERNEL_VERSION < 0x040800] (__ASSUME_COPY_FILE_RANGE):
|
||||
Likewise.
|
||||
|
||||
2019-06-20 Dmitry V. Levin <ldv@altlinux.org>
|
||||
Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24228]
|
||||
* libio/genops.c (_IO_unbuffer_all)
|
||||
[SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)]: Do not attempt to free wide
|
||||
buffers and access _IO_FILE_complete members of legacy libio streams.
|
||||
* libio/tst-bz24228.c: New file.
|
||||
* libio/tst-bz24228.map: Likewise.
|
||||
* libio/Makefile [build-shared] (tests): Add tst-bz24228.
|
||||
[build-shared] (generated): Add tst-bz24228.mtrace and
|
||||
tst-bz24228.check.
|
||||
[run-built-tests && build-shared] (tests-special): Add
|
||||
$(objpfx)tst-bz24228-mem.out.
|
||||
(LDFLAGS-tst-bz24228, tst-bz24228-ENV): New variables.
|
||||
($(objpfx)tst-bz24228-mem.out): New rule.
|
||||
|
||||
2019-03-13 Zack Weinberg <zackw@panix.com>
|
||||
|
||||
* scripts/check-obsolete-constructs.py: New test script.
|
||||
* scripts/check-installed-headers.sh: Remove tests for
|
||||
obsolete typedefs, superseded by check-obsolete-constructs.py.
|
||||
* Rules: Run scripts/check-obsolete-constructs.py over $(headers)
|
||||
as a special test. Update commentary.
|
||||
* posix/bits/types.h (__SQUAD_TYPE, __S64_TYPE): Define as __int64_t.
|
||||
(__UQUAD_TYPE, __U64_TYPE): Define as __uint64_t.
|
||||
Update commentary.
|
||||
* posix/sys/types.h (__u_intN_t): Remove.
|
||||
(u_int8_t): Typedef using __uint8_t.
|
||||
(u_int16_t): Typedef using __uint16_t.
|
||||
(u_int32_t): Typedef using __uint32_t.
|
||||
(u_int64_t): Typedef using __uint64_t.
|
||||
|
||||
2019-04-18 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
* malloc/tst-memalign.c (do_test): Disable
|
||||
-Walloc-size-larger-than= around tests of malloc with negative
|
||||
sizes.
|
||||
* malloc/tst-malloc-too-large.c (do_test): Likewise.
|
||||
|
||||
2019-05-22 Wilco Dijkstra <wdijkstr@arm.com>
|
||||
|
||||
[BZ #24531]
|
||||
* malloc/malloc.c (MAX_TCACHE_COUNT): New define.
|
||||
(do_set_tcache_count): Only update if count is small enough.
|
||||
* manual/tunables.texi (glibc.malloc.tcache_count): Document max value.
|
||||
|
||||
2019-05-15 Mark Wielaard <mark@klomp.org>
|
||||
|
||||
[BZ#24476]
|
||||
* dlfcn/dlerror.c (__dlerror_main_freeres): Guard using
|
||||
__libc_once_get (once) and static_buf == NULL.
|
||||
(__dlerror): Check we have a valid key, set result to static_buf
|
||||
otherwise.
|
||||
|
||||
2019-05-15 Andreas Schwab <schwab@suse.de>
|
||||
|
||||
[BZ #20568]
|
||||
* libio/wfileops.c (_IO_wfile_sync): Correct last argument to
|
||||
__codecvt_do_length.
|
||||
* libio/Makefile (tests): Add tst-wfile-sync.
|
||||
($(objpfx)tst-wfile-sync.out): Depend on $(gen-locales).
|
||||
* libio/tst-wfile-sync.c: New file.
|
||||
* libio/tst-wfile-sync.input: New file.
|
||||
|
||||
2019-04-23 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
[BZ #18035]
|
||||
* elf/Makefile (tests-container): Add tst-pldd.
|
||||
* elf/pldd-xx.c: Use _Static_assert in of pldd_assert.
|
||||
(E(find_maps)): Avoid use alloca, use default read file operations
|
||||
instead of explicit LFS names, and fix infinite loop.
|
||||
* elf/pldd.c: Explicit set _FILE_OFFSET_BITS, cleanup headers.
|
||||
(get_process_info): Use _Static_assert instead of assert, use default
|
||||
directory operations instead of explicit LFS names, and free some
|
||||
leadek pointers.
|
||||
* elf/tst-pldd.c: New file.
|
||||
|
||||
2019-04-17 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
* support/Makefile (libsupport-routines): Add support_subprocess,
|
||||
xposix_spawn, xposix_spawn_file_actions_addclose, and
|
||||
xposix_spawn_file_actions_adddup2.
|
||||
(tst-support_capture_subprocess-ARGS): New rule.
|
||||
* support/capture_subprocess.h (support_capture_subprogram): New
|
||||
prototype.
|
||||
* support/support_capture_subprocess.c (support_capture_subprocess):
|
||||
Refactor to use support_subprocess and support_capture_poll.
|
||||
(support_capture_subprogram): New function.
|
||||
* support/tst-support_capture_subprocess.c (write_mode_to_str,
|
||||
str_to_write_mode, test_common, parse_int, handle_restart,
|
||||
do_subprocess, do_subprogram, do_multiple_tests): New functions.
|
||||
(do_test): Add support_capture_subprogram tests.
|
||||
* support/subprocess.h: New file.
|
||||
* support/support_subprocess.c: Likewise.
|
||||
* support/xposix_spawn.c: Likewise.
|
||||
* support/xposix_spawn_file_actions_addclose.c: Likewise.
|
||||
* support/xposix_spawn_file_actions_adddup2.c: Likewise.
|
||||
* support/xspawn.h: Likewise.
|
||||
|
||||
2019-04-09 Carlos O'Donell <carlos@redhat.com>
|
||||
Kwok Cheung Yeung <kcy@codesourcery.com>
|
||||
|
||||
[BZ #16573]
|
||||
* malloc/mtrace.c: Define prototypes for all hooks.
|
||||
(set_default_hooks): New function.
|
||||
(set_trace_hooks): Likewise.
|
||||
(save_default_hooks): Likewise.
|
||||
(tr_freehook): Use new s*_hooks functions.
|
||||
(tr_mallochook): Likewise.
|
||||
(tr_reallochook): Likewise.
|
||||
(tr_memalignhook): Likewise.
|
||||
(mtrace): Likewise.
|
||||
(muntrace): Likewise.
|
||||
|
||||
2019-04-02 TAMUKI Shoichi <tamuki@linet.gr.jp>
|
||||
|
||||
[BZ #22964]
|
||||
* localedata/locales/ja_JP (LC_TIME): Add entry for the new Japanese
|
||||
era.
|
||||
|
||||
2019-03-02 TAMUKI Shoichi <tamuki@linet.gr.jp>
|
||||
|
||||
[BZ #24162]
|
||||
* localedata/locales/ja_JP (LC_TIME): Change the offset for Taisho
|
||||
gan-nen from 2 to 1. Problem reported by Morimitsu, Junji.
|
||||
|
||||
2019-03-21 Stefan Liebler <stli@linux.ibm.com>
|
||||
|
||||
* sysdeps/s390/dl-procinfo.h (HWCAP_IMPORTANT):
|
||||
Add HWCAP_S390_VX and HWCAP_S390_VXE.
|
||||
|
||||
2019-01-31 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
CVE-2019-9169
|
||||
regex: fix read overrun [BZ #24114]
|
||||
Problem found by AddressSanitizer, reported by Hongxu Chen in:
|
||||
https://debbugs.gnu.org/34140
|
||||
* posix/regexec.c (proceed_next_node):
|
||||
Do not read past end of input buffer.
|
||||
|
||||
2019-03-13 Stefan Liebler <stli@linux.ibm.com>
|
||||
|
||||
* elf/dl-sysdep.c (_dl_show_auxv): Remove condition and always
|
||||
call _dl_procinfo.
|
||||
* sysdeps/unix/sysv/linux/s390/dl-procinfo.h (_dl_procinfo):
|
||||
Ignore types other than AT_HWCAP.
|
||||
* sysdeps/sparc/dl-procinfo.h (_dl_procinfo): Likewise.
|
||||
* sysdeps/unix/sysv/linux/i386/dl-procinfo.h (_dl_procinfo):
|
||||
Likewise.
|
||||
* sysdeps/powerpc/dl-procinfo.h (_dl_procinfo): Adjust comment
|
||||
in the case of falling back to generic output mechanism.
|
||||
* sysdeps/unix/sysv/linux/arm/dl-procinfo.h (_dl_procinfo):
|
||||
Likewise.
|
||||
|
||||
2019-02-15 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24211]
|
||||
* nptl/pthread_join_common.c (__pthread_timedjoin_ex): Do not read
|
||||
pd->result after the thread descriptor has been freed.
|
||||
|
||||
2019-02-08 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24161]
|
||||
* sysdeps/nptl/fork.h (__run_fork_handlers): Add multiple_threads
|
||||
argument.
|
||||
* nptl/register-atfork.c (__run_fork_handlers): Only perform
|
||||
locking if the new do_locking argument is true.
|
||||
* sysdeps/nptl/fork.c (__libc_fork): Pass multiple_threads to
|
||||
__run_fork_handlers.
|
||||
|
||||
2019-02-07 Stefan Liebler <stli@linux.ibm.com>
|
||||
|
||||
[BZ #24180]
|
||||
* nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock):
|
||||
Add compiler barriers and comments.
|
||||
|
||||
2019-02-05 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #24164]
|
||||
arm: Use "nr" constraint for Systemtap probes, to avoid the
|
||||
compiler using memory operands for constants, due to the "o"
|
||||
alternative in the default "nor" constraint.
|
||||
* include/stap-probe.h [USE_STAP_PROBE]: Include
|
||||
<stap-probe-machine.h>
|
||||
* sysdeps/generic/stap-probe-machine.h: New file.
|
||||
* sysdeps/arm/stap-probe-machine.h: Likewise.
|
||||
|
||||
2019-02-04 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
[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.
|
||||
|
||||
2019-01-31 Carlos O'Donell <carlos@redhat.com>
|
||||
Torvald Riegel <triegel@redhat.com>
|
||||
Rik Prohaska <prohaska7@gmail.com>
|
||||
|
||||
[BZ# 23844]
|
||||
* nptl/Makefile (tests): Add tst-rwlock-tryrdlock-stall, and
|
||||
tst-rwlock-trywrlock-stall.
|
||||
* nptl/pthread_rwlock_tryrdlock.c (__pthread_rwlock_tryrdlock):
|
||||
Wake waiters if PTHREAD_RWLOCK_FUTEX_USED is set.
|
||||
* nptl/pthread_rwlock_trywrlock.c (__pthread_rwlock_trywrlock):
|
||||
Set __wrphase_fute to 1 only if we started the write phase.
|
||||
* nptl/tst-rwlock-tryrdlock-stall.c: New file.
|
||||
* nptl/tst-rwlock-trywrlock-stall.c: New file.
|
||||
* support/Makefile (libsupport-routines): Add xpthread_rwlock_destroy.
|
||||
* support/xpthread_rwlock_destroy.c: New file.
|
||||
* support/xthread.h: Declare xpthread_rwlock_destroy.
|
||||
|
||||
2019-01-31 Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
|
||||
* version.h (RELEASE): Set to "stable".
|
||||
|
||||
+23
-2
@@ -42,6 +42,22 @@ else
|
||||
objdir must be defined by the build-directory Makefile.
|
||||
endif
|
||||
|
||||
# Did we request 'make -s' run? "yes" or "no".
|
||||
# Starting from make-4.4 MAKEFLAGS now contains long
|
||||
# options like '--shuffle'. To detect presence of 's'
|
||||
# we pick first word with short options. Long options
|
||||
# are guaranteed to come after whitespace. We use '-'
|
||||
# prefix to always have a word before long options
|
||||
# even if no short options were passed.
|
||||
# Typical MAKEFLAGS values to watch for:
|
||||
# "rs --shuffle=42" (silent)
|
||||
# " --shuffle" (not silent)
|
||||
ifeq ($(findstring s, $(firstword -$(MAKEFLAGS))),)
|
||||
silent-make := no
|
||||
else
|
||||
silent-make := yes
|
||||
endif
|
||||
|
||||
# Root of the sysdeps tree.
|
||||
sysdep_dir := $(..)sysdeps
|
||||
export sysdep_dir := $(sysdep_dir)
|
||||
@@ -546,8 +562,13 @@ ifeq (yes,$(build-shared))
|
||||
link-libc-rpath = -Wl,-rpath=$(rpath-link)
|
||||
link-libc-rpath-link = -Wl,-rpath-link=$(rpath-link)
|
||||
|
||||
# Tests use -Wl,-rpath instead of -Wl,-rpath-link for
|
||||
# build-hardcoded-path-in-tests. Add -Wl,--disable-new-dtags to force
|
||||
# DT_RPATH instead of DT_RUNPATH which only applies to DT_NEEDED entries
|
||||
# in the executable and doesn't applies to DT_NEEDED entries in shared
|
||||
# libraries which are loaded via DT_NEEDED entries in the executable.
|
||||
ifeq (yes,$(build-hardcoded-path-in-tests))
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath)
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath) -Wl,--disable-new-dtags
|
||||
else
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath-link)
|
||||
endif # build-hardcoded-path-in-tests
|
||||
@@ -880,7 +901,7 @@ endif
|
||||
# umpteen zillion filenames along with it (we use `...' instead)
|
||||
# but we don't want this echoing done when the user has said
|
||||
# he doesn't want to see commands echoed by using -s.
|
||||
ifneq "$(findstring s,$(MAKEFLAGS))" "" # if -s
|
||||
ifeq ($(silent-make),yes) # if -s
|
||||
+cmdecho := echo >/dev/null
|
||||
else # not -s
|
||||
+cmdecho := echo
|
||||
|
||||
@@ -395,7 +395,8 @@ ifeq ($(run-built-tests),yes)
|
||||
$(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
|
||||
done
|
||||
endif
|
||||
$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
|
||||
$(MAKE) install DESTDIR=$(objpfx)testroot.pristine \
|
||||
subdirs='$(all-subdirs)'
|
||||
touch $(objpfx)testroot.pristine/install.stamp
|
||||
|
||||
tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
|
||||
|
||||
@@ -805,7 +805,7 @@ endif
|
||||
# Maximize efficiency by minimizing the number of rules.
|
||||
.SUFFIXES: # Clear the suffix list. We don't use suffix rules.
|
||||
# Don't define any builtin rules.
|
||||
MAKEFLAGS := $(MAKEFLAGS)r
|
||||
MAKEFLAGS := $(MAKEFLAGS) -r
|
||||
|
||||
# Generic rule for making directories.
|
||||
%/:
|
||||
@@ -822,7 +822,7 @@ MAKEFLAGS := $(MAKEFLAGS)r
|
||||
.PRECIOUS: $(foreach l,$(libtypes),$(patsubst %,$(common-objpfx)$l,c))
|
||||
|
||||
# Use the verbose option of ar and tar when not running silently.
|
||||
ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s
|
||||
ifeq ($(silent-make),no) # if not -s
|
||||
verbose := v
|
||||
else # -s
|
||||
verbose :=
|
||||
|
||||
@@ -4,6 +4,77 @@ See the end for copying conditions.
|
||||
|
||||
Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
|
||||
using `glibc' in the "product" field.
|
||||
|
||||
Version 2.29.1
|
||||
|
||||
Major new features:
|
||||
|
||||
* The entry for the new Japanese era has been added for ja_JP locale.
|
||||
|
||||
Deprecated and removed features, and other changes affecting compatibility:
|
||||
|
||||
* The copy_file_range function fails with ENOSYS if the kernel does not
|
||||
support the system call of the same name. Previously, user space
|
||||
emulation was performed, but its behavior did not match the kernel
|
||||
behavior, which was deemed too confusing. Applications which use the
|
||||
copy_file_range function will have to be run on kernels which implement
|
||||
the copy_file_range system call. Support for most architectures was added
|
||||
in version 4.5 of the mainline Linux kernel.
|
||||
|
||||
The following bugs are resolved with this release:
|
||||
|
||||
[14231] stdio-common tests memory requirements
|
||||
[16573] malloc: Set and reset all hooks for tracing
|
||||
[18035] Fix pldd hang
|
||||
[20019] NULL pointer dereference in libc.so.6 IFUNC due to uninitialized GOT
|
||||
[20568] Fix crash in _IO_wfile_sync
|
||||
[24155] x32 memcmp can treat positive length as 0 (if sign bit in RDX is set) (CVE-2019-7309)
|
||||
[24164] Systemtap probes need to use "nr" constraint on 32-bit Arm
|
||||
[24161] __run_fork_handlers self-deadlocks in malloc/tst-mallocfork2
|
||||
[24211] Use-after-free in Systemtap probe in pthread_join
|
||||
[24228] old x86 applications that use legacy libio crash on exit
|
||||
[24476] dlfcn: Guard __dlerror_main_freeres with __libc_once_get (once)
|
||||
[24744] io: Remove the copy_file_range emulation
|
||||
[24794] Partial test suite run builds corrupt test-in-container testroot
|
||||
[24986] alpha: new getegid, geteuid and getppid syscalls used
|
||||
unconditionally
|
||||
[25203] libio: Disable vtable validation for pre-2.1 interposed handles
|
||||
[25204] Ignore LD_PREFER_MAP_32BIT_EXEC for SUID programs
|
||||
[25225] ld.so fails to link on x86 if GCC defaults to -fcf-protection
|
||||
[25232] No const correctness for strchr et al. for Clang++
|
||||
[25691] stdio: Remove memory leak from multibyte conversion
|
||||
[25933] Off by one error in __strncmp_avx2
|
||||
[25976] nss_compat: internal_end*ent may clobber errno, hiding ERANGE
|
||||
[26211] printf integer overflow calculating allocation size
|
||||
[27130] "rep movsb" performance issue
|
||||
[27177] GLIBC_TUNABLES=glibc.cpu.x86_ibt=on:glibc.cpu.x86_shstk=on doesn't work
|
||||
[27457] vzeroupper use in AVX2 multiarch string functions cause HTM aborts
|
||||
[27974] Overflow bug in some implementation of wcsnlen, wmemchr, and wcsncat
|
||||
[28755] overflow bug in wcsncmp_avx2 and wcsncmp_evex
|
||||
[28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm
|
||||
variants when avoiding overflow
|
||||
[29530] segfault in printf handling thousands separator
|
||||
|
||||
Security related changes:
|
||||
|
||||
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-9169: Attempted case-insensitive regular-expression match
|
||||
via proceed_next_node in posix/regexec.c leads to heap-based buffer
|
||||
over-read. Reported by Hongxu Chen.
|
||||
|
||||
CVE-2019-19126: ld.so failed to ignore the LD_PREFER_MAP_32BIT_EXEC
|
||||
environment variable during program execution after a security
|
||||
transition, allowing local attackers to restrict the possible mapping
|
||||
addresses for loaded libraries and thus bypass ASLR for a setuid
|
||||
program. Reported by Marcin Kościelnicki.
|
||||
|
||||
|
||||
Version 2.29
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ $(common-objpfx)dummy.c:
|
||||
common-generated += dummy.o dummy.c
|
||||
|
||||
ifneq "$(headers)" ""
|
||||
# Special test of all the installed headers in this directory.
|
||||
# Test that all of the headers installed by this directory can be compiled
|
||||
# in isolation.
|
||||
tests-special += $(objpfx)check-installed-headers-c.out
|
||||
libof-check-installed-headers-c := testsuite
|
||||
$(objpfx)check-installed-headers-c.out: \
|
||||
@@ -93,6 +94,8 @@ $(objpfx)check-installed-headers-c.out: \
|
||||
$(evaluate-test)
|
||||
|
||||
ifneq "$(CXX)" ""
|
||||
# If a C++ compiler is available, also test that they can be compiled
|
||||
# in isolation as C++.
|
||||
tests-special += $(objpfx)check-installed-headers-cxx.out
|
||||
libof-check-installed-headers-cxx := testsuite
|
||||
$(objpfx)check-installed-headers-cxx.out: \
|
||||
|
||||
@@ -3777,11 +3777,32 @@ else
|
||||
fi
|
||||
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
#ifndef __CET__
|
||||
#error no CET compiler support
|
||||
#endif
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
libc_cv_compiler_default_cet=yes
|
||||
else
|
||||
libc_cv_compiler_default_cet=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
|
||||
# Check whether --enable-cet was given.
|
||||
if test "${enable_cet+set}" = set; then :
|
||||
enableval=$enable_cet; enable_cet=$enableval
|
||||
else
|
||||
enable_cet=no
|
||||
enable_cet=$libc_cv_compiler_default_cet
|
||||
fi
|
||||
|
||||
|
||||
|
||||
+8
-1
@@ -472,11 +472,18 @@ AC_ARG_ENABLE([mathvec],
|
||||
[build_mathvec=$enableval],
|
||||
[build_mathvec=notset])
|
||||
|
||||
AC_TRY_COMPILE([], [
|
||||
#ifndef __CET__
|
||||
# error no CET compiler support
|
||||
#endif],
|
||||
[libc_cv_compiler_default_cet=yes],
|
||||
[libc_cv_compiler_default_cet=no])
|
||||
|
||||
AC_ARG_ENABLE([cet],
|
||||
AC_HELP_STRING([--enable-cet],
|
||||
[enable Intel Control-flow Enforcement Technology (CET), x86 only]),
|
||||
[enable_cet=$enableval],
|
||||
[enable_cet=no])
|
||||
[enable_cet=$libc_cv_compiler_default_cet])
|
||||
|
||||
# We keep the original values in `$config_*' and never modify them, so we
|
||||
# can write them unchanged into config.make. Everything else uses
|
||||
|
||||
@@ -168,6 +168,8 @@ extra-libs-others = $(extra-libs)
|
||||
|
||||
libSegFault-routines = segfault
|
||||
libSegFault-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
# libSegFault.so installs a signal handler in its ELF constructor.
|
||||
LDFLAGS-SegFault.so = -Wl,--enable-new-dtags,-z,nodelete
|
||||
|
||||
libpcprofile-routines = pcprofile
|
||||
libpcprofile-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
|
||||
@@ -89,6 +89,18 @@ handle_signal (int signum)
|
||||
}
|
||||
/* Symbol names are not available for static functions, so we do not
|
||||
check do_test. */
|
||||
|
||||
/* Check that backtrace does not return more than what fits in the array
|
||||
(bug 25423). */
|
||||
for (int j = 0; j < NUM_FUNCTIONS; j++)
|
||||
{
|
||||
n = backtrace (addresses, j);
|
||||
if (n > j)
|
||||
{
|
||||
FAIL ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NO_INLINE int
|
||||
|
||||
+21
-8
@@ -72,9 +72,16 @@ __dlerror (void)
|
||||
__libc_once (once, init);
|
||||
|
||||
/* Get error string. */
|
||||
result = (struct dl_action_result *) __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
result = &last_result;
|
||||
if (static_buf != NULL)
|
||||
result = static_buf;
|
||||
else
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
result = (struct dl_action_result *) __libc_getspecific (key);
|
||||
if (result == NULL)
|
||||
result = &last_result;
|
||||
}
|
||||
|
||||
/* Test whether we already returned the string. */
|
||||
if (result->returned != 0)
|
||||
@@ -230,13 +237,19 @@ free_key_mem (void *mem)
|
||||
void
|
||||
__dlerror_main_freeres (void)
|
||||
{
|
||||
void *mem;
|
||||
/* Free the global memory if used. */
|
||||
check_free (&last_result);
|
||||
/* Free the TSD memory if used. */
|
||||
mem = __libc_getspecific (key);
|
||||
if (mem != NULL)
|
||||
free_key_mem (mem);
|
||||
|
||||
if (__libc_once_get (once) && static_buf == NULL)
|
||||
{
|
||||
/* init () has been run and we don't use the static buffer.
|
||||
So we have a valid key. */
|
||||
void *mem;
|
||||
/* Free the TSD memory if used. */
|
||||
mem = __libc_getspecific (key);
|
||||
if (mem != NULL)
|
||||
free_key_mem (mem);
|
||||
}
|
||||
}
|
||||
|
||||
struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
|
||||
|
||||
+6
-3
@@ -194,6 +194,7 @@ tests-internal += loadtest unload unload2 circleload1 \
|
||||
tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
|
||||
tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
|
||||
tst-create_format1
|
||||
tests-container += tst-pldd
|
||||
ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
tests += tst-dlopen-aout
|
||||
tst-dlopen-aout-no-pie = yes
|
||||
@@ -304,7 +305,7 @@ test-xfail-tst-protected1b = yes
|
||||
endif
|
||||
ifeq (yesyes,$(have-fpie)$(build-shared))
|
||||
modules-names += tst-piemod1
|
||||
tests += tst-pie1 tst-pie2
|
||||
tests += tst-pie1 tst-pie2 tst-dlopen-pie
|
||||
tests-pie += tst-pie1 tst-pie2
|
||||
ifeq (yes,$(have-protected-data))
|
||||
tests += vismain
|
||||
@@ -1065,6 +1066,8 @@ CFLAGS-tst-pie2.c += $(pie-ccflag)
|
||||
|
||||
$(objpfx)tst-piemod1.so: $(libsupport)
|
||||
$(objpfx)tst-pie1: $(objpfx)tst-piemod1.so
|
||||
$(objpfx)tst-dlopen-pie: $(libdl)
|
||||
$(objpfx)tst-dlopen-pie.out: $(objpfx)tst-pie1
|
||||
|
||||
ifeq (yes,$(build-shared))
|
||||
# NB: Please keep cet-built-dso in sysdeps/x86/Makefile in sync with
|
||||
@@ -1276,6 +1279,8 @@ CFLAGS-ifuncmain6pie.c += $(pie-ccflag)
|
||||
CFLAGS-ifuncmain7pie.c += $(pie-ccflag)
|
||||
CFLAGS-tst-ifunc-textrel.c += $(pic-ccflag)
|
||||
|
||||
LDFLAGS-ifuncmain6pie = -Wl,-z,lazy
|
||||
|
||||
$(objpfx)ifuncmain1pie: $(objpfx)ifuncmod1.so
|
||||
$(objpfx)ifuncmain1staticpie: $(objpfx)ifuncdep1pic.o
|
||||
$(objpfx)ifuncmain1vispie: $(objpfx)ifuncmod1.so
|
||||
@@ -1460,8 +1465,6 @@ $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
|
||||
|
||||
tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
|
||||
LD_HWCAP_MASK=0x1
|
||||
tst-env-setuid-tunables-ENV = \
|
||||
GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096
|
||||
|
||||
$(objpfx)tst-debug1: $(libdl)
|
||||
$(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
|
||||
|
||||
+14
-4
@@ -1173,6 +1173,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
goto call_lose;
|
||||
}
|
||||
|
||||
/* dlopen of an executable is not valid because it is not possible
|
||||
to perform proper relocations, handle static TLS, or run the
|
||||
ELF constructors. For PIE, the check needs the dynamic
|
||||
section, so there is another check below. */
|
||||
if (__glibc_unlikely (type != ET_DYN)
|
||||
&& __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0))
|
||||
{
|
||||
@@ -1209,9 +1213,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
elf_get_dynamic_info (l, NULL);
|
||||
|
||||
/* Make sure we are not dlopen'ing an object that has the
|
||||
DF_1_NOOPEN flag set. */
|
||||
if (__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
|
||||
&& (mode & __RTLD_DLOPEN))
|
||||
DF_1_NOOPEN flag set, or a PIE object. */
|
||||
if ((__glibc_unlikely (l->l_flags_1 & DF_1_NOOPEN)
|
||||
&& (mode & __RTLD_DLOPEN))
|
||||
|| (__glibc_unlikely (l->l_flags_1 & DF_1_PIE)
|
||||
&& __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0)))
|
||||
{
|
||||
/* We are not supposed to load this object. Free all resources. */
|
||||
_dl_unmap_segments (l);
|
||||
@@ -1222,7 +1228,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
if (l->l_phdr_allocated)
|
||||
free ((void *) l->l_phdr);
|
||||
|
||||
errstring = N_("shared object cannot be dlopen()ed");
|
||||
if (l->l_flags_1 & DF_1_PIE)
|
||||
errstring
|
||||
= N_("cannot dynamically load position-independent executable");
|
||||
else
|
||||
errstring = N_("shared object cannot be dlopen()ed");
|
||||
goto call_lose;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -292,8 +292,6 @@ dl_open_worker (void *a)
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
|
||||
|
||||
_dl_open_check (new);
|
||||
|
||||
/* Print scope information. */
|
||||
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES))
|
||||
_dl_show_scope (new, 0);
|
||||
@@ -366,6 +364,12 @@ dl_open_worker (void *a)
|
||||
_dl_relocate_object (l, l->l_scope, reloc_mode, 0);
|
||||
}
|
||||
|
||||
/* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE
|
||||
object when _dl_open_check throws an exception. Move it after
|
||||
relocation to avoid leaving the NODELETE object mapped without
|
||||
relocation. */
|
||||
_dl_open_check (new);
|
||||
|
||||
/* If the file is not loaded now as a dependency, add the search
|
||||
list of the newly loaded object to the scope. */
|
||||
bool any_tls = false;
|
||||
|
||||
+3
-8
@@ -328,14 +328,9 @@ _dl_show_auxv (void)
|
||||
assert (AT_NULL == 0);
|
||||
assert (AT_IGNORE == 1);
|
||||
|
||||
if (av->a_type == AT_HWCAP || av->a_type == AT_HWCAP2
|
||||
|| AT_L1I_CACHEGEOMETRY || AT_L1D_CACHEGEOMETRY
|
||||
|| AT_L2_CACHEGEOMETRY || AT_L3_CACHEGEOMETRY)
|
||||
{
|
||||
/* These are handled in a special way per platform. */
|
||||
if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0)
|
||||
continue;
|
||||
}
|
||||
/* Some entries are handled in a special way per platform. */
|
||||
if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0)
|
||||
continue;
|
||||
|
||||
if (idx < sizeof (auxvars) / sizeof (auxvars[0])
|
||||
&& auxvars[idx].form != unknown)
|
||||
|
||||
+26
-30
@@ -178,6 +178,7 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
return;
|
||||
|
||||
char *p = tunestr;
|
||||
size_t off = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -191,7 +192,11 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
/* If we reach the end of the string before getting a valid name-value
|
||||
pair, bail out. */
|
||||
if (p[len] == '\0')
|
||||
return;
|
||||
{
|
||||
if (__libc_enable_secure)
|
||||
tunestr[off] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
/* We did not find a valid name-value pair before encountering the
|
||||
colon. */
|
||||
@@ -217,35 +222,28 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
|
||||
if (tunable_is_name (cur->name, name))
|
||||
{
|
||||
/* If we are in a secure context (AT_SECURE) then ignore the tunable
|
||||
unless it is explicitly marked as secure. Tunable values take
|
||||
precendence over their envvar aliases. */
|
||||
/* If we are in a secure context (AT_SECURE) then ignore the
|
||||
tunable unless it is explicitly marked as secure. Tunable
|
||||
values take precedence over their envvar aliases. We write
|
||||
the tunables that are not SXID_ERASE back to TUNESTR, thus
|
||||
dropping all SXID_ERASE tunables and any invalid or
|
||||
unrecognized tunables. */
|
||||
if (__libc_enable_secure)
|
||||
{
|
||||
if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
|
||||
if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
|
||||
{
|
||||
if (p[len] == '\0')
|
||||
{
|
||||
/* Last tunable in the valstring. Null-terminate and
|
||||
return. */
|
||||
*name = '\0';
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove the current tunable from the string. We do
|
||||
this by overwriting the string starting from NAME
|
||||
(which is where the current tunable begins) with
|
||||
the remainder of the string. We then have P point
|
||||
to NAME so that we continue in the correct
|
||||
position in the valstring. */
|
||||
char *q = &p[len + 1];
|
||||
p = name;
|
||||
while (*q != '\0')
|
||||
*name++ = *q++;
|
||||
name[0] = '\0';
|
||||
len = 0;
|
||||
}
|
||||
if (off > 0)
|
||||
tunestr[off++] = ':';
|
||||
|
||||
const char *n = cur->name;
|
||||
|
||||
while (*n != '\0')
|
||||
tunestr[off++] = *n++;
|
||||
|
||||
tunestr[off++] = '=';
|
||||
|
||||
for (size_t j = 0; j < len; j++)
|
||||
tunestr[off++] = value[j];
|
||||
}
|
||||
|
||||
if (cur->security_level != TUNABLE_SECLEVEL_NONE)
|
||||
@@ -258,9 +256,7 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
}
|
||||
}
|
||||
|
||||
if (p[len] == '\0')
|
||||
return;
|
||||
else
|
||||
if (p[len] != '\0')
|
||||
p += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,11 @@ glibc {
|
||||
tcache_unsorted_limit {
|
||||
type: SIZE_T
|
||||
}
|
||||
mxfast {
|
||||
type: SIZE_T
|
||||
minval: 0
|
||||
security_level: SXID_IGNORE
|
||||
}
|
||||
}
|
||||
cpu {
|
||||
hwcap_mask {
|
||||
|
||||
@@ -2854,6 +2854,13 @@ enum
|
||||
#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
|
||||
#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
|
||||
|
||||
/* AArch64 specific values for the Dyn d_tag field. */
|
||||
#define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5)
|
||||
#define DT_AARCH64_NUM 6
|
||||
|
||||
/* AArch64 specific values for the st_other field. */
|
||||
#define STO_AARCH64_VARIANT_PCS 0x80
|
||||
|
||||
/* ARM relocs. */
|
||||
|
||||
#define R_ARM_NONE 0 /* No reloc */
|
||||
|
||||
@@ -19,7 +19,14 @@ typedef int (*foo_p) (void);
|
||||
#endif
|
||||
|
||||
foo_p foo_ptr = foo;
|
||||
|
||||
/* Address-significant access to protected symbols is not supported in
|
||||
position-dependent mode on several architectures because GCC
|
||||
generates relocations that assume that the address is local to the
|
||||
main program. */
|
||||
#ifdef __PIE__
|
||||
foo_p foo_procted_ptr = foo_protected;
|
||||
#endif
|
||||
|
||||
extern foo_p get_foo_p (void);
|
||||
extern foo_p get_foo_hidden_p (void);
|
||||
@@ -37,12 +44,16 @@ main (void)
|
||||
if ((*foo_ptr) () != -1)
|
||||
abort ();
|
||||
|
||||
#ifdef __PIE__
|
||||
if (foo_procted_ptr != foo_protected)
|
||||
abort ();
|
||||
#endif
|
||||
if (foo_protected () != 0)
|
||||
abort ();
|
||||
#ifdef __PIE__
|
||||
if ((*foo_procted_ptr) () != 0)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = get_foo_p ();
|
||||
if (p != foo)
|
||||
@@ -55,8 +66,10 @@ main (void)
|
||||
abort ();
|
||||
|
||||
p = get_foo_protected_p ();
|
||||
#ifdef __PIE__
|
||||
if (p != foo_protected)
|
||||
abort ();
|
||||
#endif
|
||||
if (ret_foo_protected != 0 || (*p) () != ret_foo_protected)
|
||||
abort ();
|
||||
|
||||
|
||||
@@ -14,12 +14,19 @@ get_foo (void)
|
||||
return foo;
|
||||
}
|
||||
|
||||
|
||||
/* Address-significant access to protected symbols is not supported in
|
||||
position-dependent mode on several architectures because GCC
|
||||
generates relocations that assume that the address is local to the
|
||||
main program. */
|
||||
#ifdef __PIE__
|
||||
foo_p
|
||||
__attribute__ ((noinline))
|
||||
get_foo_protected (void)
|
||||
{
|
||||
return foo_protected;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main (void)
|
||||
@@ -30,9 +37,11 @@ main (void)
|
||||
if ((*p) () != -1)
|
||||
abort ();
|
||||
|
||||
#ifdef __PIE__
|
||||
p = get_foo_protected ();
|
||||
if ((*p) () != 0)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-11
@@ -9,7 +9,6 @@
|
||||
#include "ifunc-sel.h"
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
static int
|
||||
one (void)
|
||||
@@ -28,20 +27,17 @@ foo_ifunc (void)
|
||||
}
|
||||
|
||||
extern int foo (void);
|
||||
extern foo_p get_foo (void);
|
||||
extern int call_foo (void);
|
||||
extern foo_p get_foo_p (void);
|
||||
|
||||
foo_p my_foo_ptr = foo;
|
||||
foo_p foo_ptr = foo;
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
foo_p p;
|
||||
|
||||
p = get_foo ();
|
||||
if (p != foo)
|
||||
abort ();
|
||||
if ((*p) () != -30)
|
||||
if (call_foo () != -30)
|
||||
abort ();
|
||||
|
||||
p = get_foo_p ();
|
||||
@@ -52,12 +48,8 @@ main (void)
|
||||
|
||||
if (foo_ptr != foo)
|
||||
abort ();
|
||||
if (my_foo_ptr != foo)
|
||||
abort ();
|
||||
if ((*foo_ptr) () != -30)
|
||||
abort ();
|
||||
if ((*my_foo_ptr) () != -30)
|
||||
abort ();
|
||||
if (foo () != -30)
|
||||
abort ();
|
||||
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ extern int foo (void);
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
|
||||
foo_p foo_ptr = foo;
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
foo_p
|
||||
get_foo_p (void)
|
||||
@@ -12,8 +12,8 @@ get_foo_p (void)
|
||||
return foo_ptr;
|
||||
}
|
||||
|
||||
foo_p
|
||||
get_foo (void)
|
||||
int
|
||||
call_foo (void)
|
||||
{
|
||||
return foo;
|
||||
return foo ();
|
||||
}
|
||||
|
||||
+40
-74
@@ -23,10 +23,6 @@
|
||||
#define EW_(e, w, t) EW__(e, w, _##t)
|
||||
#define EW__(e, w, t) e##w##t
|
||||
|
||||
#define pldd_assert(name, exp) \
|
||||
typedef int __assert_##name[((exp) != 0) - 1]
|
||||
|
||||
|
||||
struct E(link_map)
|
||||
{
|
||||
EW(Addr) l_addr;
|
||||
@@ -39,12 +35,12 @@ struct E(link_map)
|
||||
EW(Addr) l_libname;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (l_addr, (offsetof (struct link_map, l_addr)
|
||||
== offsetof (struct E(link_map), l_addr)));
|
||||
pldd_assert (l_name, (offsetof (struct link_map, l_name)
|
||||
== offsetof (struct E(link_map), l_name)));
|
||||
pldd_assert (l_next, (offsetof (struct link_map, l_next)
|
||||
== offsetof (struct E(link_map), l_next)));
|
||||
_Static_assert (offsetof (struct link_map, l_addr)
|
||||
== offsetof (struct E(link_map), l_addr), "l_addr");
|
||||
_Static_assert (offsetof (struct link_map, l_name)
|
||||
== offsetof (struct E(link_map), l_name), "l_name");
|
||||
_Static_assert (offsetof (struct link_map, l_next)
|
||||
== offsetof (struct E(link_map), l_next), "l_next");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -54,10 +50,10 @@ struct E(libname_list)
|
||||
EW(Addr) next;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (name, (offsetof (struct libname_list, name)
|
||||
== offsetof (struct E(libname_list), name)));
|
||||
pldd_assert (next, (offsetof (struct libname_list, next)
|
||||
== offsetof (struct E(libname_list), next)));
|
||||
_Static_assert (offsetof (struct libname_list, name)
|
||||
== offsetof (struct E(libname_list), name), "name");
|
||||
_Static_assert (offsetof (struct libname_list, next)
|
||||
== offsetof (struct E(libname_list), next), "next");
|
||||
#endif
|
||||
|
||||
struct E(r_debug)
|
||||
@@ -69,16 +65,17 @@ struct E(r_debug)
|
||||
EW(Addr) r_map;
|
||||
};
|
||||
#if CLASS == __ELF_NATIVE_CLASS
|
||||
pldd_assert (r_version, (offsetof (struct r_debug, r_version)
|
||||
== offsetof (struct E(r_debug), r_version)));
|
||||
pldd_assert (r_map, (offsetof (struct r_debug, r_map)
|
||||
== offsetof (struct E(r_debug), r_map)));
|
||||
_Static_assert (offsetof (struct r_debug, r_version)
|
||||
== offsetof (struct E(r_debug), r_version), "r_version");
|
||||
_Static_assert (offsetof (struct r_debug, r_map)
|
||||
== offsetof (struct E(r_debug), r_map), "r_map");
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
|
||||
E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
E(find_maps) (const char *exe, int memfd, pid_t pid, void *auxv,
|
||||
size_t auxv_size)
|
||||
{
|
||||
EW(Addr) phdr = 0;
|
||||
unsigned int phnum = 0;
|
||||
@@ -104,12 +101,9 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (phdr == 0 || phnum == 0 || phent == 0)
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot find program header of process"));
|
||||
|
||||
EW(Phdr) *p = alloca (phnum * phent);
|
||||
if (pread64 (memfd, p, phnum * phent, phdr) != phnum * phent)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read program header"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EW(Phdr) *p = xmalloc (phnum * phent);
|
||||
if (pread (memfd, p, phnum * phent, phdr) != phnum * phent)
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read program header"));
|
||||
|
||||
/* Determine the load offset. We need this for interpreting the
|
||||
other program header entries so we do this in a separate loop.
|
||||
@@ -129,24 +123,18 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (p[i].p_type == PT_DYNAMIC)
|
||||
{
|
||||
EW(Dyn) *dyn = xmalloc (p[i].p_filesz);
|
||||
if (pread64 (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
if (pread (memfd, dyn, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
!= p[i].p_filesz)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read dynamic section"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read dynamic section"));
|
||||
|
||||
/* Search for the DT_DEBUG entry. */
|
||||
for (unsigned int j = 0; j < p[i].p_filesz / sizeof (EW(Dyn)); ++j)
|
||||
if (dyn[j].d_tag == DT_DEBUG && dyn[j].d_un.d_ptr != 0)
|
||||
{
|
||||
struct E(r_debug) r;
|
||||
if (pread64 (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
|
||||
if (pread (memfd, &r, sizeof (r), dyn[j].d_un.d_ptr)
|
||||
!= sizeof (r))
|
||||
{
|
||||
error (0, 0, gettext ("cannot read r_debug"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read r_debug"));
|
||||
|
||||
if (r.r_map != 0)
|
||||
{
|
||||
@@ -160,13 +148,10 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
}
|
||||
else if (p[i].p_type == PT_INTERP)
|
||||
{
|
||||
interp = alloca (p[i].p_filesz);
|
||||
if (pread64 (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
interp = xmalloc (p[i].p_filesz);
|
||||
if (pread (memfd, interp, p[i].p_filesz, offset + p[i].p_vaddr)
|
||||
!= p[i].p_filesz)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read program interpreter"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read program interpreter"));
|
||||
}
|
||||
|
||||
if (list == 0)
|
||||
@@ -174,14 +159,16 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
if (interp == NULL)
|
||||
{
|
||||
// XXX check whether the executable itself is the loader
|
||||
return EXIT_FAILURE;
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// XXX perhaps try finding ld.so and _r_debug in it
|
||||
|
||||
return EXIT_FAILURE;
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free (p);
|
||||
free (interp);
|
||||
|
||||
/* Print the PID and program name first. */
|
||||
printf ("%lu:\t%s\n", (unsigned long int) pid, exe);
|
||||
|
||||
@@ -192,47 +179,27 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
do
|
||||
{
|
||||
struct E(link_map) m;
|
||||
if (pread64 (memfd, &m, sizeof (m), list) != sizeof (m))
|
||||
{
|
||||
error (0, 0, gettext ("cannot read link map"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
if (pread (memfd, &m, sizeof (m), list) != sizeof (m))
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read link map"));
|
||||
|
||||
EW(Addr) name_offset = m.l_name;
|
||||
again:
|
||||
while (1)
|
||||
{
|
||||
ssize_t n = pread64 (memfd, tmpbuf.data, tmpbuf.length, name_offset);
|
||||
ssize_t n = pread (memfd, tmpbuf.data, tmpbuf.length, name_offset);
|
||||
if (n == -1)
|
||||
{
|
||||
error (0, 0, gettext ("cannot read object name"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
error (EXIT_FAILURE, 0, gettext ("cannot read object name"));
|
||||
|
||||
if (memchr (tmpbuf.data, '\0', n) != NULL)
|
||||
break;
|
||||
|
||||
if (!scratch_buffer_grow (&tmpbuf))
|
||||
{
|
||||
error (0, 0, gettext ("cannot allocate buffer for object name"));
|
||||
status = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
error (EXIT_FAILURE, 0,
|
||||
gettext ("cannot allocate buffer for object name"));
|
||||
}
|
||||
|
||||
if (((char *)tmpbuf.data)[0] == '\0' && name_offset == m.l_name
|
||||
&& m.l_libname != 0)
|
||||
{
|
||||
/* Try the l_libname element. */
|
||||
struct E(libname_list) ln;
|
||||
if (pread64 (memfd, &ln, sizeof (ln), m.l_libname) == sizeof (ln))
|
||||
{
|
||||
name_offset = ln.name;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
/* The m.l_name and m.l_libname.name for loader linkmap points to same
|
||||
values (since BZ#387 fix). Trying to use l_libname name as the
|
||||
shared object name might lead to an infinite loop (BZ#18035). */
|
||||
|
||||
/* Skip over the executable. */
|
||||
if (((char *)tmpbuf.data)[0] != '\0')
|
||||
@@ -242,7 +209,6 @@ E(find_maps) (pid_t pid, void *auxv, size_t auxv_size)
|
||||
}
|
||||
while (list != 0);
|
||||
|
||||
out:
|
||||
scratch_buffer_free (&tmpbuf);
|
||||
return status;
|
||||
}
|
||||
|
||||
+30
-34
@@ -17,23 +17,17 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <alloca.h>
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <argp.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <fcntl.h>
|
||||
#include <libintl.h>
|
||||
#include <link.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <scratch_buffer.h>
|
||||
|
||||
@@ -76,14 +70,9 @@ static struct argp argp =
|
||||
options, parse_opt, args_doc, doc, NULL, more_help, NULL
|
||||
};
|
||||
|
||||
// File descriptor of /proc/*/mem file.
|
||||
static int memfd;
|
||||
|
||||
/* Name of the executable */
|
||||
static char *exe;
|
||||
|
||||
/* Local functions. */
|
||||
static int get_process_info (int dfd, long int pid);
|
||||
static int get_process_info (const char *exe, int dfd, long int pid);
|
||||
static void wait_for_ptrace_stop (long int pid);
|
||||
|
||||
|
||||
@@ -102,8 +91,10 @@ main (int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert (sizeof (pid_t) == sizeof (int)
|
||||
|| sizeof (pid_t) == sizeof (long int));
|
||||
_Static_assert (sizeof (pid_t) == sizeof (int)
|
||||
|| sizeof (pid_t) == sizeof (long int),
|
||||
"sizeof (pid_t) != sizeof (int) or sizeof (long int)");
|
||||
|
||||
char *endp;
|
||||
errno = 0;
|
||||
long int pid = strtol (argv[remaining], &endp, 10);
|
||||
@@ -119,25 +110,24 @@ main (int argc, char *argv[])
|
||||
if (dfd == -1)
|
||||
error (EXIT_FAILURE, errno, gettext ("cannot open %s"), buf);
|
||||
|
||||
struct scratch_buffer exebuf;
|
||||
scratch_buffer_init (&exebuf);
|
||||
/* Name of the executable */
|
||||
struct scratch_buffer exe;
|
||||
scratch_buffer_init (&exe);
|
||||
ssize_t nexe;
|
||||
while ((nexe = readlinkat (dfd, "exe",
|
||||
exebuf.data, exebuf.length)) == exebuf.length)
|
||||
exe.data, exe.length)) == exe.length)
|
||||
{
|
||||
if (!scratch_buffer_grow (&exebuf))
|
||||
if (!scratch_buffer_grow (&exe))
|
||||
{
|
||||
nexe = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nexe == -1)
|
||||
exe = (char *) "<program name undetermined>";
|
||||
/* Default stack allocation is at least 1024. */
|
||||
snprintf (exe.data, exe.length, "<program name undetermined>");
|
||||
else
|
||||
{
|
||||
exe = exebuf.data;
|
||||
exe[nexe] = '\0';
|
||||
}
|
||||
((char*)exe.data)[nexe] = '\0';
|
||||
|
||||
/* Stop all threads since otherwise the list of loaded modules might
|
||||
change while we are reading it. */
|
||||
@@ -155,8 +145,8 @@ main (int argc, char *argv[])
|
||||
error (EXIT_FAILURE, errno, gettext ("cannot prepare reading %s/task"),
|
||||
buf);
|
||||
|
||||
struct dirent64 *d;
|
||||
while ((d = readdir64 (dir)) != NULL)
|
||||
struct dirent *d;
|
||||
while ((d = readdir (dir)) != NULL)
|
||||
{
|
||||
if (! isdigit (d->d_name[0]))
|
||||
continue;
|
||||
@@ -182,7 +172,7 @@ main (int argc, char *argv[])
|
||||
|
||||
wait_for_ptrace_stop (tid);
|
||||
|
||||
struct thread_list *newp = alloca (sizeof (*newp));
|
||||
struct thread_list *newp = xmalloc (sizeof (*newp));
|
||||
newp->tid = tid;
|
||||
newp->next = thread_list;
|
||||
thread_list = newp;
|
||||
@@ -190,17 +180,22 @@ main (int argc, char *argv[])
|
||||
|
||||
closedir (dir);
|
||||
|
||||
int status = get_process_info (dfd, pid);
|
||||
if (thread_list == NULL)
|
||||
error (EXIT_FAILURE, 0, gettext ("no valid %s/task entries"), buf);
|
||||
|
||||
int status = get_process_info (exe.data, dfd, pid);
|
||||
|
||||
assert (thread_list != NULL);
|
||||
do
|
||||
{
|
||||
ptrace (PTRACE_DETACH, thread_list->tid, NULL, NULL);
|
||||
struct thread_list *prev = thread_list;
|
||||
thread_list = thread_list->next;
|
||||
free (prev);
|
||||
}
|
||||
while (thread_list != NULL);
|
||||
|
||||
close (dfd);
|
||||
scratch_buffer_free (&exe);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -281,9 +276,10 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
|
||||
|
||||
|
||||
static int
|
||||
get_process_info (int dfd, long int pid)
|
||||
get_process_info (const char *exe, int dfd, long int pid)
|
||||
{
|
||||
memfd = openat (dfd, "mem", O_RDONLY);
|
||||
/* File descriptor of /proc/<pid>/mem file. */
|
||||
int memfd = openat (dfd, "mem", O_RDONLY);
|
||||
if (memfd == -1)
|
||||
goto no_info;
|
||||
|
||||
@@ -333,9 +329,9 @@ get_process_info (int dfd, long int pid)
|
||||
|
||||
int retval;
|
||||
if (e_ident[EI_CLASS] == ELFCLASS32)
|
||||
retval = find_maps32 (pid, auxv, auxv_size);
|
||||
retval = find_maps32 (exe, memfd, pid, auxv, auxv_size);
|
||||
else
|
||||
retval = find_maps64 (pid, auxv, auxv_size);
|
||||
retval = find_maps64 (exe, memfd, pid, auxv, auxv_size);
|
||||
|
||||
free (auxv);
|
||||
close (memfd);
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ $(objpfx)rtld-libc.a: $(foreach dir,$(rtld-subdirs),\
|
||||
mv -f $@T $@
|
||||
|
||||
# Use the verbose option of ar and tar when not running silently.
|
||||
ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s
|
||||
ifeq ($(silent-make),no) # if not -s
|
||||
verbose := v
|
||||
else # -s
|
||||
verbose :=
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* dlopen test for PIE objects.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test attempts to open the (otherwise unrelated) PIE test
|
||||
program elf/tst-pie1 and expects the attempt to fail. */
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static void
|
||||
test_mode (int mode)
|
||||
{
|
||||
char *pie_path = xasprintf ("%s/elf/tst-pie1", support_objdir_root);
|
||||
if (dlopen (pie_path, mode) != NULL)
|
||||
FAIL_EXIT1 ("dlopen succeeded unexpectedly (%d)", mode);
|
||||
const char *message = dlerror ();
|
||||
const char *expected
|
||||
= "cannot dynamically load position-independent executable";
|
||||
if (strstr (message, expected) == NULL)
|
||||
FAIL_EXIT1 ("unexpected error message (mode %d): %s", mode, message);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
test_mode (RTLD_LAZY);
|
||||
test_mode (RTLD_NOW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -25,35 +25,76 @@
|
||||
#include "config.h"
|
||||
#undef _LIBC
|
||||
|
||||
#define test_parent test_parent_tunables
|
||||
#define test_child test_child_tunables
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <intprops.h>
|
||||
#include <array_length.h>
|
||||
|
||||
static int test_child_tunables (void);
|
||||
static int test_parent_tunables (void);
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
#include "tst-env-setuid.c"
|
||||
const char *teststrings[] =
|
||||
{
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
|
||||
"glibc.malloc.perturb=0x800",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
|
||||
"glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
|
||||
":glibc.malloc.garbage=2:glibc.malloc.check=1",
|
||||
"glibc.malloc.check=1:glibc.malloc.check=2",
|
||||
"not_valid.malloc.check=2",
|
||||
"glibc.not_valid.check=2",
|
||||
};
|
||||
|
||||
#define CHILD_VALSTRING_VALUE "glibc.malloc.mmap_threshold=4096"
|
||||
#define PARENT_VALSTRING_VALUE \
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096"
|
||||
const char *resultstrings[] =
|
||||
{
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
};
|
||||
|
||||
static int
|
||||
test_child_tunables (void)
|
||||
test_child (int off)
|
||||
{
|
||||
const char *val = getenv ("GLIBC_TUNABLES");
|
||||
|
||||
#if HAVE_TUNABLES
|
||||
if (val != NULL && strcmp (val, CHILD_VALSTRING_VALUE) == 0)
|
||||
if (val != NULL && strcmp (val, resultstrings[off]) == 0)
|
||||
return 0;
|
||||
|
||||
if (val != NULL)
|
||||
printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
|
||||
printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
|
||||
|
||||
return 1;
|
||||
#else
|
||||
if (val != NULL)
|
||||
{
|
||||
printf ("GLIBC_TUNABLES not cleared\n");
|
||||
printf ("[%d] GLIBC_TUNABLES not cleared\n", off);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -61,15 +102,48 @@ test_child_tunables (void)
|
||||
}
|
||||
|
||||
static int
|
||||
test_parent_tunables (void)
|
||||
do_test (int argc, char **argv)
|
||||
{
|
||||
const char *val = getenv ("GLIBC_TUNABLES");
|
||||
/* Setgid child process. */
|
||||
if (argc == 2)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
|
||||
if (val != NULL && strcmp (val, PARENT_VALSTRING_VALUE) == 0)
|
||||
return 0;
|
||||
int ret = test_child (atoi (argv[1]));
|
||||
|
||||
if (val != NULL)
|
||||
printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
|
||||
if (ret != 0)
|
||||
exit (1);
|
||||
|
||||
return 1;
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Spawn tests. */
|
||||
for (int i = 0; i < array_length (teststrings); i++)
|
||||
{
|
||||
char buf[INT_BUFSIZE_BOUND (int)];
|
||||
|
||||
printf ("Spawned test for %s (%d)\n", teststrings[i], i);
|
||||
snprintf (buf, sizeof (buf), "%d\n", i);
|
||||
if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0)
|
||||
exit (1);
|
||||
|
||||
int status = support_capture_subprogram_self_sgid (buf);
|
||||
|
||||
/* Bail out early if unsupported. */
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
ret |= status;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION_ARGV do_test
|
||||
#include <support/test-driver.c>
|
||||
|
||||
+14
-183
@@ -29,173 +29,12 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
static char SETGID_CHILD[] = "setgid-child";
|
||||
#define CHILD_STATUS 42
|
||||
|
||||
/* Return a GID which is not our current GID, but is present in the
|
||||
supplementary group list. */
|
||||
static gid_t
|
||||
choose_gid (void)
|
||||
{
|
||||
const int count = 64;
|
||||
gid_t groups[count];
|
||||
int ret = getgroups (count, groups);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf ("getgroups: %m\n");
|
||||
exit (1);
|
||||
}
|
||||
gid_t current = getgid ();
|
||||
for (int i = 0; i < ret; ++i)
|
||||
{
|
||||
if (groups[i] != current)
|
||||
return groups[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Spawn and execute a program and verify that it returns the CHILD_STATUS. */
|
||||
static pid_t
|
||||
do_execve (char **args)
|
||||
{
|
||||
pid_t kid = vfork ();
|
||||
|
||||
if (kid < 0)
|
||||
{
|
||||
printf ("vfork: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (kid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
execve (args[0], args, environ);
|
||||
_exit (-errno);
|
||||
}
|
||||
|
||||
if (kid < 0)
|
||||
return 1;
|
||||
|
||||
int status;
|
||||
|
||||
if (waitpid (kid, &status, 0) < 0)
|
||||
{
|
||||
printf ("waitpid: %m\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != CHILD_STATUS)
|
||||
{
|
||||
printf ("Unexpected exit status %d from child process\n",
|
||||
WEXITSTATUS (status));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copies the executable into a restricted directory, so that we can
|
||||
safely make it SGID with the TARGET group ID. Then runs the
|
||||
executable. */
|
||||
static int
|
||||
run_executable_sgid (gid_t target)
|
||||
{
|
||||
char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
|
||||
test_dir, (intmax_t) getpid ());
|
||||
char *execname = xasprintf ("%s/bin", dirname);
|
||||
int infd = -1;
|
||||
int outfd = -1;
|
||||
int ret = 0;
|
||||
if (mkdir (dirname, 0700) < 0)
|
||||
{
|
||||
printf ("mkdir: %m\n");
|
||||
goto err;
|
||||
}
|
||||
infd = open ("/proc/self/exe", O_RDONLY);
|
||||
if (infd < 0)
|
||||
{
|
||||
printf ("open (/proc/self/exe): %m\n");
|
||||
goto err;
|
||||
}
|
||||
outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
|
||||
if (outfd < 0)
|
||||
{
|
||||
printf ("open (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
char buf[4096];
|
||||
for (;;)
|
||||
{
|
||||
ssize_t rdcount = read (infd, buf, sizeof (buf));
|
||||
if (rdcount < 0)
|
||||
{
|
||||
printf ("read: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (rdcount == 0)
|
||||
break;
|
||||
char *p = buf;
|
||||
char *end = buf + rdcount;
|
||||
while (p != end)
|
||||
{
|
||||
ssize_t wrcount = write (outfd, buf, end - p);
|
||||
if (wrcount == 0)
|
||||
errno = ENOSPC;
|
||||
if (wrcount <= 0)
|
||||
{
|
||||
printf ("write: %m\n");
|
||||
goto err;
|
||||
}
|
||||
p += wrcount;
|
||||
}
|
||||
}
|
||||
if (fchown (outfd, getuid (), target) < 0)
|
||||
{
|
||||
printf ("fchown (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (fchmod (outfd, 02750) < 0)
|
||||
{
|
||||
printf ("fchmod (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (close (outfd) < 0)
|
||||
{
|
||||
printf ("close (outfd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (close (infd) < 0)
|
||||
{
|
||||
printf ("close (infd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
char *args[] = {execname, SETGID_CHILD, NULL};
|
||||
|
||||
ret = do_execve (args);
|
||||
|
||||
err:
|
||||
if (outfd >= 0)
|
||||
close (outfd);
|
||||
if (infd >= 0)
|
||||
close (infd);
|
||||
if (execname)
|
||||
{
|
||||
unlink (execname);
|
||||
free (execname);
|
||||
}
|
||||
if (dirname)
|
||||
{
|
||||
rmdir (dirname);
|
||||
free (dirname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef test_child
|
||||
static int
|
||||
@@ -256,40 +95,32 @@ do_test (int argc, char **argv)
|
||||
if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
{
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
exit (EXIT_UNSUPPORTED);
|
||||
}
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
|
||||
int ret = test_child ();
|
||||
|
||||
if (ret != 0)
|
||||
exit (1);
|
||||
|
||||
exit (CHILD_STATUS);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test_parent () != 0)
|
||||
exit (1);
|
||||
|
||||
/* Try running a setgid program. */
|
||||
gid_t target = choose_gid ();
|
||||
if (target == 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Could not find a suitable GID for user %jd, skipping test\n",
|
||||
(intmax_t) getuid ());
|
||||
exit (0);
|
||||
}
|
||||
int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
|
||||
|
||||
return run_executable_sgid (target);
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status))
|
||||
FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Something went wrong and our argv was corrupted. */
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION_ARGV do_test
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/* Basic tests for pldd program.
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <array_length.h>
|
||||
#include <gnu/lib-names.h>
|
||||
|
||||
#include <support/subprocess.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static void
|
||||
target_process (void *arg)
|
||||
{
|
||||
pause ();
|
||||
}
|
||||
|
||||
/* The test runs in a container because pldd does not support tracing
|
||||
a binary started by the loader iself (as with testrun.sh). */
|
||||
|
||||
static bool
|
||||
in_str_list (const char *libname, const char *const strlist[])
|
||||
{
|
||||
for (const char *const *str = strlist; *str != NULL; str++)
|
||||
if (strcmp (libname, *str) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Create a copy of current test to check with pldd. */
|
||||
struct support_subprocess target = support_subprocess (target_process, NULL);
|
||||
|
||||
/* Run 'pldd' on test subprocess. */
|
||||
struct support_capture_subprocess pldd;
|
||||
{
|
||||
/* Three digits per byte plus null terminator. */
|
||||
char pid[3 * sizeof (uint32_t) + 1];
|
||||
snprintf (pid, array_length (pid), "%d", target.pid);
|
||||
|
||||
const char prog[] = "/usr/bin/pldd";
|
||||
|
||||
pldd = support_capture_subprogram (prog,
|
||||
(char *const []) { (char *) prog, pid, NULL });
|
||||
|
||||
support_capture_subprocess_check (&pldd, "pldd", 0, sc_allow_stdout);
|
||||
}
|
||||
|
||||
/* Check 'pldd' output. The test is expected to be linked against only
|
||||
loader and libc. */
|
||||
{
|
||||
pid_t pid;
|
||||
char buffer[512];
|
||||
#define STRINPUT(size) "%" # size "s"
|
||||
|
||||
FILE *out = fmemopen (pldd.out.buffer, pldd.out.length, "r");
|
||||
TEST_VERIFY (out != NULL);
|
||||
|
||||
/* First line is in the form of <pid>: <full path of executable> */
|
||||
TEST_COMPARE (fscanf (out, "%u: " STRINPUT (512), &pid, buffer), 2);
|
||||
|
||||
TEST_COMPARE (pid, target.pid);
|
||||
TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0);
|
||||
|
||||
/* It expects only one loader and libc loaded by the program. */
|
||||
bool interpreter_found = false, libc_found = false;
|
||||
while (fgets (buffer, array_length (buffer), out) != NULL)
|
||||
{
|
||||
/* Ignore vDSO. */
|
||||
if (buffer[0] != '/')
|
||||
continue;
|
||||
|
||||
/* Remove newline so baseline (buffer) can compare against the
|
||||
LD_SO and LIBC_SO macros unmodified. */
|
||||
if (buffer[strlen(buffer)-1] == '\n')
|
||||
buffer[strlen(buffer)-1] = '\0';
|
||||
|
||||
const char *libname = basename (buffer);
|
||||
|
||||
/* It checks for default names in case of build configure with
|
||||
--enable-hardcoded-path-in-tests (BZ #24506). */
|
||||
if (in_str_list (libname,
|
||||
(const char *const []) { "ld.so", LD_SO, NULL }))
|
||||
{
|
||||
TEST_COMPARE (interpreter_found, false);
|
||||
interpreter_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_str_list (libname,
|
||||
(const char *const []) { "libc.so", LIBC_SO, NULL }))
|
||||
{
|
||||
TEST_COMPARE (libc_found, false);
|
||||
libc_found = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TEST_COMPARE (interpreter_found, true);
|
||||
TEST_COMPARE (libc_found, true);
|
||||
|
||||
fclose (out);
|
||||
}
|
||||
|
||||
support_capture_subprocess_free (&pldd);
|
||||
support_process_terminate (&target);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+3
-1
@@ -73,7 +73,7 @@ modules.so := $(addsuffix .so, $(modules))
|
||||
ifeq (yes,$(build-shared))
|
||||
tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
|
||||
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
|
||||
bug-iconv10 bug-iconv11 bug-iconv12
|
||||
bug-iconv10 bug-iconv11 bug-iconv12 bug-iconv14
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -316,6 +316,8 @@ $(objpfx)bug-iconv10.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv12.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv14.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
|
||||
$(objpfx)iconv-test.out: run-iconv-test.sh $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so)) \
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Assertion in ISO-2022-JP-3 due to two-character sequence (bug 27256).
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <iconv.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* Use an escape sequence to return to the initial state. */
|
||||
static void
|
||||
with_escape_sequence (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D\e(B";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 3);
|
||||
TEST_COMPARE (inbuf - in, strlen (in) - 3);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Return to the initial shift state, producing the pending
|
||||
character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
/* Use an explicit flush to return to the initial state. */
|
||||
static void
|
||||
with_flush (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Flush the pending character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
with_escape_sequence ();
|
||||
with_flush ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+48
-19
@@ -67,23 +67,34 @@ enum
|
||||
CURRENT_SEL_MASK = 7 << 3
|
||||
};
|
||||
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the state
|
||||
also contains the last two bytes to be output, shifted by 6 bits, and a
|
||||
one-bit indicator whether they must be preceded by the shift sequence,
|
||||
in bit 22. */
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the
|
||||
state also contains the last two bytes to be output, shifted by 6
|
||||
bits, and a one-bit indicator whether they must be preceded by the
|
||||
shift sequence, in bit 22. During ISO-2022-JP-3 to UCS-4
|
||||
conversion, COUNT may also contain a non-zero pending wide
|
||||
character, shifted by six bits. This happens for certain inputs in
|
||||
JISX0213_1_2004_set and JISX0213_2_set if the second wide character
|
||||
in a combining sequence cannot be written because the buffer is
|
||||
full. */
|
||||
|
||||
/* Since this is a stateful encoding we have to provide code which resets
|
||||
the output state to the initial state. This has to be done during the
|
||||
flushing. */
|
||||
#define EMIT_SHIFT_TO_INIT \
|
||||
if ((data->__statep->__count & ~7) != ASCII_set) \
|
||||
if (data->__statep->__count != ASCII_set) \
|
||||
{ \
|
||||
if (FROM_DIRECTION) \
|
||||
{ \
|
||||
/* It's easy, we don't have to emit anything, we just reset the \
|
||||
state for the input. */ \
|
||||
data->__statep->__count &= 7; \
|
||||
data->__statep->__count |= ASCII_set; \
|
||||
if (__glibc_likely (outbuf + 4 <= outend)) \
|
||||
{ \
|
||||
/* Write out the last character. */ \
|
||||
*((uint32_t *) outbuf) = data->__statep->__count >> 6; \
|
||||
outbuf += sizeof (uint32_t); \
|
||||
data->__statep->__count = ASCII_set; \
|
||||
} \
|
||||
else \
|
||||
/* We don't have enough room in the output buffer. */ \
|
||||
status = __GCONV_FULL_OUTPUT; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -151,7 +162,21 @@ enum
|
||||
#define LOOPFCT FROM_LOOP
|
||||
#define BODY \
|
||||
{ \
|
||||
uint32_t ch = *inptr; \
|
||||
uint32_t ch; \
|
||||
\
|
||||
/* Output any pending character. */ \
|
||||
ch = set >> 6; \
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
/* Remove the pending character, but preserve state bits. */ \
|
||||
set &= (1 << 6) - 1; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise read the next input byte. */ \
|
||||
ch = *inptr; \
|
||||
\
|
||||
/* Recognize escape sequences. */ \
|
||||
if (__glibc_unlikely (ch == ESC)) \
|
||||
@@ -297,21 +322,25 @@ enum
|
||||
uint32_t u1 = __jisx0213_to_ucs_combining[ch - 1][0]; \
|
||||
uint32_t u2 = __jisx0213_to_ucs_combining[ch - 1][1]; \
|
||||
\
|
||||
inptr += 2; \
|
||||
\
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
\
|
||||
/* See whether we have room for two characters. */ \
|
||||
if (outptr + 8 <= outend) \
|
||||
if (outptr + 4 <= outend) \
|
||||
{ \
|
||||
inptr += 2; \
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
put32 (outptr, u2); \
|
||||
outptr += 4; \
|
||||
continue; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise store only the first character now, and \
|
||||
put the second one into the queue. */ \
|
||||
set |= u2 << 6; \
|
||||
/* Tell the caller why we terminate the loop. */ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
inptr += 2; \
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
# endif
|
||||
# define DT_1_SUPPORTED_MASK \
|
||||
(DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
|
||||
| DF_1_ORIGIN | DF_1_NODEFLIB)
|
||||
| DF_1_ORIGIN | DF_1_NODEFLIB | DF_1_PIE)
|
||||
|
||||
#endif /* !_ISOMAC */
|
||||
#endif /* elf.h */
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#ifdef USE_STAP_PROBE
|
||||
|
||||
# include <stap-probe-machine.h>
|
||||
# include <sys/sdt.h>
|
||||
|
||||
/* Our code uses one macro LIBC_PROBE (name, n, arg1, ..., argn).
|
||||
|
||||
@@ -112,4 +112,8 @@ ifeq ($(build-static-nss),yes)
|
||||
CFLAGS += -DSTATIC_NSS
|
||||
endif
|
||||
|
||||
# The test uses dlopen indirectly and would otherwise load system
|
||||
# objects.
|
||||
tst-idna_name_classify-ENV = \
|
||||
LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)elf
|
||||
$(objpfx)tst-idna_name_classify.out: $(gen-locales)
|
||||
|
||||
@@ -73,11 +73,6 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
|
||||
tst-fts tst-fts-lfs tst-open-tmpfile \
|
||||
tst-copy_file_range tst-getcwd-abspath \
|
||||
|
||||
# This test includes the compat implementation of copy_file_range,
|
||||
# which uses internal, unexported libc functions.
|
||||
tests-static += tst-copy_file_range-compat
|
||||
tests-internal += tst-copy_file_range-compat
|
||||
|
||||
# Likewise for statx, but we do not need static linking here.
|
||||
tests-internal += tst-statx
|
||||
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
/* Emulation of copy_file_range.
|
||||
Copyright (C) 2017-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/>. */
|
||||
|
||||
/* The following macros should be defined before including this
|
||||
file:
|
||||
|
||||
COPY_FILE_RANGE_DECL Declaration specifiers for the function below.
|
||||
COPY_FILE_RANGE Name of the function to define. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
COPY_FILE_RANGE_DECL
|
||||
ssize_t
|
||||
COPY_FILE_RANGE (int infd, __off64_t *pinoff,
|
||||
int outfd, __off64_t *poutoff,
|
||||
size_t length, unsigned int flags)
|
||||
{
|
||||
if (flags != 0)
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
struct stat64 instat;
|
||||
struct stat64 outstat;
|
||||
if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0)
|
||||
return -1;
|
||||
if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode))
|
||||
{
|
||||
__set_errno (EISDIR);
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode))
|
||||
{
|
||||
/* We need a regular input file so that the we can seek
|
||||
backwards in case of a write failure. */
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (instat.st_dev != outstat.st_dev)
|
||||
{
|
||||
/* Cross-device copies are not supported. */
|
||||
__set_errno (EXDEV);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* The output descriptor must not have O_APPEND set. */
|
||||
{
|
||||
int flags = __fcntl (outfd, F_GETFL);
|
||||
if (flags & O_APPEND)
|
||||
{
|
||||
__set_errno (EBADF);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid an overflow in the result. */
|
||||
if (length > SSIZE_MAX)
|
||||
length = SSIZE_MAX;
|
||||
|
||||
/* Main copying loop. The buffer size is arbitrary and is a
|
||||
trade-off between stack size consumption, cache usage, and
|
||||
amortization of system call overhead. */
|
||||
size_t copied = 0;
|
||||
char buf[8192];
|
||||
while (length > 0)
|
||||
{
|
||||
size_t to_read = length;
|
||||
if (to_read > sizeof (buf))
|
||||
to_read = sizeof (buf);
|
||||
|
||||
/* Fill the buffer. */
|
||||
ssize_t read_count;
|
||||
if (pinoff == NULL)
|
||||
read_count = read (infd, buf, to_read);
|
||||
else
|
||||
read_count = __libc_pread64 (infd, buf, to_read, *pinoff);
|
||||
if (read_count == 0)
|
||||
/* End of file reached prematurely. */
|
||||
return copied;
|
||||
if (read_count < 0)
|
||||
{
|
||||
if (copied > 0)
|
||||
/* Report the number of bytes copied so far. */
|
||||
return copied;
|
||||
return -1;
|
||||
}
|
||||
if (pinoff != NULL)
|
||||
*pinoff += read_count;
|
||||
|
||||
/* Write the buffer part which was read to the destination. */
|
||||
char *end = buf + read_count;
|
||||
for (char *p = buf; p < end; )
|
||||
{
|
||||
ssize_t write_count;
|
||||
if (poutoff == NULL)
|
||||
write_count = write (outfd, p, end - p);
|
||||
else
|
||||
write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff);
|
||||
if (write_count < 0)
|
||||
{
|
||||
/* Adjust the input read position to match what we have
|
||||
written, so that the caller can pick up after the
|
||||
error. */
|
||||
size_t written = p - buf;
|
||||
/* NB: This needs to be signed so that we can form the
|
||||
negative value below. */
|
||||
ssize_t overread = read_count - written;
|
||||
if (pinoff == NULL)
|
||||
{
|
||||
if (overread > 0)
|
||||
{
|
||||
/* We are on an error recovery path, so we
|
||||
cannot deal with failure here. */
|
||||
int save_errno = errno;
|
||||
(void) __libc_lseek64 (infd, -overread, SEEK_CUR);
|
||||
__set_errno (save_errno);
|
||||
}
|
||||
}
|
||||
else /* pinoff != NULL */
|
||||
*pinoff -= overread;
|
||||
|
||||
if (copied + written > 0)
|
||||
/* Report the number of bytes copied so far. */
|
||||
return copied + written;
|
||||
return -1;
|
||||
}
|
||||
p += write_count;
|
||||
if (poutoff != NULL)
|
||||
*poutoff += write_count;
|
||||
} /* Write loop. */
|
||||
|
||||
copied += read_count;
|
||||
length -= read_count;
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
+12
-4
@@ -1,4 +1,4 @@
|
||||
/* Generic implementation of copy_file_range.
|
||||
/* Stub implementation of copy_file_range.
|
||||
Copyright (C) 2017-2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
@@ -16,7 +16,15 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define COPY_FILE_RANGE_DECL
|
||||
#define COPY_FILE_RANGE copy_file_range
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <io/copy_file_range-compat.c>
|
||||
ssize_t
|
||||
copy_file_range (int infd, __off64_t *pinoff,
|
||||
int outfd, __off64_t *poutoff,
|
||||
size_t length, unsigned int flags)
|
||||
{
|
||||
__set_errno (ENOSYS);
|
||||
return -1;
|
||||
}
|
||||
stub_warning (copy_file_range)
|
||||
|
||||
+9
-551
@@ -20,22 +20,15 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xunistd.h>
|
||||
#ifdef CLONE_NEWNS
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
/* Boolean flags which indicate whether to use pointers with explicit
|
||||
output flags. */
|
||||
@@ -49,10 +42,6 @@ static int infd;
|
||||
static char *outfile;
|
||||
static int outfd;
|
||||
|
||||
/* Like the above, but on a different file system. xdevfile can be
|
||||
NULL if no suitable file system has been found. */
|
||||
static char *xdevfile;
|
||||
|
||||
/* Input and output offsets. Set according to do_inoff and do_outoff
|
||||
before the test. The offsets themselves are always set to
|
||||
zero. */
|
||||
@@ -61,13 +50,10 @@ static off64_t *pinoff;
|
||||
static off64_t outoff;
|
||||
static off64_t *poutoff;
|
||||
|
||||
/* These are a collection of copy sizes used in tests. The selection
|
||||
takes into account that the fallback implementation uses an
|
||||
internal buffer of 8192 bytes. */
|
||||
/* These are a collection of copy sizes used in tests. */
|
||||
enum { maximum_size = 99999 };
|
||||
static const int typical_sizes[] =
|
||||
{ 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, 16383, 16384, 16385,
|
||||
maximum_size };
|
||||
{ 0, 1, 2, 3, 1024, 2048, 4096, 8191, 8192, 8193, maximum_size };
|
||||
|
||||
/* The random contents of this array can be used as a pattern to check
|
||||
for correct write operations. */
|
||||
@@ -76,101 +62,6 @@ static unsigned char random_data[maximum_size];
|
||||
/* The size chosen by the test harness. */
|
||||
static int current_size;
|
||||
|
||||
/* Maximum writable file offset. Updated by find_maximum_offset
|
||||
below. */
|
||||
static off64_t maximum_offset;
|
||||
|
||||
/* Error code when crossing the offset. */
|
||||
static int maximum_offset_errno;
|
||||
|
||||
/* If true: Writes which cross the limit will fail. If false: Writes
|
||||
which cross the limit will result in a partial write. */
|
||||
static bool maximum_offset_hard_limit;
|
||||
|
||||
/* Fills maximum_offset etc. above. Truncates outfd as a side
|
||||
effect. */
|
||||
static void
|
||||
find_maximum_offset (void)
|
||||
{
|
||||
xftruncate (outfd, 0);
|
||||
if (maximum_offset != 0)
|
||||
return;
|
||||
|
||||
uint64_t upper = -1;
|
||||
upper >>= 1; /* Maximum of off64_t. */
|
||||
TEST_VERIFY ((off64_t) upper > 0);
|
||||
TEST_VERIFY ((off64_t) (upper + 1) < 0);
|
||||
if (lseek64 (outfd, upper, SEEK_SET) >= 0)
|
||||
{
|
||||
if (write (outfd, "", 1) == 1)
|
||||
FAIL_EXIT1 ("created a file larger than the off64_t range");
|
||||
}
|
||||
|
||||
uint64_t lower = 1024 * 1024; /* A reasonable minimum file size. */
|
||||
/* Loop invariant: writing at lower succeeds, writing at upper fails. */
|
||||
while (lower + 1 < upper)
|
||||
{
|
||||
uint64_t middle = (lower + upper) / 2;
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: remaining test range %" PRIu64 " .. %" PRIu64
|
||||
", probe at %" PRIu64 "\n", __func__, lower, upper, middle);
|
||||
xftruncate (outfd, 0);
|
||||
if (lseek64 (outfd, middle, SEEK_SET) >= 0
|
||||
&& write (outfd, "", 1) == 1)
|
||||
lower = middle;
|
||||
else
|
||||
upper = middle;
|
||||
}
|
||||
TEST_VERIFY (lower + 1 == upper);
|
||||
maximum_offset = lower;
|
||||
printf ("info: maximum writable file offset: %" PRIu64 " (%" PRIx64 ")\n",
|
||||
lower, lower);
|
||||
|
||||
/* Check that writing at the valid offset actually works. */
|
||||
xftruncate (outfd, 0);
|
||||
xlseek (outfd, lower, SEEK_SET);
|
||||
TEST_COMPARE (write (outfd, "", 1), 1);
|
||||
|
||||
/* Cross the boundary with a two-byte write. This can either result
|
||||
in a short write, or a failure. */
|
||||
xlseek (outfd, lower, SEEK_SET);
|
||||
ssize_t ret = write (outfd, " ", 2);
|
||||
if (ret < 0)
|
||||
{
|
||||
maximum_offset_errno = errno;
|
||||
maximum_offset_hard_limit = true;
|
||||
}
|
||||
else
|
||||
maximum_offset_hard_limit = false;
|
||||
|
||||
/* Check that writing at the next offset actually fails. This also
|
||||
obtains the expected errno value. */
|
||||
xftruncate (outfd, 0);
|
||||
const char *action;
|
||||
if (lseek64 (outfd, lower + 1, SEEK_SET) != 0)
|
||||
{
|
||||
if (write (outfd, "", 1) != -1)
|
||||
FAIL_EXIT1 ("write to impossible offset %" PRIu64 " succeeded",
|
||||
lower + 1);
|
||||
action = "writing";
|
||||
int errno_copy = errno;
|
||||
if (maximum_offset_hard_limit)
|
||||
TEST_COMPARE (errno_copy, maximum_offset_errno);
|
||||
else
|
||||
maximum_offset_errno = errno_copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
action = "seeking";
|
||||
maximum_offset_errno = errno;
|
||||
}
|
||||
printf ("info: %s out of range fails with %m (%d)\n",
|
||||
action, maximum_offset_errno);
|
||||
|
||||
xftruncate (outfd, 0);
|
||||
xlseek (outfd, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
/* Perform a copy of a file. */
|
||||
static void
|
||||
simple_file_copy (void)
|
||||
@@ -247,390 +138,6 @@ simple_file_copy (void)
|
||||
free (bytes);
|
||||
}
|
||||
|
||||
/* Test that reading from a pipe willfails. */
|
||||
static void
|
||||
pipe_as_source (void)
|
||||
{
|
||||
int pipefds[2];
|
||||
xpipe (pipefds);
|
||||
|
||||
for (int length = 0; length < 2; ++length)
|
||||
{
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: length=%d\n", __func__, length);
|
||||
|
||||
/* Make sure that there is something to copy in the pipe. */
|
||||
xwrite (pipefds[1], "@", 1);
|
||||
|
||||
TEST_COMPARE (copy_file_range (pipefds[0], pinoff, outfd, poutoff,
|
||||
length, 0), -1);
|
||||
/* Linux 4.10 and later return EINVAL. Older kernels return
|
||||
EXDEV. */
|
||||
TEST_VERIFY (errno == EINVAL || errno == EXDEV);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
TEST_COMPARE (outoff, 0);
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 0);
|
||||
|
||||
/* Make sure that nothing was read. */
|
||||
char buf = 'A';
|
||||
TEST_COMPARE (read (pipefds[0], &buf, 1), 1);
|
||||
TEST_COMPARE (buf, '@');
|
||||
}
|
||||
|
||||
xclose (pipefds[0]);
|
||||
xclose (pipefds[1]);
|
||||
}
|
||||
|
||||
/* Test that writing to a pipe fails. */
|
||||
static void
|
||||
pipe_as_destination (void)
|
||||
{
|
||||
/* Make sure that there is something to read in the input file. */
|
||||
xwrite (infd, "abc", 3);
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
int pipefds[2];
|
||||
xpipe (pipefds);
|
||||
|
||||
for (int length = 0; length < 2; ++length)
|
||||
{
|
||||
if (test_verbose > 0)
|
||||
printf ("info: %s: length=%d\n", __func__, length);
|
||||
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, pipefds[1], poutoff,
|
||||
length, 0), -1);
|
||||
/* Linux 4.10 and later return EINVAL. Older kernels return
|
||||
EXDEV. */
|
||||
TEST_VERIFY (errno == EINVAL || errno == EXDEV);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
TEST_COMPARE (outoff, 0);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
|
||||
/* Make sure that nothing was written. */
|
||||
struct pollfd pollfd = { .fd = pipefds[0], .events = POLLIN, };
|
||||
TEST_COMPARE (poll (&pollfd, 1, 0), 0);
|
||||
}
|
||||
|
||||
xclose (pipefds[0]);
|
||||
xclose (pipefds[1]);
|
||||
}
|
||||
|
||||
/* Test a write failure after (potentially) writing some bytes.
|
||||
Failure occurs near the start of the buffer. */
|
||||
static void
|
||||
delayed_write_failure_beginning (void)
|
||||
{
|
||||
/* We need to write something to provoke the error. */
|
||||
if (current_size == 0)
|
||||
return;
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
/* Write failure near the start. The actual error code varies among
|
||||
file systems. */
|
||||
find_maximum_offset ();
|
||||
off64_t where = maximum_offset;
|
||||
|
||||
if (current_size == 1)
|
||||
++where;
|
||||
outoff = where;
|
||||
if (do_outoff)
|
||||
xlseek (outfd, 1, SEEK_SET);
|
||||
else
|
||||
xlseek (outfd, where, SEEK_SET);
|
||||
if (maximum_offset_hard_limit || where > maximum_offset)
|
||||
{
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
if (do_outoff)
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
|
||||
else
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where);
|
||||
TEST_COMPARE (outoff, where);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The offset is not a hard limit. This means we write one
|
||||
byte. */
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), 1);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 1);
|
||||
TEST_COMPARE (inoff, 0);
|
||||
}
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 1);
|
||||
TEST_COMPARE (outoff, where + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), where + 1);
|
||||
TEST_COMPARE (outoff, where);
|
||||
}
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, where + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test a write failure after (potentially) writing some bytes.
|
||||
Failure occurs near the end of the buffer. */
|
||||
static void
|
||||
delayed_write_failure_end (void)
|
||||
{
|
||||
if (current_size <= 1)
|
||||
/* This would be same as the first test because there is not
|
||||
enough data to write to make a difference. */
|
||||
return;
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
find_maximum_offset ();
|
||||
off64_t where = maximum_offset - current_size + 1;
|
||||
if (current_size == sizeof (random_data))
|
||||
/* Otherwise we do not reach the non-writable byte. */
|
||||
++where;
|
||||
outoff = where;
|
||||
if (do_outoff)
|
||||
xlseek (outfd, 1, SEEK_SET);
|
||||
else
|
||||
xlseek (outfd, where, SEEK_SET);
|
||||
ssize_t ret = copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
TEST_COMPARE (ret, -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The first copy succeeded. This happens in the emulation
|
||||
because the internal buffer of limited size does not
|
||||
necessarily cross the off64_t boundary on the first write
|
||||
operation. */
|
||||
if (test_verbose > 0)
|
||||
printf ("info: copy_file_range (%zu) returned %zd\n",
|
||||
sizeof (random_data), ret);
|
||||
TEST_VERIFY (ret > 0);
|
||||
TEST_VERIFY (ret < maximum_size);
|
||||
struct stat64 st;
|
||||
xfstat (outfd, &st);
|
||||
TEST_COMPARE (st.st_size, where + ret);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, ret);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), ret);
|
||||
|
||||
char *buffer = xmalloc (ret);
|
||||
TEST_COMPARE (pread64 (outfd, buffer, ret, where), ret);
|
||||
TEST_VERIFY (memcmp (buffer, random_data, ret) == 0);
|
||||
free (buffer);
|
||||
|
||||
/* The second copy fails. */
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
sizeof (random_data), 0), -1);
|
||||
TEST_COMPARE (errno, maximum_offset_errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test a write failure across devices. */
|
||||
static void
|
||||
cross_device_failure (void)
|
||||
{
|
||||
if (xdevfile == NULL)
|
||||
/* Subtest not supported due to missing cross-device file. */
|
||||
return;
|
||||
|
||||
/* We need something to write. */
|
||||
xwrite (infd, random_data, sizeof (random_data));
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
int xdevfd = xopen (xdevfile, O_RDWR | O_LARGEFILE, 0);
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, xdevfd, poutoff,
|
||||
current_size, 0), -1);
|
||||
TEST_COMPARE (errno, EXDEV);
|
||||
TEST_COMPARE (xlseek (infd, 0, SEEK_CUR), 0);
|
||||
struct stat64 st;
|
||||
xfstat (xdevfd, &st);
|
||||
TEST_COMPARE (st.st_size, 0);
|
||||
|
||||
xclose (xdevfd);
|
||||
}
|
||||
|
||||
/* Try to exercise ENOSPC behavior with a tempfs file system (so that
|
||||
we do not have to fill up a regular file system to get the error).
|
||||
This function runs in a subprocess, so that we do not change the
|
||||
mount namespace of the actual test process. */
|
||||
static void
|
||||
enospc_failure_1 (void *closure)
|
||||
{
|
||||
#ifdef CLONE_NEWNS
|
||||
support_become_root ();
|
||||
|
||||
/* Make sure that we do not alter the file system mounts of the
|
||||
parents. */
|
||||
if (! support_enter_mount_namespace ())
|
||||
{
|
||||
printf ("warning: ENOSPC test skipped\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *mountpoint = closure;
|
||||
if (mount ("none", mountpoint, "tmpfs", MS_NODEV | MS_NOEXEC,
|
||||
"size=500k") != 0)
|
||||
{
|
||||
printf ("warning: could not mount tmpfs at %s: %m\n", mountpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The source file must reside on the same file system. */
|
||||
char *intmpfsfile = xasprintf ("%s/%s", mountpoint, "in");
|
||||
int intmpfsfd = xopen (intmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
|
||||
xwrite (intmpfsfd, random_data, sizeof (random_data));
|
||||
xlseek (intmpfsfd, 1, SEEK_SET);
|
||||
inoff = 1;
|
||||
|
||||
char *outtmpfsfile = xasprintf ("%s/%s", mountpoint, "out");
|
||||
int outtmpfsfd = xopen (outtmpfsfile, O_RDWR | O_CREAT | O_LARGEFILE, 0600);
|
||||
|
||||
/* Fill the file with data until ENOSPC is reached. */
|
||||
while (true)
|
||||
{
|
||||
ssize_t ret = write (outtmpfsfd, random_data, sizeof (random_data));
|
||||
if (ret < 0 && errno != ENOSPC)
|
||||
FAIL_EXIT1 ("write to %s: %m", outtmpfsfile);
|
||||
if (ret < sizeof (random_data))
|
||||
break;
|
||||
}
|
||||
TEST_COMPARE (write (outtmpfsfd, "", 1), -1);
|
||||
TEST_COMPARE (errno, ENOSPC);
|
||||
off64_t maxsize = xlseek (outtmpfsfd, 0, SEEK_CUR);
|
||||
TEST_VERIFY_EXIT (maxsize > sizeof (random_data));
|
||||
|
||||
/* Constructed the expected file contents. */
|
||||
char *expected = xmalloc (maxsize);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, expected, maxsize, 0), maxsize);
|
||||
/* Go back a little, so some bytes can be written. */
|
||||
enum { offset = 20000 };
|
||||
TEST_VERIFY_EXIT (offset < maxsize);
|
||||
TEST_VERIFY_EXIT (offset < sizeof (random_data));
|
||||
memcpy (expected + maxsize - offset, random_data + 1, offset);
|
||||
|
||||
if (do_outoff)
|
||||
{
|
||||
outoff = maxsize - offset;
|
||||
xlseek (outtmpfsfd, 2, SEEK_SET);
|
||||
}
|
||||
else
|
||||
xlseek (outtmpfsfd, -offset, SEEK_CUR);
|
||||
|
||||
/* First call is expected to succeed because we made room for some
|
||||
bytes. */
|
||||
TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
|
||||
maximum_size, 0), offset);
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1 + offset);
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (outoff, maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
|
||||
struct stat64 st;
|
||||
xfstat (outtmpfsfd, &st);
|
||||
TEST_COMPARE (st.st_size, maxsize);
|
||||
char *actual = xmalloc (st.st_size);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, actual, st.st_size, 0), st.st_size);
|
||||
TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
|
||||
|
||||
/* Second call should fail with ENOSPC. */
|
||||
TEST_COMPARE (copy_file_range (intmpfsfd, pinoff, outtmpfsfd, poutoff,
|
||||
maximum_size, 0), -1);
|
||||
TEST_COMPARE (errno, ENOSPC);
|
||||
|
||||
/* Offsets should be unchanged. */
|
||||
if (do_inoff)
|
||||
{
|
||||
TEST_COMPARE (inoff, 1 + offset);
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (intmpfsfd, 0, SEEK_CUR), 1 + offset);
|
||||
if (do_outoff)
|
||||
{
|
||||
TEST_COMPARE (outoff, maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), 2);
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_CUR), maxsize);
|
||||
TEST_COMPARE (xlseek (outtmpfsfd, 0, SEEK_END), maxsize);
|
||||
TEST_COMPARE (pread64 (outtmpfsfd, actual, maxsize, 0), maxsize);
|
||||
TEST_VERIFY (memcmp (expected, actual, maxsize) == 0);
|
||||
|
||||
free (actual);
|
||||
free (expected);
|
||||
|
||||
xclose (intmpfsfd);
|
||||
xclose (outtmpfsfd);
|
||||
free (intmpfsfile);
|
||||
free (outtmpfsfile);
|
||||
|
||||
#else /* !CLONE_NEWNS */
|
||||
puts ("warning: ENOSPC test skipped (no mount namespaces)");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Call enospc_failure_1 in a subprocess. */
|
||||
static void
|
||||
enospc_failure (void)
|
||||
{
|
||||
char *mountpoint
|
||||
= support_create_temp_directory ("tst-copy_file_range-enospc-");
|
||||
support_isolate_in_subprocess (enospc_failure_1, mountpoint);
|
||||
free (mountpoint);
|
||||
}
|
||||
|
||||
/* The target file descriptor must have O_APPEND enabled. */
|
||||
static void
|
||||
oappend_failure (void)
|
||||
{
|
||||
/* Add data, to make sure we do not fail because there is
|
||||
insufficient input data. */
|
||||
xwrite (infd, random_data, current_size);
|
||||
xlseek (infd, 0, SEEK_SET);
|
||||
|
||||
xclose (outfd);
|
||||
outfd = xopen (outfile, O_RDWR | O_APPEND, 0);
|
||||
TEST_COMPARE (copy_file_range (infd, pinoff, outfd, poutoff,
|
||||
current_size, 0), -1);
|
||||
TEST_COMPARE (errno, EBADF);
|
||||
}
|
||||
|
||||
/* Test that a short input file results in a shortened copy. */
|
||||
static void
|
||||
short_copy (void)
|
||||
@@ -721,14 +228,6 @@ struct test_case
|
||||
static struct test_case tests[] =
|
||||
{
|
||||
{ "simple_file_copy", simple_file_copy, .sizes = true },
|
||||
{ "pipe_as_source", pipe_as_source, },
|
||||
{ "pipe_as_destination", pipe_as_destination, },
|
||||
{ "delayed_write_failure_beginning", delayed_write_failure_beginning,
|
||||
.sizes = true },
|
||||
{ "delayed_write_failure_end", delayed_write_failure_end, .sizes = true },
|
||||
{ "cross_device_failure", cross_device_failure, .sizes = true },
|
||||
{ "enospc_failure", enospc_failure, },
|
||||
{ "oappend_failure", oappend_failure, .sizes = true },
|
||||
{ "short_copy", short_copy, .sizes = true },
|
||||
};
|
||||
|
||||
@@ -739,58 +238,18 @@ do_test (void)
|
||||
*p = rand () >> 24;
|
||||
|
||||
infd = create_temp_file ("tst-copy_file_range-in-", &infile);
|
||||
outfd = create_temp_file ("tst-copy_file_range-out-", &outfile);
|
||||
{
|
||||
int outfd = create_temp_file ("tst-copy_file_range-out-", &outfile);
|
||||
if (!support_descriptor_supports_holes (outfd))
|
||||
FAIL_UNSUPPORTED ("File %s does not support holes", outfile);
|
||||
xclose (outfd);
|
||||
}
|
||||
|
||||
/* Try to find a different directory from the default input/output
|
||||
file. */
|
||||
{
|
||||
struct stat64 instat;
|
||||
xfstat (infd, &instat);
|
||||
static const char *const candidates[] =
|
||||
{ NULL, "/var/tmp", "/dev/shm" };
|
||||
for (const char *const *c = candidates; c < array_end (candidates); ++c)
|
||||
ssize_t ret = copy_file_range (infd, NULL, outfd, NULL, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
const char *path = *c;
|
||||
char *to_free = NULL;
|
||||
if (path == NULL)
|
||||
{
|
||||
to_free = xreadlink ("/proc/self/exe");
|
||||
path = dirname (to_free);
|
||||
}
|
||||
|
||||
struct stat64 cstat;
|
||||
xstat (path, &cstat);
|
||||
if (cstat.st_dev == instat.st_dev)
|
||||
{
|
||||
free (to_free);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf ("info: using alternate temporary files directory: %s\n", path);
|
||||
xdevfile = xasprintf ("%s/tst-copy_file_range-xdev-XXXXXX", path);
|
||||
free (to_free);
|
||||
break;
|
||||
if (errno == ENOSYS)
|
||||
FAIL_UNSUPPORTED ("copy_file_range is not support on this system");
|
||||
FAIL_EXIT1 ("copy_file_range probing call: %m");
|
||||
}
|
||||
if (xdevfile != NULL)
|
||||
{
|
||||
int xdevfd = mkstemp (xdevfile);
|
||||
if (xdevfd < 0)
|
||||
FAIL_EXIT1 ("mkstemp (\"%s\"): %m", xdevfile);
|
||||
struct stat64 xdevst;
|
||||
xfstat (xdevfd, &xdevst);
|
||||
TEST_VERIFY (xdevst.st_dev != instat.st_dev);
|
||||
add_temp_file (xdevfile);
|
||||
xclose (xdevfd);
|
||||
}
|
||||
else
|
||||
puts ("warning: no alternate directory on different file system found");
|
||||
}
|
||||
xclose (infd);
|
||||
xclose (outfd);
|
||||
|
||||
for (do_inoff = 0; do_inoff < 2; ++do_inoff)
|
||||
for (do_outoff = 0; do_outoff < 2; ++do_outoff)
|
||||
@@ -832,7 +291,6 @@ do_test (void)
|
||||
|
||||
free (infile);
|
||||
free (outfile);
|
||||
free (xdevfile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+15
-2
@@ -65,7 +65,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
|
||||
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
|
||||
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
|
||||
tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
|
||||
tst-sprintf-ub tst-sprintf-chk-ub
|
||||
tst-sprintf-ub tst-sprintf-chk-ub tst-wfile-sync
|
||||
|
||||
tests-internal = tst-vtables tst-vtables-interposed tst-readline
|
||||
|
||||
@@ -73,6 +73,9 @@ ifeq (yes,$(build-shared))
|
||||
# Add test-fopenloc only if shared library is enabled since it depends on
|
||||
# shared localedata objects.
|
||||
tests += tst-fopenloc
|
||||
# Add tst-bz24228 only if shared library is enabled since it can never meet its
|
||||
# objective with static linking because the relevant code just is not there.
|
||||
tests += tst-bz24228
|
||||
endif
|
||||
test-srcs = test-freopen
|
||||
|
||||
@@ -157,11 +160,14 @@ CFLAGS-tst_putwc.c += -DOBJPFX=\"$(objpfx)\"
|
||||
CFLAGS-tst-sprintf-ub.c += -Wno-restrict
|
||||
CFLAGS-tst-sprintf-chk-ub.c += -Wno-restrict
|
||||
|
||||
LDFLAGS-tst-bz24228 = -Wl,--version-script=tst-bz24228.map
|
||||
|
||||
tst_wprintf2-ARGS = "Some Text"
|
||||
|
||||
test-fmemopen-ENV = MALLOC_TRACE=$(objpfx)test-fmemopen.mtrace
|
||||
tst-fopenloc-ENV = MALLOC_TRACE=$(objpfx)tst-fopenloc.mtrace
|
||||
tst-bz22415-ENV = MALLOC_TRACE=$(objpfx)tst-bz22415.mtrace
|
||||
tst-bz24228-ENV = MALLOC_TRACE=$(objpfx)tst-bz24228.mtrace
|
||||
|
||||
generated += test-fmemopen.mtrace test-fmemopen.check
|
||||
generated += tst-fopenloc.mtrace tst-fopenloc.check
|
||||
@@ -170,6 +176,7 @@ generated += tst-bz22415.mtrace tst-bz22415.check
|
||||
aux := fileops genops stdfiles stdio strops
|
||||
|
||||
ifeq ($(build-shared),yes)
|
||||
generated += tst-bz24228.mtrace tst-bz24228.check
|
||||
aux += oldfileops oldstdfiles
|
||||
endif
|
||||
|
||||
@@ -184,7 +191,8 @@ tests-special += $(objpfx)test-freopen.out $(objpfx)test-fmemopen-mem.out \
|
||||
ifeq (yes,$(build-shared))
|
||||
# Run tst-fopenloc-cmp.out and tst-openloc-mem.out only if shared
|
||||
# library is enabled since they depend on tst-fopenloc.out.
|
||||
tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out
|
||||
tests-special += $(objpfx)tst-fopenloc-cmp.out $(objpfx)tst-fopenloc-mem.out \
|
||||
$(objpfx)tst-bz24228-mem.out
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -212,6 +220,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
|
||||
@@ -235,3 +244,7 @@ $(objpfx)tst-fopenloc-mem.out: $(objpfx)tst-fopenloc.out
|
||||
$(objpfx)tst-bz22415-mem.out: $(objpfx)tst-bz22415.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-bz22415.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-bz24228-mem.out: $(objpfx)tst-bz24228.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-bz24228.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+12
-4
@@ -789,9 +789,16 @@ _IO_unbuffer_all (void)
|
||||
|
||||
for (fp = (FILE *) _IO_list_all; fp; fp = fp->_chain)
|
||||
{
|
||||
int legacy = 0;
|
||||
|
||||
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
|
||||
if (__glibc_unlikely (_IO_vtable_offset (fp) != 0))
|
||||
legacy = 1;
|
||||
#endif
|
||||
|
||||
if (! (fp->_flags & _IO_UNBUFFERED)
|
||||
/* Iff stream is un-orientated, it wasn't used. */
|
||||
&& fp->_mode != 0)
|
||||
&& (legacy || fp->_mode != 0))
|
||||
{
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
int cnt;
|
||||
@@ -805,7 +812,7 @@ _IO_unbuffer_all (void)
|
||||
__sched_yield ();
|
||||
#endif
|
||||
|
||||
if (! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
|
||||
if (! legacy && ! dealloc_buffers && !(fp->_flags & _IO_USER_BUF))
|
||||
{
|
||||
fp->_flags |= _IO_USER_BUF;
|
||||
|
||||
@@ -816,7 +823,7 @@ _IO_unbuffer_all (void)
|
||||
|
||||
_IO_SETBUF (fp, NULL, 0);
|
||||
|
||||
if (fp->_mode > 0)
|
||||
if (! legacy && fp->_mode > 0)
|
||||
_IO_wsetb (fp, NULL, NULL, 0);
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
@@ -827,7 +834,8 @@ _IO_unbuffer_all (void)
|
||||
|
||||
/* Make sure that never again the wide char functions can be
|
||||
used. */
|
||||
fp->_mode = -1;
|
||||
if (! legacy)
|
||||
fp->_mode = -1;
|
||||
}
|
||||
|
||||
#ifdef _IO_MTSAFE_IO
|
||||
|
||||
@@ -87,6 +87,11 @@ _IO_check_libio (void)
|
||||
stdout->_vtable_offset = stderr->_vtable_offset =
|
||||
((int) sizeof (struct _IO_FILE)
|
||||
- (int) sizeof (struct _IO_FILE_complete));
|
||||
|
||||
if (_IO_stdin_.vtable != &_IO_old_file_jumps
|
||||
|| _IO_stdout_.vtable != &_IO_old_file_jumps
|
||||
|| _IO_stderr_.vtable != &_IO_old_file_jumps)
|
||||
IO_set_accept_foreign_vtables (&_IO_vtable_check);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* BZ #24228 check for memory corruption in legacy libio
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <mcheck.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,5 @@
|
||||
# Hide the symbol from libc.so.6 to switch to the libio/oldfileops.c
|
||||
# implementation when it is available for the architecture.
|
||||
{
|
||||
local: _IO_stdin_used;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Test that _IO_wfile_sync does not crash (bug 20568).
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
TEST_VERIFY_EXIT (setlocale (LC_ALL, "de_DE.UTF-8") != NULL);
|
||||
/* Fill the stdio buffer and advance the read pointer. */
|
||||
TEST_VERIFY_EXIT (fgetwc (stdin) != WEOF);
|
||||
/* This calls _IO_wfile_sync, it should not crash. */
|
||||
TEST_VERIFY_EXIT (setvbuf (stdin, NULL, _IONBF, 0) == 0);
|
||||
/* Verify that the external file offset has been synchronized. */
|
||||
TEST_COMPARE (xlseek (0, 0, SEEK_CUR), 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1 @@
|
||||
This is a test of _IO_wfile_sync.
|
||||
+3
-2
@@ -508,11 +508,12 @@ _IO_wfile_sync (FILE *fp)
|
||||
generate the wide characters up to the current reading
|
||||
position. */
|
||||
int nread;
|
||||
|
||||
size_t wnread = (fp->_wide_data->_IO_read_ptr
|
||||
- fp->_wide_data->_IO_read_base);
|
||||
fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
|
||||
nread = (*cv->__codecvt_do_length) (cv, &fp->_wide_data->_IO_state,
|
||||
fp->_IO_read_base,
|
||||
fp->_IO_read_end, delta);
|
||||
fp->_IO_read_end, wnread);
|
||||
fp->_IO_read_ptr = fp->_IO_read_base + nread;
|
||||
delta = -(fp->_IO_read_end - fp->_IO_read_base - nread);
|
||||
}
|
||||
|
||||
@@ -14946,12 +14946,14 @@ am_pm "<U5348><U524D>";"<U5348><U5F8C>"
|
||||
|
||||
t_fmt_ampm "%p%I<U6642>%M<U5206>%S<U79D2>"
|
||||
|
||||
era "+:2:1990//01//01:+*:<U5E73><U6210>:%EC%Ey<U5E74>";/
|
||||
era "+:2:2020//01//01:+*:<U4EE4><U548C>:%EC%Ey<U5E74>";/
|
||||
"+:1:2019//05//01:2019//12//31:<U4EE4><U548C>:%EC<U5143><U5E74>";/
|
||||
"+:2:1990//01//01:2019//04//30:<U5E73><U6210>:%EC%Ey<U5E74>";/
|
||||
"+:1:1989//01//08:1989//12//31:<U5E73><U6210>:%EC<U5143><U5E74>";/
|
||||
"+:2:1927//01//01:1989//01//07:<U662D><U548C>:%EC%Ey<U5E74>";/
|
||||
"+:1:1926//12//25:1926//12//31:<U662D><U548C>:%EC<U5143><U5E74>";/
|
||||
"+:2:1913//01//01:1926//12//24:<U5927><U6B63>:%EC%Ey<U5E74>";/
|
||||
"+:2:1912//07//30:1912//12//31:<U5927><U6B63>:%EC<U5143><U5E74>";/
|
||||
"+:1:1912//07//30:1912//12//31:<U5927><U6B63>:%EC<U5143><U5E74>";/
|
||||
"+:6:1873//01//01:1912//07//29:<U660E><U6CBB>:%EC%Ey<U5E74>";/
|
||||
"+:1:0001//01//01:1872//12//31:<U897F><U66A6>:%EC%Ey<U5E74>";/
|
||||
"+:1:-0001//12//31:-*:<U7D00><U5143><U524D>:%EC%Ey<U5E74>"
|
||||
|
||||
+3
-1
@@ -54,7 +54,7 @@ tests-internal += \
|
||||
tst-dynarray-at-fail \
|
||||
|
||||
ifneq (no,$(have-tunables))
|
||||
tests += tst-malloc-usable-tunables
|
||||
tests += tst-malloc-usable-tunables tst-mxfast
|
||||
tests-static += tst-malloc-usable-static-tunables
|
||||
endif
|
||||
|
||||
@@ -196,6 +196,8 @@ tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
|
||||
tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
|
||||
tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
|
||||
|
||||
tst-mxfast-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=0:glibc.malloc.mxfast=0
|
||||
|
||||
ifeq ($(experimental-malloc),yes)
|
||||
CPPFLAGS-malloc.c += -DUSE_TCACHE=1
|
||||
else
|
||||
|
||||
@@ -237,6 +237,7 @@ TUNABLE_CALLBACK_FNDECL (set_tcache_max, size_t)
|
||||
TUNABLE_CALLBACK_FNDECL (set_tcache_count, size_t)
|
||||
TUNABLE_CALLBACK_FNDECL (set_tcache_unsorted_limit, size_t)
|
||||
#endif
|
||||
TUNABLE_CALLBACK_FNDECL (set_mxfast, size_t)
|
||||
#else
|
||||
/* Initialization routine. */
|
||||
#include <string.h>
|
||||
@@ -324,6 +325,7 @@ ptmalloc_init (void)
|
||||
TUNABLE_GET (tcache_unsorted_limit, size_t,
|
||||
TUNABLE_CALLBACK (set_tcache_unsorted_limit));
|
||||
# endif
|
||||
TUNABLE_GET (mxfast, size_t, TUNABLE_CALLBACK (set_mxfast));
|
||||
#else
|
||||
const char *s = NULL;
|
||||
if (__glibc_likely (_environ != NULL))
|
||||
|
||||
+37
-17
@@ -321,6 +321,10 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line,
|
||||
/* This is another arbitrary limit, which tunables can change. Each
|
||||
tcache bin will hold at most this number of chunks. */
|
||||
# define TCACHE_FILL_COUNT 7
|
||||
|
||||
/* Maximum chunks in tcache bins for tunables. This value must fit the range
|
||||
of tcache->counts[] entries, else they may overflow. */
|
||||
# define MAX_TCACHE_COUNT UINT16_MAX
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1631,7 +1635,7 @@ static INTERNAL_SIZE_T global_max_fast;
|
||||
|
||||
#define set_max_fast(s) \
|
||||
global_max_fast = (((s) == 0) \
|
||||
? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
|
||||
? MIN_CHUNK_SIZE / 2 : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
|
||||
|
||||
static inline INTERNAL_SIZE_T
|
||||
get_max_fast (void)
|
||||
@@ -2915,7 +2919,7 @@ typedef struct tcache_entry
|
||||
time), this is for performance reasons. */
|
||||
typedef struct tcache_perthread_struct
|
||||
{
|
||||
char counts[TCACHE_MAX_BINS];
|
||||
uint16_t counts[TCACHE_MAX_BINS];
|
||||
tcache_entry *entries[TCACHE_MAX_BINS];
|
||||
} tcache_perthread_struct;
|
||||
|
||||
@@ -2928,7 +2932,6 @@ static __always_inline void
|
||||
tcache_put (mchunkptr chunk, size_t tc_idx)
|
||||
{
|
||||
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
|
||||
assert (tc_idx < TCACHE_MAX_BINS);
|
||||
|
||||
/* Mark this chunk as "in the tcache" so the test in _int_free will
|
||||
detect a double free. */
|
||||
@@ -2945,8 +2948,6 @@ static __always_inline void *
|
||||
tcache_get (size_t tc_idx)
|
||||
{
|
||||
tcache_entry *e = tcache->entries[tc_idx];
|
||||
assert (tc_idx < TCACHE_MAX_BINS);
|
||||
assert (tcache->entries[tc_idx] > 0);
|
||||
tcache->entries[tc_idx] = e->next;
|
||||
--(tcache->counts[tc_idx]);
|
||||
e->key = NULL;
|
||||
@@ -3051,9 +3052,8 @@ __libc_malloc (size_t bytes)
|
||||
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
if (tc_idx < mp_.tcache_bins
|
||||
/*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
|
||||
&& tcache
|
||||
&& tcache->entries[tc_idx] != NULL)
|
||||
&& tcache->counts[tc_idx] > 0)
|
||||
{
|
||||
return tcache_get (tc_idx);
|
||||
}
|
||||
@@ -3876,10 +3876,14 @@ _int_malloc (mstate av, size_t bytes)
|
||||
{
|
||||
victim->fd_nextsize = fwd;
|
||||
victim->bk_nextsize = fwd->bk_nextsize;
|
||||
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
|
||||
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
|
||||
fwd->bk_nextsize = victim;
|
||||
victim->bk_nextsize->fd_nextsize = victim;
|
||||
}
|
||||
bck = fwd->bk;
|
||||
if (bck->fd != fwd)
|
||||
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5120,8 +5124,11 @@ static inline int
|
||||
__always_inline
|
||||
do_set_tcache_count (size_t value)
|
||||
{
|
||||
LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
|
||||
mp_.tcache_count = value;
|
||||
if (value <= MAX_TCACHE_COUNT)
|
||||
{
|
||||
LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
|
||||
mp_.tcache_count = value;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5135,6 +5142,19 @@ do_set_tcache_unsorted_limit (size_t value)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
__always_inline
|
||||
do_set_mxfast (size_t value)
|
||||
{
|
||||
if (value >= 0 && value <= MAX_FAST_SIZE)
|
||||
{
|
||||
LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
|
||||
set_max_fast (value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
__libc_mallopt (int param_number, int value)
|
||||
{
|
||||
@@ -5154,13 +5174,7 @@ __libc_mallopt (int param_number, int value)
|
||||
switch (param_number)
|
||||
{
|
||||
case M_MXFAST:
|
||||
if (value >= 0 && value <= MAX_FAST_SIZE)
|
||||
{
|
||||
LIBC_PROBE (memory_mallopt_mxfast, 2, value, get_max_fast ());
|
||||
set_max_fast (value);
|
||||
}
|
||||
else
|
||||
res = 0;
|
||||
do_set_mxfast (value);
|
||||
break;
|
||||
|
||||
case M_TRIM_THRESHOLD:
|
||||
@@ -5426,6 +5440,12 @@ __malloc_info (int options, FILE *fp)
|
||||
|
||||
__libc_lock_lock (ar_ptr->mutex);
|
||||
|
||||
/* Account for top chunk. The top-most available chunk is
|
||||
treated specially and is never in any bin. See "initial_top"
|
||||
comments. */
|
||||
avail = chunksize (ar_ptr->top);
|
||||
nblocks = 1; /* Top always exists. */
|
||||
|
||||
for (size_t i = 0; i < NFASTBINS; ++i)
|
||||
{
|
||||
mchunkptr p = fastbin (ar_ptr, i);
|
||||
@@ -5511,7 +5531,7 @@ __malloc_info (int options, FILE *fp)
|
||||
|
||||
for (size_t i = 0; i < nsizes; ++i)
|
||||
if (sizes[i].count != 0 && i != NFASTBINS)
|
||||
fprintf (fp, " \
|
||||
fprintf (fp, "\
|
||||
<size from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n",
|
||||
sizes[i].from, sizes[i].to, sizes[i].total, sizes[i].count);
|
||||
|
||||
|
||||
+46
-26
@@ -121,6 +121,41 @@ lock_and_info (const void *caller, Dl_info *mem)
|
||||
return res;
|
||||
}
|
||||
|
||||
static void tr_freehook (void *, const void *);
|
||||
static void * tr_mallochook (size_t, const void *);
|
||||
static void * tr_reallochook (void *, size_t, const void *);
|
||||
static void * tr_memalignhook (size_t, size_t, const void *);
|
||||
|
||||
/* Set all the default non-trace hooks. */
|
||||
static __always_inline void
|
||||
set_default_hooks (void)
|
||||
{
|
||||
__free_hook = tr_old_free_hook;
|
||||
__malloc_hook = tr_old_malloc_hook;
|
||||
__realloc_hook = tr_old_realloc_hook;
|
||||
__memalign_hook = tr_old_memalign_hook;
|
||||
}
|
||||
|
||||
/* Set all of the tracing hooks used for mtrace. */
|
||||
static __always_inline void
|
||||
set_trace_hooks (void)
|
||||
{
|
||||
__free_hook = tr_freehook;
|
||||
__malloc_hook = tr_mallochook;
|
||||
__realloc_hook = tr_reallochook;
|
||||
__memalign_hook = tr_memalignhook;
|
||||
}
|
||||
|
||||
/* Save the current set of hooks as the default hooks. */
|
||||
static __always_inline void
|
||||
save_default_hooks (void)
|
||||
{
|
||||
tr_old_free_hook = __free_hook;
|
||||
tr_old_malloc_hook = __malloc_hook;
|
||||
tr_old_realloc_hook = __realloc_hook;
|
||||
tr_old_memalign_hook = __memalign_hook;
|
||||
}
|
||||
|
||||
static void
|
||||
tr_freehook (void *ptr, const void *caller)
|
||||
{
|
||||
@@ -138,12 +173,12 @@ tr_freehook (void *ptr, const void *caller)
|
||||
tr_break ();
|
||||
__libc_lock_lock (lock);
|
||||
}
|
||||
__free_hook = tr_old_free_hook;
|
||||
set_default_hooks ();
|
||||
if (tr_old_free_hook != NULL)
|
||||
(*tr_old_free_hook)(ptr, caller);
|
||||
else
|
||||
free (ptr);
|
||||
__free_hook = tr_freehook;
|
||||
set_trace_hooks ();
|
||||
__libc_lock_unlock (lock);
|
||||
}
|
||||
|
||||
@@ -155,12 +190,12 @@ tr_mallochook (size_t size, const void *caller)
|
||||
Dl_info mem;
|
||||
Dl_info *info = lock_and_info (caller, &mem);
|
||||
|
||||
__malloc_hook = tr_old_malloc_hook;
|
||||
set_default_hooks ();
|
||||
if (tr_old_malloc_hook != NULL)
|
||||
hdr = (void *) (*tr_old_malloc_hook)(size, caller);
|
||||
else
|
||||
hdr = (void *) malloc (size);
|
||||
__malloc_hook = tr_mallochook;
|
||||
set_trace_hooks ();
|
||||
|
||||
tr_where (caller, info);
|
||||
/* We could be printing a NULL here; that's OK. */
|
||||
@@ -185,16 +220,12 @@ tr_reallochook (void *ptr, size_t size, const void *caller)
|
||||
Dl_info mem;
|
||||
Dl_info *info = lock_and_info (caller, &mem);
|
||||
|
||||
__free_hook = tr_old_free_hook;
|
||||
__malloc_hook = tr_old_malloc_hook;
|
||||
__realloc_hook = tr_old_realloc_hook;
|
||||
set_default_hooks ();
|
||||
if (tr_old_realloc_hook != NULL)
|
||||
hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
|
||||
else
|
||||
hdr = (void *) realloc (ptr, size);
|
||||
__free_hook = tr_freehook;
|
||||
__malloc_hook = tr_mallochook;
|
||||
__realloc_hook = tr_reallochook;
|
||||
set_trace_hooks ();
|
||||
|
||||
tr_where (caller, info);
|
||||
if (hdr == NULL)
|
||||
@@ -230,14 +261,12 @@ tr_memalignhook (size_t alignment, size_t size, const void *caller)
|
||||
Dl_info mem;
|
||||
Dl_info *info = lock_and_info (caller, &mem);
|
||||
|
||||
__memalign_hook = tr_old_memalign_hook;
|
||||
__malloc_hook = tr_old_malloc_hook;
|
||||
set_default_hooks ();
|
||||
if (tr_old_memalign_hook != NULL)
|
||||
hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
|
||||
else
|
||||
hdr = (void *) memalign (alignment, size);
|
||||
__memalign_hook = tr_memalignhook;
|
||||
__malloc_hook = tr_mallochook;
|
||||
set_trace_hooks ();
|
||||
|
||||
tr_where (caller, info);
|
||||
/* We could be printing a NULL here; that's OK. */
|
||||
@@ -305,14 +334,8 @@ mtrace (void)
|
||||
malloc_trace_buffer = mtb;
|
||||
setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
|
||||
fprintf (mallstream, "= Start\n");
|
||||
tr_old_free_hook = __free_hook;
|
||||
__free_hook = tr_freehook;
|
||||
tr_old_malloc_hook = __malloc_hook;
|
||||
__malloc_hook = tr_mallochook;
|
||||
tr_old_realloc_hook = __realloc_hook;
|
||||
__realloc_hook = tr_reallochook;
|
||||
tr_old_memalign_hook = __memalign_hook;
|
||||
__memalign_hook = tr_memalignhook;
|
||||
save_default_hooks ();
|
||||
set_trace_hooks ();
|
||||
#ifdef _LIBC
|
||||
if (!added_atexit_handler)
|
||||
{
|
||||
@@ -338,10 +361,7 @@ muntrace (void)
|
||||
file. */
|
||||
FILE *f = mallstream;
|
||||
mallstream = NULL;
|
||||
__free_hook = tr_old_free_hook;
|
||||
__malloc_hook = tr_old_malloc_hook;
|
||||
__realloc_hook = tr_old_realloc_hook;
|
||||
__memalign_hook = tr_old_memalign_hook;
|
||||
set_default_hooks ();
|
||||
|
||||
fprintf (f, "= End\n");
|
||||
fclose (f);
|
||||
|
||||
@@ -72,13 +72,28 @@ test_large_allocations (size_t size)
|
||||
void * ptr_to_realloc;
|
||||
|
||||
test_setup ();
|
||||
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
|
||||
TEST_VERIFY (malloc (size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
ptr_to_realloc = malloc (16);
|
||||
TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
free (ptr_to_realloc);
|
||||
|
||||
@@ -135,7 +150,13 @@ test_large_aligned_allocations (size_t size)
|
||||
for (align = 1; align <= pagesize; align *= 2)
|
||||
{
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
TEST_VERIFY (memalign (align, size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
/* posix_memalign expects an alignment that is a power of 2 *and* a
|
||||
@@ -151,7 +172,13 @@ test_large_aligned_allocations (size_t size)
|
||||
if ((size % align) == 0)
|
||||
{
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
TEST_VERIFY (aligned_alloc (align, size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
}
|
||||
}
|
||||
@@ -159,11 +186,23 @@ test_large_aligned_allocations (size_t size)
|
||||
/* Both valloc and pvalloc return page-aligned memory. */
|
||||
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
TEST_VERIFY (valloc (size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
TEST_VERIFY (pvalloc (size) == NULL);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
TEST_VERIFY (errno == ENOMEM);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libc-diag.h>
|
||||
|
||||
static int errors = 0;
|
||||
|
||||
@@ -41,9 +42,18 @@ do_test (void)
|
||||
|
||||
errno = 0;
|
||||
|
||||
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
|
||||
/* An attempt to allocate a huge value should return NULL and set
|
||||
errno to ENOMEM. */
|
||||
p = memalign (sizeof (void *), -1);
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
#endif
|
||||
|
||||
save = errno;
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Test that glibc.malloc.mxfast tunable works.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test verifies that setting the glibc.malloc.mxfast tunable to
|
||||
zero results in free'd blocks being returned to the small bins, not
|
||||
the fast bins. */
|
||||
|
||||
#include <malloc.h>
|
||||
#include <support/check.h>
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
struct mallinfo m;
|
||||
char *volatile p1;
|
||||
char *volatile p2;
|
||||
|
||||
/* Arbitrary value; must be in default fastbin range. */
|
||||
p1 = malloc (3);
|
||||
/* Something large so that p1 isn't a "top block" */
|
||||
p2 = malloc (512);
|
||||
free (p1);
|
||||
|
||||
m = mallinfo ();
|
||||
|
||||
/* This will fail if there are any blocks in the fastbins. */
|
||||
TEST_COMPARE (m.smblks, 0);
|
||||
|
||||
/* To keep gcc happy. */
|
||||
free (p2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+5
-5
@@ -1404,10 +1404,13 @@ failure occurs. The return value is zero if the end of the input file
|
||||
is encountered immediately.
|
||||
|
||||
If no bytes can be copied, to report an error, @code{copy_file_range}
|
||||
returns the value @math{-1} and sets @code{errno}. The following
|
||||
@code{errno} error conditions are specific to this function:
|
||||
returns the value @math{-1} and sets @code{errno}. The table below
|
||||
lists some of the error conditions for this function.
|
||||
|
||||
@table @code
|
||||
@item ENOSYS
|
||||
The kernel does not implement the required functionality.
|
||||
|
||||
@item EISDIR
|
||||
At least one of the descriptors @var{inputfd} or @var{outputfd} refers
|
||||
to a directory.
|
||||
@@ -1437,9 +1440,6 @@ reading.
|
||||
|
||||
The argument @var{outputfd} is not a valid file descriptor open for
|
||||
writing, or @var{outputfd} has been opened with @code{O_APPEND}.
|
||||
|
||||
@item EXDEV
|
||||
The input and output files reside on different file systems.
|
||||
@end table
|
||||
|
||||
In addition, @code{copy_file_range} can fail with the error codes
|
||||
|
||||
+14
-2
@@ -189,8 +189,8 @@ per-thread cache. The default (and maximum) value is 1032 bytes on
|
||||
|
||||
@deftp Tunable glibc.malloc.tcache_count
|
||||
The maximum number of chunks of each size to cache. The default is 7.
|
||||
There is no upper limit, other than available system memory. If set
|
||||
to zero, the per-thread cache is effectively disabled.
|
||||
The upper limit is 65535. If set to zero, the per-thread cache is effectively
|
||||
disabled.
|
||||
|
||||
The approximate maximum overhead of the per-thread cache is thus equal
|
||||
to the number of bins times the chunk count in each bin times the size
|
||||
@@ -214,6 +214,18 @@ pre-fill the per-thread cache with. The default, or when set to zero,
|
||||
is no limit.
|
||||
@end deftp
|
||||
|
||||
@deftp Tunable glibc.malloc.mxfast
|
||||
One of the optimizations malloc uses is to maintain a series of ``fast
|
||||
bins'' that hold chunks up to a specific size. The default and
|
||||
maximum size which may be held this way is 80 bytes on 32-bit systems
|
||||
or 160 bytes on 64-bit systems. Applications which value size over
|
||||
speed may choose to reduce the size of requests which are serviced
|
||||
from fast bins with this tunable. Note that the value specified
|
||||
includes malloc's internal overhead, which is normally the size of one
|
||||
pointer, so add 4 on 32-bit systems or 8 on 64-bit systems to the size
|
||||
passed to @code{malloc} for the largest bin size to enable.
|
||||
@end deftp
|
||||
|
||||
@node Elision Tunables
|
||||
@section Elision Tunables
|
||||
@cindex elision tunables
|
||||
|
||||
+2
-1
@@ -319,7 +319,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
|
||||
tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
|
||||
tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \
|
||||
tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
|
||||
tst-rwlock-pwn
|
||||
tst-rwlock-pwn \
|
||||
tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall
|
||||
|
||||
tests-internal := tst-rwlock19 tst-rwlock20 \
|
||||
tst-sem11 tst-sem12 tst-sem13 \
|
||||
|
||||
@@ -572,7 +572,9 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
|
||||
|
||||
/* Place the thread descriptor at the end of the stack. */
|
||||
#if TLS_TCB_AT_TP
|
||||
pd = (struct pthread *) ((char *) mem + size) - 1;
|
||||
pd = (struct pthread *) ((((uintptr_t) mem + size)
|
||||
- TLS_TCB_SIZE)
|
||||
& ~__static_tls_align_m1);
|
||||
#elif TLS_DTV_AT_TP
|
||||
pd = (struct pthread *) ((((uintptr_t) mem + size
|
||||
- __static_tls_size)
|
||||
|
||||
@@ -86,6 +86,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return,
|
||||
pthread_cleanup_pop (0);
|
||||
}
|
||||
|
||||
void *pd_result = pd->result;
|
||||
if (__glibc_likely (result == 0))
|
||||
{
|
||||
/* We mark the thread as terminated and as joined. */
|
||||
@@ -93,7 +94,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return,
|
||||
|
||||
/* Store the return value if the caller is interested. */
|
||||
if (thread_return != NULL)
|
||||
*thread_return = pd->result;
|
||||
*thread_return = pd_result;
|
||||
|
||||
/* Free the TCB. */
|
||||
__free_tcb (pd);
|
||||
@@ -101,7 +102,7 @@ __pthread_timedjoin_ex (pthread_t threadid, void **thread_return,
|
||||
else
|
||||
pd->joinid = NULL;
|
||||
|
||||
LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd->result);
|
||||
LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lll_trylock (mutex->__data.__lock) == 0)
|
||||
if (atomic_load_relaxed (&(mutex->__data.__lock)) == 0
|
||||
&& lll_trylock (mutex->__data.__lock) == 0)
|
||||
{
|
||||
/* Record the ownership. */
|
||||
mutex->__data.__owner = id;
|
||||
@@ -79,7 +80,10 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
/*FALL THROUGH*/
|
||||
case PTHREAD_MUTEX_ADAPTIVE_NP:
|
||||
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||
if (lll_trylock (mutex->__data.__lock) != 0)
|
||||
/* Mutex type is already loaded, lock check overhead should
|
||||
be minimal. */
|
||||
if (atomic_load_relaxed (&(mutex->__data.__lock)) != 0
|
||||
|| lll_trylock (mutex->__data.__lock) != 0)
|
||||
break;
|
||||
|
||||
/* Record the ownership. */
|
||||
@@ -94,6 +98,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
|
||||
@@ -119,7 +126,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
|
||||
@@ -135,6 +147,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;
|
||||
@@ -142,6 +156,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);
|
||||
|
||||
@@ -160,6 +176,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;
|
||||
@@ -173,13 +192,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;
|
||||
@@ -211,10 +237,15 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -223,12 +254,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. */
|
||||
@@ -250,6 +285,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;
|
||||
@@ -270,6 +308,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;
|
||||
@@ -287,7 +328,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
|
||||
@@ -310,13 +356,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,15 +94,22 @@ __pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
|
||||
/* Same as in __pthread_rwlock_rdlock_full:
|
||||
We started the read phase, so we are also responsible for
|
||||
updating the write-phase futex. Relaxed MO is sufficient.
|
||||
Note that there can be no other reader that we have to wake
|
||||
because all other readers will see the read phase started by us
|
||||
(or they will try to start it themselves); if a writer started
|
||||
the read phase, we cannot have started it. Furthermore, we
|
||||
cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
|
||||
overwrite the value set by the most recent writer (or the readers
|
||||
before it in case of explicit hand-over) and we know that there
|
||||
are no waiting readers. */
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
We have to do the same steps as a writer would when handing over the
|
||||
read phase to use because other readers cannot distinguish between
|
||||
us and the writer.
|
||||
Note that __pthread_rwlock_tryrdlock callers will not have to be
|
||||
woken up because they will either see the read phase started by us
|
||||
or they will try to start it themselves; however, callers of
|
||||
__pthread_rwlock_rdlock_full just increase the reader count and then
|
||||
check what state the lock is in, so they cannot distinguish between
|
||||
us and a writer that acquired and released the lock in the
|
||||
meantime. */
|
||||
if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||
& PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
{
|
||||
int private = __pthread_rwlock_get_private (rwlock);
|
||||
futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -46,8 +46,15 @@ __pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
|
||||
&rwlock->__data.__readers, &r,
|
||||
r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||
{
|
||||
/* We have become the primary writer and we cannot have shared
|
||||
the PTHREAD_RWLOCK_FUTEX_USED flag with someone else, so we
|
||||
can simply enable blocking (see full wrlock code). */
|
||||
atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
/* If we started a write phase, we need to enable readers to
|
||||
wait. If we did not, we must not change it because other threads
|
||||
may have set the PTHREAD_RWLOCK_FUTEX_USED in the meantime. */
|
||||
if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
THREAD_GETMEM (THREAD_SELF, tid));
|
||||
return 0;
|
||||
|
||||
@@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle)
|
||||
}
|
||||
|
||||
void
|
||||
__run_fork_handlers (enum __run_fork_handler_type who)
|
||||
__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
|
||||
{
|
||||
struct fork_handler *runp;
|
||||
|
||||
if (who == atfork_run_prepare)
|
||||
{
|
||||
lll_lock (atfork_lock, LLL_PRIVATE);
|
||||
if (do_locking)
|
||||
lll_lock (atfork_lock, LLL_PRIVATE);
|
||||
size_t sl = fork_handler_list_size (&fork_handlers);
|
||||
for (size_t i = sl; i > 0; i--)
|
||||
{
|
||||
@@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_handler_type who)
|
||||
else if (who == atfork_run_parent && runp->parent_handler)
|
||||
runp->parent_handler ();
|
||||
}
|
||||
lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||
if (do_locking)
|
||||
lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* For a full analysis see comment:
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14
|
||||
|
||||
Provided here for reference:
|
||||
|
||||
--- Analysis of pthread_rwlock_tryrdlock() stall ---
|
||||
A read lock begins to execute.
|
||||
|
||||
In __pthread_rwlock_rdlock_full:
|
||||
|
||||
We can attempt a read lock, but find that the lock is
|
||||
in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit
|
||||
is set), and the lock is held by a primary writer
|
||||
(PTHREAD_RWLOCK_WRLOCKED is set). In this case we must
|
||||
wait for explicit hand over from the writer to us or
|
||||
one of the other waiters. The read lock threads are
|
||||
about to execute:
|
||||
|
||||
341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers,
|
||||
342 (1 << PTHREAD_RWLOCK_READER_SHIFT))
|
||||
343 + (1 << PTHREAD_RWLOCK_READER_SHIFT));
|
||||
|
||||
An unlock beings to execute.
|
||||
|
||||
Then in __pthread_rwlock_wrunlock:
|
||||
|
||||
547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
...
|
||||
549 while (!atomic_compare_exchange_weak_release
|
||||
550 (&rwlock->__data.__readers, &r,
|
||||
551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||
553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||
554 {
|
||||
...
|
||||
556 }
|
||||
|
||||
We clear PTHREAD_RWLOCK_WRLOCKED, and if there are
|
||||
no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE.
|
||||
|
||||
Back in the read lock.
|
||||
|
||||
The read lock adjusts __readres as above.
|
||||
|
||||
383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0
|
||||
384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||
385 {
|
||||
...
|
||||
390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r,
|
||||
391 r ^ PTHREAD_RWLOCK_WRPHASE))
|
||||
392 {
|
||||
|
||||
And then attemps to start the read phase.
|
||||
|
||||
Assume there happens to be a tryrdlock at this point, noting
|
||||
that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE
|
||||
is 1. So the try lock attemps to start the read phase.
|
||||
|
||||
In __pthread_rwlock_tryrdlock:
|
||||
|
||||
44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||
45 {
|
||||
...
|
||||
49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
|
||||
50 && (rwlock->__data.__flags
|
||||
51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
|
||||
52 return EBUSY;
|
||||
53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
|
||||
54 }
|
||||
...
|
||||
89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
|
||||
90 &r, rnew));
|
||||
|
||||
And succeeds.
|
||||
|
||||
Back in the write unlock:
|
||||
|
||||
557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0)
|
||||
558 {
|
||||
...
|
||||
563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||
564 & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||
565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||
566 }
|
||||
|
||||
We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero
|
||||
and don't wake anyone. This is OK because we handed
|
||||
over to the trylock. It will be the trylock's responsibility
|
||||
to wake any waiters.
|
||||
|
||||
Back in the read lock:
|
||||
|
||||
The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because
|
||||
the __readers value was adjusted by the trylock, and so it falls through
|
||||
to waiting on the lock for explicit handover from either a new writer
|
||||
or a new reader.
|
||||
|
||||
448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||
450 abstime, private);
|
||||
|
||||
We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex
|
||||
is in use.
|
||||
|
||||
At this point we have readers waiting on the read lock
|
||||
to unlock. The wrlock is done. The trylock is finishing
|
||||
the installation of the read phase.
|
||||
|
||||
92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||
93 {
|
||||
...
|
||||
105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||
106 }
|
||||
|
||||
The trylock does note that we were the one that
|
||||
installed the read phase, but the comments are not
|
||||
correct, the execution ordering above shows that
|
||||
readers might indeed be waiting, and they are.
|
||||
|
||||
The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED,
|
||||
and the waiting reader is never worken becuase as noted
|
||||
above it is conditional on the futex being used.
|
||||
|
||||
The solution is for the trylock thread to inspect
|
||||
PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers.
|
||||
|
||||
--- Analysis of pthread_rwlock_trywrlock() stall ---
|
||||
|
||||
A write lock begins to execute, takes the write lock,
|
||||
and then releases the lock...
|
||||
|
||||
In pthread_rwlock_wrunlock():
|
||||
|
||||
547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||
...
|
||||
549 while (!atomic_compare_exchange_weak_release
|
||||
550 (&rwlock->__data.__readers, &r,
|
||||
551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||
552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||
553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||
554 {
|
||||
...
|
||||
556 }
|
||||
|
||||
... leaving it in the write phase with zero readers
|
||||
(the case where we leave the write phase in place
|
||||
during a write unlock).
|
||||
|
||||
A write trylock begins to execute.
|
||||
|
||||
In __pthread_rwlock_trywrlock:
|
||||
|
||||
40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||
41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0)
|
||||
42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0))))
|
||||
43 {
|
||||
|
||||
The lock is not locked.
|
||||
|
||||
There are no readers.
|
||||
|
||||
45 if (atomic_compare_exchange_weak_acquire (
|
||||
46 &rwlock->__data.__readers, &r,
|
||||
47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||
|
||||
We atomically install the write phase and we take the
|
||||
exclusive write lock.
|
||||
|
||||
48 {
|
||||
49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||
|
||||
We get this far.
|
||||
|
||||
A reader lock begins to execute.
|
||||
|
||||
In pthread_rwlock_rdlock:
|
||||
|
||||
437 for (;;)
|
||||
438 {
|
||||
439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
441 {
|
||||
442 int private = __pthread_rwlock_get_private (rwlock);
|
||||
443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||
444 && (!atomic_compare_exchange_weak_relaxed
|
||||
445 (&rwlock->__data.__wrphase_futex,
|
||||
446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)))
|
||||
447 continue;
|
||||
448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||
449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||
450 abstime, private);
|
||||
|
||||
We are in a write phase, so the while() on line 439 is true.
|
||||
|
||||
The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set
|
||||
since this is the first reader to lock.
|
||||
|
||||
The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED
|
||||
on the expectation that this reader will be woken during
|
||||
the handoff.
|
||||
|
||||
Back in pthread_rwlock_trywrlock:
|
||||
|
||||
50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||
51 atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||
52 THREAD_GETMEM (THREAD_SELF, tid));
|
||||
53 return 0;
|
||||
54 }
|
||||
...
|
||||
57 }
|
||||
|
||||
We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED,
|
||||
and so in the unlock we will not awaken the waiting reader.
|
||||
|
||||
The solution to this is to realize that if we did not start the write
|
||||
phase we need not write 1 or any other value to __wrphase_futex.
|
||||
This ensures that any readers (which saw __wrphase_futex != 0) can
|
||||
set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to
|
||||
wake them.
|
||||
|
||||
If we installed the write phase then all other readers are looping
|
||||
here:
|
||||
|
||||
In __pthread_rwlock_rdlock_full:
|
||||
|
||||
437 for (;;)
|
||||
438 {
|
||||
439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||
440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||
441 {
|
||||
...
|
||||
508 }
|
||||
|
||||
waiting for the write phase to be installed or removed before they
|
||||
can begin waiting on __wrphase_futex (part of the algorithm), or
|
||||
taking a concurrent read lock, and thus we can safely write 1 to
|
||||
__wrphase_futex.
|
||||
|
||||
If we did not install the write phase then the readers may already
|
||||
be waiting on the futex, the original writer wrote 1 to __wrphase_futex
|
||||
as part of starting the write phase, and we cannot also write 1
|
||||
without loosing the PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||
|
||||
---
|
||||
|
||||
Summary for the pthread_rwlock_tryrdlock() stall:
|
||||
|
||||
The stall is caused by pthread_rwlock_tryrdlock failing to check
|
||||
that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex
|
||||
and then waking the futex.
|
||||
|
||||
The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||
correctly woken. Before the fix the test stalls as readers can
|
||||
wait forever on __wrphase_futex. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <support/xthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* We need only one lock to reproduce the issue. We will need multiple
|
||||
threads to get the exact case where we have a read, try, and unlock
|
||||
all interleaving to produce the case where the readers are waiting
|
||||
and the try fails to wake them. */
|
||||
pthread_rwlock_t onelock;
|
||||
|
||||
/* The number of threads is arbitrary but empirically chosen to have
|
||||
enough threads that we see the condition where waiting readers are
|
||||
not woken by a successful tryrdlock. */
|
||||
#define NTHREADS 32
|
||||
|
||||
_Atomic int do_exit;
|
||||
|
||||
void *
|
||||
run_loop (void *arg)
|
||||
{
|
||||
int i = 0, ret;
|
||||
while (!do_exit)
|
||||
{
|
||||
/* Arbitrarily choose if we are the writer or reader. Choose a
|
||||
high enough ratio of readers to writers to make it likely
|
||||
that readers block (and eventually are susceptable to
|
||||
stalling).
|
||||
|
||||
If we are a writer, take the write lock, and then unlock.
|
||||
If we are a reader, try the lock, then lock, then unlock. */
|
||||
if ((i % 8) != 0)
|
||||
xpthread_rwlock_wrlock (&onelock);
|
||||
else
|
||||
{
|
||||
if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0)
|
||||
{
|
||||
if (ret == EBUSY)
|
||||
xpthread_rwlock_rdlock (&onelock);
|
||||
else
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
/* Thread does some work and then unlocks. */
|
||||
xpthread_rwlock_unlock (&onelock);
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
int i;
|
||||
pthread_t tids[NTHREADS];
|
||||
xpthread_rwlock_init (&onelock, NULL);
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||
/* Run for some amount of time. Empirically speaking exercising
|
||||
the stall via pthread_rwlock_tryrdlock is much harder, and on
|
||||
a 3.5GHz 4 core x86_64 VM system it takes somewhere around
|
||||
20-200s to stall, approaching 100% stall past 200s. We can't
|
||||
wait that long for a regression test so we just test for 20s,
|
||||
and expect the stall to happen with a 5-10% chance (enough for
|
||||
developers to see). */
|
||||
sleep (20);
|
||||
/* Then exit. */
|
||||
printf ("INFO: Exiting...\n");
|
||||
do_exit = 1;
|
||||
/* If any readers stalled then we will timeout waiting for them. */
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
xpthread_join (tids[i]);
|
||||
printf ("INFO: Done.\n");
|
||||
xpthread_rwlock_destroy (&onelock);
|
||||
printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT 30
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
|
||||
|
||||
Summary for the pthread_rwlock_trywrlock() stall:
|
||||
|
||||
The stall is caused by pthread_rwlock_trywrlock setting
|
||||
__wrphase_futex futex to 1 and loosing the
|
||||
PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||
|
||||
The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||
correctly woken. Before the fix the test stalls as readers can
|
||||
wait forever on __wrphase_futex. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <support/xthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* We need only one lock to reproduce the issue. We will need multiple
|
||||
threads to get the exact case where we have a read, try, and unlock
|
||||
all interleaving to produce the case where the readers are waiting
|
||||
and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
|
||||
subsequent unlock fails to wake them. */
|
||||
pthread_rwlock_t onelock;
|
||||
|
||||
/* The number of threads is arbitrary but empirically chosen to have
|
||||
enough threads that we see the condition where waiting readers are
|
||||
not woken by a successful unlock. */
|
||||
#define NTHREADS 32
|
||||
|
||||
_Atomic int do_exit;
|
||||
|
||||
void *
|
||||
run_loop (void *arg)
|
||||
{
|
||||
int i = 0, ret;
|
||||
while (!do_exit)
|
||||
{
|
||||
/* Arbitrarily choose if we are the writer or reader. Choose a
|
||||
high enough ratio of readers to writers to make it likely
|
||||
that readers block (and eventually are susceptable to
|
||||
stalling).
|
||||
|
||||
If we are a writer, take the write lock, and then unlock.
|
||||
If we are a reader, try the lock, then lock, then unlock. */
|
||||
if ((i % 8) != 0)
|
||||
{
|
||||
if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
|
||||
{
|
||||
if (ret == EBUSY)
|
||||
xpthread_rwlock_wrlock (&onelock);
|
||||
else
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
xpthread_rwlock_rdlock (&onelock);
|
||||
/* Thread does some work and then unlocks. */
|
||||
xpthread_rwlock_unlock (&onelock);
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
int i;
|
||||
pthread_t tids[NTHREADS];
|
||||
xpthread_rwlock_init (&onelock, NULL);
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||
/* Run for some amount of time. The pthread_rwlock_tryrwlock stall
|
||||
is very easy to trigger and happens in seconds under the test
|
||||
conditions. */
|
||||
sleep (10);
|
||||
/* Then exit. */
|
||||
printf ("INFO: Exiting...\n");
|
||||
do_exit = 1;
|
||||
/* If any readers stalled then we will timeout waiting for them. */
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
xpthread_join (tids[i]);
|
||||
printf ("INFO: Done.\n");
|
||||
xpthread_rwlock_destroy (&onelock);
|
||||
printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+49
-41
@@ -19,12 +19,16 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <support/support.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
struct test_s
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
__attribute__ ((aligned(0x20))) int a;
|
||||
__attribute__ ((aligned(0x200))) int b;
|
||||
};
|
||||
|
||||
#define INIT_A 1
|
||||
@@ -36,15 +40,34 @@ __thread struct test_s s __attribute__ ((tls_model ("initial-exec"))) =
|
||||
.b = INIT_B
|
||||
};
|
||||
|
||||
/* Use noinline in combination with not static to ensure that the
|
||||
alignment check is really done. Otherwise it was optimized out! */
|
||||
__attribute__ ((noinline)) void
|
||||
check_alignment (const char *thr_name, const char *ptr_name,
|
||||
int *ptr, int alignment)
|
||||
{
|
||||
uintptr_t offset_aligment = ((uintptr_t) ptr) & (alignment - 1);
|
||||
if (offset_aligment)
|
||||
{
|
||||
FAIL_EXIT1 ("%s (%p) is not 0x%x-byte aligned in %s thread\n",
|
||||
ptr_name, ptr, alignment, thr_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_s (const char *thr_name)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
FAIL_EXIT1 ("initial value of s in %s thread wrong\n", thr_name);
|
||||
|
||||
check_alignment (thr_name, "s.a", &s.a, 0x20);
|
||||
check_alignment (thr_name, "s.b", &s.b, 0x200);
|
||||
}
|
||||
|
||||
static void *
|
||||
tf (void *arg)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
{
|
||||
puts ("initial value of s in child thread wrong");
|
||||
exit (1);
|
||||
}
|
||||
check_s ("child");
|
||||
|
||||
++s.a;
|
||||
|
||||
@@ -55,25 +78,14 @@ tf (void *arg)
|
||||
int
|
||||
do_test (void)
|
||||
{
|
||||
if (s.a != INIT_A || s.b != INIT_B)
|
||||
{
|
||||
puts ("initial value of s in main thread wrong");
|
||||
exit (1);
|
||||
}
|
||||
check_s ("main");
|
||||
|
||||
pthread_attr_t a;
|
||||
|
||||
if (pthread_attr_init (&a) != 0)
|
||||
{
|
||||
puts ("attr_init failed");
|
||||
exit (1);
|
||||
}
|
||||
xpthread_attr_init (&a);
|
||||
|
||||
if (pthread_attr_setstacksize (&a, 1 * 1024 * 1024) != 0)
|
||||
{
|
||||
puts ("attr_setstacksize failed");
|
||||
return 1;
|
||||
}
|
||||
#define STACK_SIZE (1 * 1024 * 1024)
|
||||
xpthread_attr_setstacksize (&a, STACK_SIZE);
|
||||
|
||||
#define N 10
|
||||
int i;
|
||||
@@ -83,29 +95,25 @@ do_test (void)
|
||||
pthread_t th[M];
|
||||
int j;
|
||||
for (j = 0; j < M; ++j, ++s.a)
|
||||
if (pthread_create (&th[j], &a, tf, NULL) != 0)
|
||||
{
|
||||
puts ("pthread_create failed");
|
||||
exit (1);
|
||||
}
|
||||
th[j] = xpthread_create (&a, tf, NULL);
|
||||
|
||||
for (j = 0; j < M; ++j)
|
||||
if (pthread_join (th[j], NULL) != 0)
|
||||
{
|
||||
puts ("pthread_join failed");
|
||||
exit (1);
|
||||
}
|
||||
xpthread_join (th[j]);
|
||||
}
|
||||
|
||||
if (pthread_attr_destroy (&a) != 0)
|
||||
{
|
||||
puts ("attr_destroy failed");
|
||||
exit (1);
|
||||
}
|
||||
/* Also check the alignment of the tls variables if a misaligned stack is
|
||||
specified. */
|
||||
pthread_t th;
|
||||
void *thr_stack = NULL;
|
||||
thr_stack = xposix_memalign (0x200, STACK_SIZE + 1);
|
||||
xpthread_attr_setstack (&a, thr_stack + 1, STACK_SIZE);
|
||||
th = xpthread_create (&a, tf, NULL);
|
||||
xpthread_join (th);
|
||||
free (thr_stack);
|
||||
|
||||
xpthread_attr_destroy (&a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
||||
#include <support/test-driver.c>
|
||||
|
||||
@@ -248,7 +248,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
: NULL);
|
||||
ndomain = (ndomain ? newbuf + ndomaindiff
|
||||
: NULL);
|
||||
buffer = newbuf;
|
||||
*tofreep = buffer = newbuf;
|
||||
}
|
||||
|
||||
nhost = memcpy (buffer + bufused,
|
||||
@@ -319,7 +319,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
|
||||
{
|
||||
buflen *= 2;
|
||||
buffer = xrealloc (buffer, buflen);
|
||||
*tofreep = buffer = xrealloc (buffer, buflen);
|
||||
}
|
||||
else if (status == NSS_STATUS_RETURN
|
||||
|| status == NSS_STATUS_NOTFOUND
|
||||
|
||||
+3
-1
@@ -60,7 +60,9 @@ tests = test-netdb test-digits-dots tst-nss-getpwent bug17079 \
|
||||
xtests = bug-erange
|
||||
|
||||
tests-container = \
|
||||
tst-nss-test3
|
||||
tst-nss-test3 \
|
||||
tst-nss-db-endpwent \
|
||||
tst-nss-db-endgrent
|
||||
|
||||
# Tests which need libdl
|
||||
ifeq (yes,$(build-shared))
|
||||
|
||||
@@ -142,7 +142,7 @@ _nss_compat_setgrent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endgrent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -163,6 +163,15 @@ internal_endgrent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endgrent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endgrent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endgrent (void)
|
||||
{
|
||||
@@ -483,7 +492,7 @@ _nss_compat_getgrnam_r (const char *name, struct group *grp,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endgrent (&ent);
|
||||
internal_endgrent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -612,7 +621,7 @@ _nss_compat_getgrgid_r (gid_t gid, struct group *grp,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endgrent (&ent);
|
||||
internal_endgrent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ internal_setgrent (ent_t *ent)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endgrent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -157,6 +157,15 @@ internal_endgrent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endgrent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endgrent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
/* Add new group record. */
|
||||
static void
|
||||
add_group (long int *start, long int *size, gid_t **groupsp, long int limit,
|
||||
@@ -501,7 +510,7 @@ _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
|
||||
done:
|
||||
scratch_buffer_free (&tmpbuf);
|
||||
|
||||
internal_endgrent (&intern);
|
||||
internal_endgrent_noerror (&intern);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ _nss_compat_setpwent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endpwent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -287,6 +287,15 @@ internal_endpwent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endpwent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endpwent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endpwent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endpwent (void)
|
||||
{
|
||||
@@ -822,7 +831,7 @@ _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endpwent (&ent);
|
||||
internal_endpwent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1061,7 +1070,7 @@ _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endpwent (&ent);
|
||||
internal_endpwent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ _nss_compat_setspent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endspent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -244,6 +244,15 @@ internal_endspent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endspent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endspent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endspent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endspent (void)
|
||||
{
|
||||
@@ -261,7 +270,6 @@ _nss_compat_endspent (void)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent,
|
||||
char *group, char *buffer, size_t buflen,
|
||||
@@ -786,7 +794,7 @@ _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endspent (&ent);
|
||||
internal_endspent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -63,5 +63,9 @@ internal_setent (const char *file, struct nss_db_map *mapping)
|
||||
void
|
||||
internal_endent (struct nss_db_map *mapping)
|
||||
{
|
||||
munmap (mapping->header, mapping->len);
|
||||
if (mapping->header != NULL)
|
||||
{
|
||||
munmap (mapping->header, mapping->len);
|
||||
mapping->header = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Test for endgrent changing errno for BZ #24696
|
||||
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 <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* The following test verifies that if the db NSS Service is initialized
|
||||
with no database (getgrent), that a subsequent closure (endgrent) does
|
||||
not set errno. In the case of the db service it is not an error to close
|
||||
the service and so it should not set errno. */
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Just make sure it's not there, although usually it won't be. */
|
||||
unlink ("/var/db/group.db");
|
||||
|
||||
/* This, in conjunction with the testroot's nsswitch.conf, causes
|
||||
the nss_db module to be "connected" and initialized - but the
|
||||
testroot has no group.db, so no mapping will be created. */
|
||||
getgrent ();
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* Before the fix, this would call munmap (NULL) and set errno. */
|
||||
endgrent ();
|
||||
|
||||
if (errno != 0)
|
||||
FAIL_EXIT1 ("endgrent set errno to %d\n", errno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1 @@
|
||||
group : db files
|
||||
@@ -0,0 +1,66 @@
|
||||
/* Test for endpwent->getpwent crash for BZ #24695
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include <support/support.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* It is entirely allowed to start with a getpwent call without
|
||||
resetting the state of the service via a call to setpwent.
|
||||
You can also call getpwent more times than you have entries in
|
||||
the service, and it should not fail. This test iteratates the
|
||||
database once, gets to the end, and then attempts a second
|
||||
iteration to look for crashes. */
|
||||
|
||||
static void
|
||||
try_it (void)
|
||||
{
|
||||
struct passwd *pw;
|
||||
|
||||
/* setpwent is intentionally omitted here. The first call to
|
||||
getpwent detects that it's first and initializes. The second
|
||||
time try_it is called, this "first call" was not detected before
|
||||
the fix, and getpwent would crash. */
|
||||
|
||||
while ((pw = getpwent ()) != NULL)
|
||||
;
|
||||
|
||||
/* We only care if this segfaults or not. */
|
||||
endpwent ();
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
cmd = xasprintf ("%s/makedb -o /var/db/passwd.db /var/db/passwd.in",
|
||||
support_bindir_prefix);
|
||||
system (cmd);
|
||||
free (cmd);
|
||||
|
||||
try_it ();
|
||||
try_it ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1 @@
|
||||
passwd: db
|
||||
@@ -0,0 +1,4 @@
|
||||
.root root:x:0:0:root:/root:/bin/bash
|
||||
=0 root:x:0:0:root:/root:/bin/bash
|
||||
.bin bin:x:1:1:bin:/bin:/sbin/nologin
|
||||
=1 bin:x:1:1:bin:/bin:/sbin/nologin
|
||||
+5
-5
@@ -87,7 +87,7 @@ __extension__ typedef unsigned long long int __uintmax_t;
|
||||
32 -- "natural" 32-bit type (always int)
|
||||
64 -- "natural" 64-bit type (long or long long)
|
||||
LONG32 -- 32-bit type, traditionally long
|
||||
QUAD -- 64-bit type, always long long
|
||||
QUAD -- 64-bit type, traditionally long long
|
||||
WORD -- natural type of __WORDSIZE bits (int or long)
|
||||
LONGWORD -- type of __WORDSIZE bits, traditionally long
|
||||
|
||||
@@ -113,14 +113,14 @@ __extension__ typedef unsigned long long int __uintmax_t;
|
||||
#define __SLONGWORD_TYPE long int
|
||||
#define __ULONGWORD_TYPE unsigned long int
|
||||
#if __WORDSIZE == 32
|
||||
# define __SQUAD_TYPE __quad_t
|
||||
# define __UQUAD_TYPE __u_quad_t
|
||||
# define __SQUAD_TYPE __int64_t
|
||||
# define __UQUAD_TYPE __uint64_t
|
||||
# define __SWORD_TYPE int
|
||||
# define __UWORD_TYPE unsigned int
|
||||
# define __SLONG32_TYPE long int
|
||||
# define __ULONG32_TYPE unsigned long int
|
||||
# define __S64_TYPE __quad_t
|
||||
# define __U64_TYPE __u_quad_t
|
||||
# define __S64_TYPE __int64_t
|
||||
# define __U64_TYPE __uint64_t
|
||||
/* We want __extension__ before typedef's that use nonstandard base types
|
||||
such as `long long' in C89 mode. */
|
||||
# define __STD_TYPE __extension__ typedef
|
||||
|
||||
+13
-12
@@ -827,31 +827,32 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
|
||||
{
|
||||
size_t home_len = strlen (p->pw_dir);
|
||||
size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
|
||||
char *d;
|
||||
char *d, *newp;
|
||||
bool use_alloca = glob_use_alloca (alloca_used,
|
||||
home_len + rest_len + 1);
|
||||
|
||||
if (__glibc_unlikely (malloc_dirname))
|
||||
free (dirname);
|
||||
malloc_dirname = 0;
|
||||
|
||||
if (glob_use_alloca (alloca_used, home_len + rest_len + 1))
|
||||
dirname = alloca_account (home_len + rest_len + 1,
|
||||
alloca_used);
|
||||
if (use_alloca)
|
||||
newp = alloca_account (home_len + rest_len + 1, alloca_used);
|
||||
else
|
||||
{
|
||||
dirname = malloc (home_len + rest_len + 1);
|
||||
if (dirname == NULL)
|
||||
newp = malloc (home_len + rest_len + 1);
|
||||
if (newp == NULL)
|
||||
{
|
||||
scratch_buffer_free (&pwtmpbuf);
|
||||
retval = GLOB_NOSPACE;
|
||||
goto out;
|
||||
}
|
||||
malloc_dirname = 1;
|
||||
}
|
||||
d = mempcpy (dirname, p->pw_dir, home_len);
|
||||
d = mempcpy (newp, p->pw_dir, home_len);
|
||||
if (end_name != NULL)
|
||||
d = mempcpy (d, end_name, rest_len);
|
||||
*d = '\0';
|
||||
|
||||
if (__glibc_unlikely (malloc_dirname))
|
||||
free (dirname);
|
||||
dirname = newp;
|
||||
malloc_dirname = !use_alloca;
|
||||
|
||||
dirlen = home_len + rest_len;
|
||||
dirname_modified = 1;
|
||||
}
|
||||
|
||||
+4
-2
@@ -1293,8 +1293,10 @@ proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs,
|
||||
else if (naccepted)
|
||||
{
|
||||
char *buf = (char *) re_string_get_buffer (&mctx->input);
|
||||
if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
|
||||
naccepted) != 0)
|
||||
if (mctx->input.valid_len - *pidx < naccepted
|
||||
|| (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
|
||||
naccepted)
|
||||
!= 0))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-25
@@ -154,37 +154,20 @@ typedef unsigned int uint;
|
||||
|
||||
#include <bits/stdint-intn.h>
|
||||
|
||||
#if !__GNUC_PREREQ (2, 7)
|
||||
|
||||
/* These were defined by ISO C without the first `_'. */
|
||||
typedef unsigned char u_int8_t;
|
||||
typedef unsigned short int u_int16_t;
|
||||
typedef unsigned int u_int32_t;
|
||||
# if __WORDSIZE == 64
|
||||
typedef unsigned long int u_int64_t;
|
||||
# else
|
||||
__extension__ typedef unsigned long long int u_int64_t;
|
||||
# endif
|
||||
|
||||
typedef int register_t;
|
||||
|
||||
#else
|
||||
|
||||
/* For GCC 2.7 and later, we can use specific type-size attributes. */
|
||||
# define __u_intN_t(N, MODE) \
|
||||
typedef unsigned int u_int##N##_t __attribute__ ((__mode__ (MODE)))
|
||||
|
||||
__u_intN_t (8, __QI__);
|
||||
__u_intN_t (16, __HI__);
|
||||
__u_intN_t (32, __SI__);
|
||||
__u_intN_t (64, __DI__);
|
||||
typedef __uint8_t u_int8_t;
|
||||
typedef __uint16_t u_int16_t;
|
||||
typedef __uint32_t u_int32_t;
|
||||
typedef __uint64_t u_int64_t;
|
||||
|
||||
#if __GNUC_PREREQ (2, 7)
|
||||
typedef int register_t __attribute__ ((__mode__ (__word__)));
|
||||
|
||||
#else
|
||||
typedef int register_t;
|
||||
#endif
|
||||
|
||||
/* Some code from BIND tests this macro to see if the types above are
|
||||
defined. */
|
||||
#endif
|
||||
#define __BIT_TYPES_DEFINED__ 1
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* BZ #18877 and #21270 mmap offset test.
|
||||
/* BZ #18877, BZ #21270, and BZ #24699 mmap offset test.
|
||||
|
||||
Copyright (C) 2015-2019 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <mmap_info.h>
|
||||
|
||||
#include <support/check.h>
|
||||
|
||||
@@ -76,7 +77,7 @@ do_test_bz18877 (void)
|
||||
|
||||
/* Check if invalid offset are handled correctly by mmap. */
|
||||
static int
|
||||
do_test_bz21270 (void)
|
||||
do_test_large_offset (void)
|
||||
{
|
||||
/* For architectures with sizeof (off_t) < sizeof (off64_t) mmap is
|
||||
implemented with __SYS_mmap2 syscall and the offset is represented in
|
||||
@@ -90,7 +91,7 @@ do_test_bz21270 (void)
|
||||
const size_t length = 4096;
|
||||
|
||||
void *addr = mmap64 (NULL, length, prot, flags, fd, offset);
|
||||
if (sizeof (off_t) < sizeof (off64_t))
|
||||
if (mmap64_maximum_offset (page_shift) < UINT64_MAX)
|
||||
{
|
||||
if ((addr != MAP_FAILED) && (errno != EINVAL))
|
||||
FAIL_RET ("mmap succeed");
|
||||
@@ -110,7 +111,7 @@ do_test (void)
|
||||
int ret = 0;
|
||||
|
||||
ret += do_test_bz18877 ();
|
||||
ret += do_test_bz21270 ();
|
||||
ret += do_test_large_offset ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Check installed headers for cleanliness. For each header, confirm
|
||||
# that it's possible to compile a file that includes that header and
|
||||
# does nothing else, in several different compilation modes. Also,
|
||||
# scan the header for a set of obsolete typedefs that should no longer
|
||||
# appear.
|
||||
# For each installed header, confirm that it's possible to compile a
|
||||
# file that includes that header and does nothing else, in several
|
||||
# different compilation modes.
|
||||
|
||||
# These compilation switches assume GCC or compatible, which is probably
|
||||
# fine since we also assume that when _building_ glibc.
|
||||
@@ -31,13 +29,6 @@ cxx_modes="-std=c++98 -std=gnu++98 -std=c++11 -std=gnu++11"
|
||||
# These are probably the most commonly used three.
|
||||
lib_modes="-D_DEFAULT_SOURCE=1 -D_GNU_SOURCE=1 -D_XOPEN_SOURCE=700"
|
||||
|
||||
# sys/types.h+bits/types.h have to define the obsolete types.
|
||||
# rpc(svc)/* have the obsolete types too deeply embedded in their API
|
||||
# to remove.
|
||||
skip_obsolete_type_check='*/sys/types.h|*/bits/types.h|*/rpc/*|*/rpcsvc/*'
|
||||
obsolete_type_re=\
|
||||
'\<((__)?(quad_t|u(short|int|long|_(char|short|int([0-9]+_t)?|long|quad_t))))\>'
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
|
||||
exit 2
|
||||
@@ -46,14 +37,10 @@ case "$1" in
|
||||
(c)
|
||||
lang_modes="$c_modes"
|
||||
cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.c)
|
||||
already="$skip_obsolete_type_check"
|
||||
;;
|
||||
(c++)
|
||||
lang_modes="$cxx_modes"
|
||||
cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.cc)
|
||||
# The obsolete-type check can be skipped for C++; it is
|
||||
# sufficient to do it for C.
|
||||
already="*"
|
||||
;;
|
||||
(*)
|
||||
echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
|
||||
@@ -151,22 +138,8 @@ $expanded_lib_mode
|
||||
int avoid_empty_translation_unit;
|
||||
EOF
|
||||
if $cc_cmd -fsyntax-only $lang_mode "$cih_test_c" 2>&1
|
||||
then
|
||||
includes=$($cc_cmd -fsyntax-only -H $lang_mode \
|
||||
"$cih_test_c" 2>&1 | sed -ne 's/^[.][.]* //p')
|
||||
for h in $includes; do
|
||||
# Don't repeat work.
|
||||
eval 'case "$h" in ('"$already"') continue;; esac'
|
||||
|
||||
if grep -qE "$obsolete_type_re" "$h"; then
|
||||
echo "*** Obsolete types detected:"
|
||||
grep -HE "$obsolete_type_re" "$h"
|
||||
failed=1
|
||||
fi
|
||||
already="$already|$h"
|
||||
done
|
||||
else
|
||||
failed=1
|
||||
then :
|
||||
else failed=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
Executable
+466
@@ -0,0 +1,466 @@
|
||||
#! /usr/bin/python3
|
||||
# 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/>.
|
||||
|
||||
"""Verifies that installed headers do not use any obsolete constructs:
|
||||
* legacy BSD typedefs superseded by <stdint.h>:
|
||||
ushort uint ulong u_char u_short u_int u_long u_intNN_t quad_t u_quad_t
|
||||
(sys/types.h is allowed to _define_ these types, but not to use them
|
||||
to define anything else).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Simplified lexical analyzer for C preprocessing tokens.
|
||||
# Does not implement trigraphs.
|
||||
# Does not implement backslash-newline in the middle of any lexical
|
||||
# item other than a string literal.
|
||||
# Does not implement universal-character-names in identifiers.
|
||||
# Treats prefixed strings (e.g. L"...") as two tokens (L and "...")
|
||||
# Accepts non-ASCII characters only within comments and strings.
|
||||
|
||||
# Caution: The order of the outermost alternation matters.
|
||||
# STRING must be before BAD_STRING, CHARCONST before BAD_CHARCONST,
|
||||
# BLOCK_COMMENT before BAD_BLOCK_COM before PUNCTUATOR, and OTHER must
|
||||
# be last.
|
||||
# Caution: There should be no capturing groups other than the named
|
||||
# captures in the outermost alternation.
|
||||
|
||||
# For reference, these are all of the C punctuators as of C11:
|
||||
# [ ] ( ) { } , ; ? ~
|
||||
# ! != * *= / /= ^ ^= = ==
|
||||
# # ##
|
||||
# % %= %> %: %:%:
|
||||
# & &= &&
|
||||
# | |= ||
|
||||
# + += ++
|
||||
# - -= -- ->
|
||||
# . ...
|
||||
# : :>
|
||||
# < <% <: << <<= <=
|
||||
# > >= >> >>=
|
||||
|
||||
# The BAD_* tokens are not part of the official definition of pp-tokens;
|
||||
# they match unclosed strings, character constants, and block comments,
|
||||
# so that the regex engine doesn't have to backtrack all the way to the
|
||||
# beginning of a broken construct and then emit dozens of junk tokens.
|
||||
|
||||
PP_TOKEN_RE_ = re.compile(r"""
|
||||
(?P<STRING> \"(?:[^\"\\\r\n]|\\(?:[\r\n -~]|\r\n))*\")
|
||||
|(?P<BAD_STRING> \"(?:[^\"\\\r\n]|\\[ -~])*)
|
||||
|(?P<CHARCONST> \'(?:[^\'\\\r\n]|\\(?:[\r\n -~]|\r\n))*\')
|
||||
|(?P<BAD_CHARCONST> \'(?:[^\'\\\r\n]|\\[ -~])*)
|
||||
|(?P<BLOCK_COMMENT> /\*(?:\*(?!/)|[^*])*\*/)
|
||||
|(?P<BAD_BLOCK_COM> /\*(?:\*(?!/)|[^*])*\*?)
|
||||
|(?P<LINE_COMMENT> //[^\r\n]*)
|
||||
|(?P<IDENT> [_a-zA-Z][_a-zA-Z0-9]*)
|
||||
|(?P<PP_NUMBER> \.?[0-9](?:[0-9a-df-oq-zA-DF-OQ-Z_.]|[eEpP][+-]?)*)
|
||||
|(?P<PUNCTUATOR>
|
||||
[,;?~(){}\[\]]
|
||||
| [!*/^=]=?
|
||||
| \#\#?
|
||||
| %(?:[=>]|:(?:%:)?)?
|
||||
| &[=&]?
|
||||
|\|[=|]?
|
||||
|\+[=+]?
|
||||
| -[=->]?
|
||||
|\.(?:\.\.)?
|
||||
| :>?
|
||||
| <(?:[%:]|<(?:=|<=?)?)?
|
||||
| >(?:=|>=?)?)
|
||||
|(?P<ESCNL> \\(?:\r|\n|\r\n))
|
||||
|(?P<WHITESPACE> [ \t\n\r\v\f]+)
|
||||
|(?P<OTHER> .)
|
||||
""", re.DOTALL | re.VERBOSE)
|
||||
|
||||
HEADER_NAME_RE_ = re.compile(r"""
|
||||
< [^>\r\n]+ >
|
||||
| " [^"\r\n]+ "
|
||||
""", re.DOTALL | re.VERBOSE)
|
||||
|
||||
ENDLINE_RE_ = re.compile(r"""\r|\n|\r\n""")
|
||||
|
||||
# based on the sample code in the Python re documentation
|
||||
Token_ = collections.namedtuple("Token", (
|
||||
"kind", "text", "line", "column", "context"))
|
||||
Token_.__doc__ = """
|
||||
One C preprocessing token, comment, or chunk of whitespace.
|
||||
'kind' identifies the token type, which will be one of:
|
||||
STRING, CHARCONST, BLOCK_COMMENT, LINE_COMMENT, IDENT,
|
||||
PP_NUMBER, PUNCTUATOR, ESCNL, WHITESPACE, HEADER_NAME,
|
||||
or OTHER. The BAD_* alternatives in PP_TOKEN_RE_ are
|
||||
handled within tokenize_c, below.
|
||||
|
||||
'text' is the sequence of source characters making up the token;
|
||||
no decoding whatsoever is performed.
|
||||
|
||||
'line' and 'column' give the position of the first character of the
|
||||
token within the source file. They are both 1-based.
|
||||
|
||||
'context' indicates whether or not this token occurred within a
|
||||
preprocessing directive; it will be None for running text,
|
||||
'<null>' for the leading '#' of a directive line (because '#'
|
||||
all by itself on a line is a "null directive"), or the name of
|
||||
the directive for tokens within a directive line, starting with
|
||||
the IDENT for the name itself.
|
||||
"""
|
||||
|
||||
def tokenize_c(file_contents, reporter):
|
||||
"""Yield a series of Token objects, one for each preprocessing
|
||||
token, comment, or chunk of whitespace within FILE_CONTENTS.
|
||||
The REPORTER object is expected to have one method,
|
||||
reporter.error(token, message), which will be called to
|
||||
indicate a lexical error at the position of TOKEN.
|
||||
If MESSAGE contains the four-character sequence '{!r}', that
|
||||
is expected to be replaced by repr(token.text).
|
||||
"""
|
||||
|
||||
Token = Token_
|
||||
PP_TOKEN_RE = PP_TOKEN_RE_
|
||||
ENDLINE_RE = ENDLINE_RE_
|
||||
HEADER_NAME_RE = HEADER_NAME_RE_
|
||||
|
||||
line_num = 1
|
||||
line_start = 0
|
||||
pos = 0
|
||||
limit = len(file_contents)
|
||||
directive = None
|
||||
at_bol = True
|
||||
while pos < limit:
|
||||
if directive == "include":
|
||||
mo = HEADER_NAME_RE.match(file_contents, pos)
|
||||
if mo:
|
||||
kind = "HEADER_NAME"
|
||||
directive = "after_include"
|
||||
else:
|
||||
mo = PP_TOKEN_RE.match(file_contents, pos)
|
||||
kind = mo.lastgroup
|
||||
if kind != "WHITESPACE":
|
||||
directive = "after_include"
|
||||
else:
|
||||
mo = PP_TOKEN_RE.match(file_contents, pos)
|
||||
kind = mo.lastgroup
|
||||
|
||||
text = mo.group()
|
||||
line = line_num
|
||||
column = mo.start() - line_start
|
||||
adj_line_start = 0
|
||||
# only these kinds can contain a newline
|
||||
if kind in ("WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT",
|
||||
"STRING", "CHARCONST", "BAD_BLOCK_COM", "ESCNL"):
|
||||
for tmo in ENDLINE_RE.finditer(text):
|
||||
line_num += 1
|
||||
adj_line_start = tmo.end()
|
||||
if adj_line_start:
|
||||
line_start = mo.start() + adj_line_start
|
||||
|
||||
# Track whether or not we are scanning a preprocessing directive.
|
||||
if kind == "LINE_COMMENT" or (kind == "WHITESPACE" and adj_line_start):
|
||||
at_bol = True
|
||||
directive = None
|
||||
else:
|
||||
if kind == "PUNCTUATOR" and text == "#" and at_bol:
|
||||
directive = "<null>"
|
||||
elif kind == "IDENT" and directive == "<null>":
|
||||
directive = text
|
||||
at_bol = False
|
||||
|
||||
# Report ill-formed tokens and rewrite them as their well-formed
|
||||
# equivalents, so downstream processing doesn't have to know about them.
|
||||
# (Rewriting instead of discarding provides better error recovery.)
|
||||
if kind == "BAD_BLOCK_COM":
|
||||
reporter.error(Token("BAD_BLOCK_COM", "", line, column+1, ""),
|
||||
"unclosed block comment")
|
||||
text += "*/"
|
||||
kind = "BLOCK_COMMENT"
|
||||
elif kind == "BAD_STRING":
|
||||
reporter.error(Token("BAD_STRING", "", line, column+1, ""),
|
||||
"unclosed string")
|
||||
text += "\""
|
||||
kind = "STRING"
|
||||
elif kind == "BAD_CHARCONST":
|
||||
reporter.error(Token("BAD_CHARCONST", "", line, column+1, ""),
|
||||
"unclosed char constant")
|
||||
text += "'"
|
||||
kind = "CHARCONST"
|
||||
|
||||
tok = Token(kind, text, line, column+1,
|
||||
"include" if directive == "after_include" else directive)
|
||||
# Do not complain about OTHER tokens inside macro definitions.
|
||||
# $ and @ appear in macros defined by headers intended to be
|
||||
# included from assembly language, e.g. sysdeps/mips/sys/asm.h.
|
||||
if kind == "OTHER" and directive != "define":
|
||||
self.error(tok, "stray {!r} in program")
|
||||
|
||||
yield tok
|
||||
pos = mo.end()
|
||||
|
||||
#
|
||||
# Base and generic classes for individual checks.
|
||||
#
|
||||
|
||||
class ConstructChecker:
|
||||
"""Scan a stream of C preprocessing tokens and possibly report
|
||||
problems with them. The REPORTER object passed to __init__ has
|
||||
one method, reporter.error(token, message), which should be
|
||||
called to indicate a problem detected at the position of TOKEN.
|
||||
If MESSAGE contains the four-character sequence '{!r}' then that
|
||||
will be replaced with a textual representation of TOKEN.
|
||||
"""
|
||||
def __init__(self, reporter):
|
||||
self.reporter = reporter
|
||||
|
||||
def examine(self, tok):
|
||||
"""Called once for each token in a header file.
|
||||
Call self.reporter.error if a problem is detected.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def eof(self):
|
||||
"""Called once at the end of the stream. Subclasses need only
|
||||
override this if it might have something to do."""
|
||||
pass
|
||||
|
||||
class NoCheck(ConstructChecker):
|
||||
"""Generic checker class which doesn't do anything. Substitute this
|
||||
class for a real checker when a particular check should be skipped
|
||||
for some file."""
|
||||
|
||||
def examine(self, tok):
|
||||
pass
|
||||
|
||||
#
|
||||
# Check for obsolete type names.
|
||||
#
|
||||
|
||||
# The obsolete type names we're looking for:
|
||||
OBSOLETE_TYPE_RE_ = re.compile(r"""\A
|
||||
(__)?
|
||||
( quad_t
|
||||
| u(?: short | int | long
|
||||
| _(?: char | short | int(?:[0-9]+_t)? | long | quad_t )))
|
||||
\Z""", re.VERBOSE)
|
||||
|
||||
class ObsoleteNotAllowed(ConstructChecker):
|
||||
"""Don't allow any use of the obsolete typedefs."""
|
||||
def examine(self, tok):
|
||||
if OBSOLETE_TYPE_RE_.match(tok.text):
|
||||
self.reporter.error(tok, "use of {!r}")
|
||||
|
||||
class ObsoletePrivateDefinitionsAllowed(ConstructChecker):
|
||||
"""Allow definitions of the private versions of the
|
||||
obsolete typedefs; that is, 'typedef [anything] __obsolete;'
|
||||
"""
|
||||
def __init__(self, reporter):
|
||||
super().__init__(reporter)
|
||||
self.in_typedef = False
|
||||
self.prev_token = None
|
||||
|
||||
def examine(self, tok):
|
||||
# bits/types.h hides 'typedef' in a macro sometimes.
|
||||
if (tok.kind == "IDENT"
|
||||
and tok.text in ("typedef", "__STD_TYPE")
|
||||
and tok.context is None):
|
||||
self.in_typedef = True
|
||||
elif tok.kind == "PUNCTUATOR" and tok.text == ";" and self.in_typedef:
|
||||
self.in_typedef = False
|
||||
if self.prev_token.kind == "IDENT":
|
||||
m = OBSOLETE_TYPE_RE_.match(self.prev_token.text)
|
||||
if m and m.group(1) != "__":
|
||||
self.reporter.error(self.prev_token, "use of {!r}")
|
||||
self.prev_token = None
|
||||
else:
|
||||
self._check_prev()
|
||||
|
||||
self.prev_token = tok
|
||||
|
||||
def eof(self):
|
||||
self._check_prev()
|
||||
|
||||
def _check_prev(self):
|
||||
if (self.prev_token is not None
|
||||
and self.prev_token.kind == "IDENT"
|
||||
and OBSOLETE_TYPE_RE_.match(self.prev_token.text)):
|
||||
self.reporter.error(self.prev_token, "use of {!r}")
|
||||
|
||||
class ObsoletePublicDefinitionsAllowed(ConstructChecker):
|
||||
"""Allow definitions of the public versions of the obsolete
|
||||
typedefs. Only specific forms of definition are allowed:
|
||||
|
||||
typedef __obsolete obsolete; // identifiers must agree
|
||||
typedef __uintN_t u_intN_t; // N must agree
|
||||
typedef unsigned long int ulong;
|
||||
typedef unsigned short int ushort;
|
||||
typedef unsigned int uint;
|
||||
"""
|
||||
def __init__(self, reporter):
|
||||
super().__init__(reporter)
|
||||
self.typedef_tokens = []
|
||||
|
||||
def examine(self, tok):
|
||||
if tok.kind in ("WHITESPACE", "BLOCK_COMMENT",
|
||||
"LINE_COMMENT", "NL", "ESCNL"):
|
||||
pass
|
||||
|
||||
elif (tok.kind == "IDENT" and tok.text == "typedef"
|
||||
and tok.context is None):
|
||||
if self.typedef_tokens:
|
||||
self.reporter.error(tok, "typedef inside typedef")
|
||||
self._reset()
|
||||
self.typedef_tokens.append(tok)
|
||||
|
||||
elif tok.kind == "PUNCTUATOR" and tok.text == ";":
|
||||
self._finish()
|
||||
|
||||
elif self.typedef_tokens:
|
||||
self.typedef_tokens.append(tok)
|
||||
|
||||
def eof(self):
|
||||
self._reset()
|
||||
|
||||
def _reset(self):
|
||||
while self.typedef_tokens:
|
||||
tok = self.typedef_tokens.pop(0)
|
||||
if tok.kind == "IDENT" and OBSOLETE_TYPE_RE_.match(tok.text):
|
||||
self.reporter.error(tok, "use of {!r}")
|
||||
|
||||
def _finish(self):
|
||||
if not self.typedef_tokens: return
|
||||
if self.typedef_tokens[-1].kind == "IDENT":
|
||||
m = OBSOLETE_TYPE_RE_.match(self.typedef_tokens[-1].text)
|
||||
if m:
|
||||
if self._permissible_public_definition(m):
|
||||
self.typedef_tokens.clear()
|
||||
self._reset()
|
||||
|
||||
def _permissible_public_definition(self, m):
|
||||
if m.group(1) == "__": return False
|
||||
name = m.group(2)
|
||||
toks = self.typedef_tokens
|
||||
ntok = len(toks)
|
||||
if ntok == 3 and toks[1].kind == "IDENT":
|
||||
defn = toks[1].text
|
||||
n = OBSOLETE_TYPE_RE_.match(defn)
|
||||
if n and n.group(1) == "__" and n.group(2) == name:
|
||||
return True
|
||||
|
||||
if (name[:5] == "u_int" and name[-2:] == "_t"
|
||||
and defn[:6] == "__uint" and defn[-2:] == "_t"
|
||||
and name[5:-2] == defn[6:-2]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if (name == "ulong" and ntok == 5
|
||||
and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
|
||||
and toks[2].kind == "IDENT" and toks[2].text == "long"
|
||||
and toks[3].kind == "IDENT" and toks[3].text == "int"):
|
||||
return True
|
||||
|
||||
if (name == "ushort" and ntok == 5
|
||||
and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
|
||||
and toks[2].kind == "IDENT" and toks[2].text == "short"
|
||||
and toks[3].kind == "IDENT" and toks[3].text == "int"):
|
||||
return True
|
||||
|
||||
if (name == "uint" and ntok == 4
|
||||
and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
|
||||
and toks[2].kind == "IDENT" and toks[2].text == "int"):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def ObsoleteTypedefChecker(reporter, fname):
|
||||
"""Factory: produce an instance of the appropriate
|
||||
obsolete-typedef checker for FNAME."""
|
||||
|
||||
# The obsolete rpc/ and rpcsvc/ headers are allowed to use the
|
||||
# obsolete types, because it would be more trouble than it's
|
||||
# worth to remove them from headers that we intend to stop
|
||||
# installing eventually anyway.
|
||||
if (fname.startswith("rpc/")
|
||||
or fname.startswith("rpcsvc/")
|
||||
or "/rpc/" in fname
|
||||
or "/rpcsvc/" in fname):
|
||||
return NoCheck(reporter)
|
||||
|
||||
# bits/types.h is allowed to define the __-versions of the
|
||||
# obsolete types.
|
||||
if (fname == "bits/types.h"
|
||||
or fname.endswith("/bits/types.h")):
|
||||
return ObsoletePrivateDefinitionsAllowed(reporter)
|
||||
|
||||
# sys/types.h is allowed to use the __-versions of the
|
||||
# obsolete types, but only to define the unprefixed versions.
|
||||
if (fname == "sys/types.h"
|
||||
or fname.endswith("/sys/types.h")):
|
||||
return ObsoletePublicDefinitionsAllowed(reporter)
|
||||
|
||||
return ObsoleteNotAllowed(reporter)
|
||||
|
||||
#
|
||||
# Master control
|
||||
#
|
||||
|
||||
class HeaderChecker:
|
||||
"""Perform all of the checks on each header. This is also the
|
||||
"reporter" object expected by tokenize_c and ConstructChecker.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.fname = None
|
||||
self.status = 0
|
||||
|
||||
def error(self, tok, message):
|
||||
self.status = 1
|
||||
if '{!r}' in message:
|
||||
message = message.format(tok.text)
|
||||
sys.stderr.write("{}:{}:{}: error: {}\n".format(
|
||||
self.fname, tok.line, tok.column, message))
|
||||
|
||||
def check(self, fname):
|
||||
self.fname = fname
|
||||
try:
|
||||
with open(fname, "rt") as fp:
|
||||
contents = fp.read()
|
||||
except OSError as e:
|
||||
sys.stderr.write("{}: {}\n".format(fname, e.strerror))
|
||||
self.status = 1
|
||||
return
|
||||
|
||||
typedef_checker = ObsoleteTypedefChecker(self, self.fname)
|
||||
|
||||
for tok in tokenize_c(contents, self):
|
||||
typedef_checker.examine(tok)
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__)
|
||||
ap.add_argument("headers", metavar="header", nargs="+",
|
||||
help="one or more headers to scan for obsolete constructs")
|
||||
args = ap.parse_args()
|
||||
|
||||
checker = HeaderChecker()
|
||||
for fname in args.headers:
|
||||
# Headers whose installed name begins with "finclude/" contain
|
||||
# Fortran, not C, and this program should completely ignore them.
|
||||
if not (fname.startswith("finclude/") or "/finclude/" in fname):
|
||||
checker.check(fname)
|
||||
sys.exit(checker.status)
|
||||
|
||||
main()
|
||||
+11
-2
@@ -66,6 +66,10 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
|
||||
tst-scanf-round \
|
||||
tst-renameat2 tst-bz11319 tst-bz11319-fortify2 \
|
||||
scanf14a scanf16a \
|
||||
tst-printf-bz25691 \
|
||||
tst-vfprintf-width-prec-alloc \
|
||||
tst-grouping2 \
|
||||
# tests
|
||||
|
||||
|
||||
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
|
||||
@@ -75,10 +79,12 @@ tests-special += $(objpfx)tst-unbputc.out $(objpfx)tst-printf.out \
|
||||
$(objpfx)tst-printf-bz18872-mem.out \
|
||||
$(objpfx)tst-setvbuf1-cmp.out \
|
||||
$(objpfx)tst-vfprintf-width-prec-mem.out \
|
||||
$(objpfx)tst-printfsz-islongdouble.out
|
||||
$(objpfx)tst-printfsz-islongdouble.out \
|
||||
$(objpfx)tst-printf-bz25691-mem.out
|
||||
generated += tst-printf-bz18872.c tst-printf-bz18872.mtrace \
|
||||
tst-printf-bz18872-mem.out \
|
||||
tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out
|
||||
tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out \
|
||||
tst-printf-bz25691.mtrace tst-printf-bz25691-mem.out
|
||||
endif
|
||||
|
||||
include ../Rules
|
||||
@@ -91,6 +97,7 @@ $(objpfx)bug14.out: $(gen-locales)
|
||||
$(objpfx)scanf13.out: $(gen-locales)
|
||||
$(objpfx)test-vfprintf.out: $(gen-locales)
|
||||
$(objpfx)tst-grouping.out: $(gen-locales)
|
||||
$(objpfx)tst-grouping2.out: $(gen-locales)
|
||||
$(objpfx)tst-sprintf.out: $(gen-locales)
|
||||
$(objpfx)tst-sscanf.out: $(gen-locales)
|
||||
$(objpfx)tst-swprintf.out: $(gen-locales)
|
||||
@@ -100,6 +107,8 @@ endif
|
||||
tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace
|
||||
tst-vfprintf-width-prec-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace
|
||||
tst-printf-bz25691-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace
|
||||
|
||||
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
|
||||
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' > $@; \
|
||||
|
||||
@@ -57,7 +57,7 @@ do_test (void)
|
||||
|
||||
ret = fprintf (fp, "%." SN3 "d", 1);
|
||||
printf ("ret = %d\n", ret);
|
||||
if (ret != -1 || errno != EOVERFLOW)
|
||||
if (ret != N3)
|
||||
return 1;
|
||||
|
||||
/* GCC 9 warns about output of more than INT_MAX characters; this is
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Test printf with grouping and large width (bug 29530)
|
||||
Copyright (C) 2022 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 <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
const int field_width = 1000;
|
||||
char buf[field_width + 1];
|
||||
|
||||
xsetlocale (LC_NUMERIC, "de_DE.UTF-8");
|
||||
|
||||
/* This used to crash in group_number. */
|
||||
TEST_COMPARE (sprintf (buf, "%'*d", field_width, 1000), field_width);
|
||||
TEST_COMPARE_STRING (buf + field_width - 6, " 1.000");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Test for memory leak with large width (BZ#25691).
|
||||
Copyright (C) 2020 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdint.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <mcheck.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
|
||||
/* For 's' conversion specifier with 'l' modifier the array must be
|
||||
converted to multibyte characters up to the precision specific
|
||||
value. */
|
||||
{
|
||||
/* The input size value is to force a heap allocation on temporary
|
||||
buffer (in the old implementation). */
|
||||
const size_t winputsize = 64 * 1024 + 1;
|
||||
wchar_t *winput = xmalloc (winputsize * sizeof (wchar_t));
|
||||
wmemset (winput, L'a', winputsize - 1);
|
||||
winput[winputsize - 1] = L'\0';
|
||||
|
||||
char result[9];
|
||||
const char expected[] = "aaaaaaaa";
|
||||
int ret;
|
||||
|
||||
ret = snprintf (result, sizeof (result), "%.65537ls", winput);
|
||||
TEST_COMPARE (ret, winputsize - 1);
|
||||
TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
|
||||
|
||||
ret = snprintf (result, sizeof (result), "%ls", winput);
|
||||
TEST_COMPARE (ret, winputsize - 1);
|
||||
TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
|
||||
|
||||
free (winput);
|
||||
}
|
||||
|
||||
/* For 's' converstion specifier the array is interpreted as a multibyte
|
||||
character sequence and converted to wide characters up to the precision
|
||||
specific value. */
|
||||
{
|
||||
/* The input size value is to force a heap allocation on temporary
|
||||
buffer (in the old implementation). */
|
||||
const size_t mbssize = 32 * 1024;
|
||||
char *mbs = xmalloc (mbssize);
|
||||
memset (mbs, 'a', mbssize - 1);
|
||||
mbs[mbssize - 1] = '\0';
|
||||
|
||||
const size_t expectedsize = 32 * 1024;
|
||||
wchar_t *expected = xmalloc (expectedsize * sizeof (wchar_t));
|
||||
wmemset (expected, L'a', expectedsize - 1);
|
||||
expected[expectedsize-1] = L'\0';
|
||||
|
||||
const size_t resultsize = mbssize * sizeof (wchar_t);
|
||||
wchar_t *result = xmalloc (resultsize);
|
||||
int ret;
|
||||
|
||||
ret = swprintf (result, resultsize, L"%.65537s", mbs);
|
||||
TEST_COMPARE (ret, mbssize - 1);
|
||||
TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
|
||||
expected, expectedsize * sizeof (wchar_t));
|
||||
|
||||
ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
|
||||
TEST_COMPARE (ret, mbssize - 1);
|
||||
TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
|
||||
expected, expectedsize * sizeof (wchar_t));
|
||||
|
||||
/* Same test, but with an invalid multibyte sequence. */
|
||||
mbs[mbssize - 2] = 0xff;
|
||||
|
||||
ret = swprintf (result, resultsize, L"%.65537s", mbs);
|
||||
TEST_COMPARE (ret, -1);
|
||||
|
||||
ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
|
||||
TEST_COMPARE (ret, -1);
|
||||
|
||||
free (mbs);
|
||||
free (result);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Test large width or precision does not involve large allocation.
|
||||
Copyright (C) 2020 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 <stdio.h>
|
||||
#include <sys/resource.h>
|
||||
#include <support/check.h>
|
||||
|
||||
char test_string[] = "test";
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct rlimit limit;
|
||||
TEST_VERIFY_EXIT (getrlimit (RLIMIT_AS, &limit) == 0);
|
||||
limit.rlim_cur = 200 * 1024 * 1024;
|
||||
TEST_VERIFY_EXIT (setrlimit (RLIMIT_AS, &limit) == 0);
|
||||
FILE *fp = fopen ("/dev/null", "w");
|
||||
TEST_VERIFY_EXIT (fp != NULL);
|
||||
TEST_COMPARE (fprintf (fp, "%1000000000d", 1), 1000000000);
|
||||
TEST_COMPARE (fprintf (fp, "%.1000000000s", test_string), 4);
|
||||
TEST_COMPARE (fprintf (fp, "%1000000000d %1000000000d", 1, 2), 2000000001);
|
||||
TEST_COMPARE (fprintf (fp, "%2$.*1$s", 0x7fffffff, test_string), 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+183
-264
@@ -31,6 +31,7 @@
|
||||
#include <locale/localeinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <scratch_buffer.h>
|
||||
#include <intprops.h>
|
||||
|
||||
/* This code is shared between the standard stdio implementation found
|
||||
in GNU C library and the libio implementation originally found in
|
||||
@@ -45,10 +46,6 @@
|
||||
#include <wctype.h>
|
||||
#endif
|
||||
|
||||
/* In some cases we need extra space for all the output which is not
|
||||
counted in the width of the string. We assume 32 characters is
|
||||
enough. */
|
||||
#define EXTSIZ 32
|
||||
#define ARGCHECK(S, Format) \
|
||||
do \
|
||||
{ \
|
||||
@@ -68,22 +65,38 @@
|
||||
} while (0)
|
||||
#define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
|
||||
|
||||
#define done_add(val) \
|
||||
do { \
|
||||
unsigned int _val = val; \
|
||||
assert ((unsigned int) done < (unsigned int) INT_MAX); \
|
||||
if (__glibc_unlikely (INT_MAX - done < _val)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
__set_errno (EOVERFLOW); \
|
||||
goto all_done; \
|
||||
} \
|
||||
done += _val; \
|
||||
} while (0)
|
||||
/* Add LENGTH to DONE. Return the new value of DONE, or -1 on
|
||||
overflow (and set errno accordingly). */
|
||||
static inline int
|
||||
done_add_func (size_t length, int done)
|
||||
{
|
||||
if (done < 0)
|
||||
return done;
|
||||
int ret;
|
||||
if (INT_ADD_WRAPV (done, length, &ret))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define done_add(val) \
|
||||
do \
|
||||
{ \
|
||||
/* Ensure that VAL has a type similar to int. */ \
|
||||
_Static_assert (sizeof (val) == sizeof (int), "value int size"); \
|
||||
_Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \
|
||||
done = done_add_func ((val), done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#ifndef COMPILE_WPRINTF
|
||||
# define vfprintf __vfprintf_internal
|
||||
# define CHAR_T char
|
||||
# define OTHER_CHAR_T wchar_t
|
||||
# define UCHAR_T unsigned char
|
||||
# define INT_T int
|
||||
typedef const char *THOUSANDS_SEP_T;
|
||||
@@ -92,25 +105,14 @@ typedef const char *THOUSANDS_SEP_T;
|
||||
# define STR_LEN(Str) strlen (Str)
|
||||
|
||||
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
|
||||
# define PAD(Padchar) \
|
||||
do { \
|
||||
if (width > 0) \
|
||||
{ \
|
||||
ssize_t written = _IO_padn (s, (Padchar), width); \
|
||||
if (__glibc_unlikely (written != width)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done_add (written); \
|
||||
} \
|
||||
} while (0)
|
||||
# define PUTC(C, F) _IO_putc_unlocked (C, F)
|
||||
# define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
|
||||
return -1
|
||||
# define CONVERT_FROM_OTHER_STRING __wcsrtombs
|
||||
#else
|
||||
# define vfprintf __vfwprintf_internal
|
||||
# define CHAR_T wchar_t
|
||||
# define OTHER_CHAR_T char
|
||||
/* This is a hack!!! There should be a type uwchar_t. */
|
||||
# define UCHAR_T unsigned int /* uwchar_t */
|
||||
# define INT_T wint_t
|
||||
@@ -122,21 +124,9 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
# include <_itowa.h>
|
||||
|
||||
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
|
||||
# define PAD(Padchar) \
|
||||
do { \
|
||||
if (width > 0) \
|
||||
{ \
|
||||
ssize_t written = _IO_wpadn (s, (Padchar), width); \
|
||||
if (__glibc_unlikely (written != width)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done_add (written); \
|
||||
} \
|
||||
} while (0)
|
||||
# define PUTC(C, F) _IO_putwc_unlocked (C, F)
|
||||
# define ORIENT if (_IO_fwide (s, 1) != 1) return -1
|
||||
# define CONVERT_FROM_OTHER_STRING __mbsrtowcs
|
||||
|
||||
# undef _itoa
|
||||
# define _itoa(Val, Buf, Base, Case) _itowa (Val, Buf, Base, Case)
|
||||
@@ -145,6 +135,33 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
# define EOF WEOF
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
pad_func (FILE *s, CHAR_T padchar, int width, int done)
|
||||
{
|
||||
if (width > 0)
|
||||
{
|
||||
ssize_t written;
|
||||
#ifndef COMPILE_WPRINTF
|
||||
written = _IO_padn (s, padchar, width);
|
||||
#else
|
||||
written = _IO_wpadn (s, padchar, width);
|
||||
#endif
|
||||
if (__glibc_unlikely (written != width))
|
||||
return -1;
|
||||
return done_add_func (width, done);
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
#define PAD(Padchar) \
|
||||
do \
|
||||
{ \
|
||||
done = pad_func (s, (Padchar), width, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#include "_i18n_number.h"
|
||||
|
||||
/* Include the shared code for parsing the format string. */
|
||||
@@ -164,24 +181,115 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define outstring(String, Len) \
|
||||
do \
|
||||
{ \
|
||||
assert ((size_t) done <= (size_t) INT_MAX); \
|
||||
if ((size_t) PUT (s, (String), (Len)) != (size_t) (Len)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
if (__glibc_unlikely (INT_MAX - done < (Len))) \
|
||||
{ \
|
||||
done = -1; \
|
||||
__set_errno (EOVERFLOW); \
|
||||
goto all_done; \
|
||||
} \
|
||||
done += (Len); \
|
||||
} \
|
||||
while (0)
|
||||
static inline int
|
||||
outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
|
||||
{
|
||||
assert ((size_t) done <= (size_t) INT_MAX);
|
||||
if ((size_t) PUT (s, string, length) != (size_t) (length))
|
||||
return -1;
|
||||
return done_add_func (length, done);
|
||||
}
|
||||
|
||||
#define outstring(String, Len) \
|
||||
do \
|
||||
{ \
|
||||
const void *string_ = (String); \
|
||||
done = outstring_func (s, string_, (Len), done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* Write the string SRC to S. If PREC is non-negative, write at most
|
||||
PREC bytes. If LEFT is true, perform left justification. */
|
||||
static int
|
||||
outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
|
||||
int width, bool left, int done)
|
||||
{
|
||||
/* Use a small buffer to combine processing of multiple characters.
|
||||
CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
|
||||
characters, and buf_length counts that. */
|
||||
enum { buf_length = 256 / sizeof (CHAR_T) };
|
||||
CHAR_T buf[buf_length];
|
||||
_Static_assert (sizeof (buf) > MB_LEN_MAX,
|
||||
"buffer is large enough for a single multi-byte character");
|
||||
|
||||
/* Add the initial padding if needed. */
|
||||
if (width > 0 && !left)
|
||||
{
|
||||
/* Make a first pass to find the output width, so that we can
|
||||
add the required padding. */
|
||||
mbstate_t mbstate = { 0 };
|
||||
const OTHER_CHAR_T *src_copy = src;
|
||||
size_t total_written;
|
||||
if (prec < 0)
|
||||
total_written = CONVERT_FROM_OTHER_STRING
|
||||
(NULL, &src_copy, 0, &mbstate);
|
||||
else
|
||||
{
|
||||
/* The source might not be null-terminated. Enforce the
|
||||
limit manually, based on the output length. */
|
||||
total_written = 0;
|
||||
size_t limit = prec;
|
||||
while (limit > 0 && src_copy != NULL)
|
||||
{
|
||||
size_t write_limit = buf_length;
|
||||
if (write_limit > limit)
|
||||
write_limit = limit;
|
||||
size_t written = CONVERT_FROM_OTHER_STRING
|
||||
(buf, &src_copy, write_limit, &mbstate);
|
||||
if (written == (size_t) -1)
|
||||
return -1;
|
||||
if (written == 0)
|
||||
break;
|
||||
total_written += written;
|
||||
limit -= written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output initial padding. */
|
||||
if (total_written < width)
|
||||
{
|
||||
done = pad_func (s, L_(' '), width - total_written, done);
|
||||
if (done < 0)
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert the input string, piece by piece. */
|
||||
size_t total_written = 0;
|
||||
{
|
||||
mbstate_t mbstate = { 0 };
|
||||
/* If prec is negative, remaining is not decremented, otherwise,
|
||||
it serves as the write limit. */
|
||||
size_t remaining = -1;
|
||||
if (prec >= 0)
|
||||
remaining = prec;
|
||||
while (remaining > 0 && src != NULL)
|
||||
{
|
||||
size_t write_limit = buf_length;
|
||||
if (remaining < write_limit)
|
||||
write_limit = remaining;
|
||||
size_t written = CONVERT_FROM_OTHER_STRING
|
||||
(buf, &src, write_limit, &mbstate);
|
||||
if (written == (size_t) -1)
|
||||
return -1;
|
||||
if (written == 0)
|
||||
break;
|
||||
done = outstring_func (s, (const UCHAR_T *) buf, written, done);
|
||||
if (done < 0)
|
||||
return done;
|
||||
total_written += written;
|
||||
if (prec >= 0)
|
||||
remaining -= written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add final padding. */
|
||||
if (width > 0 && left && total_written < width)
|
||||
return pad_func (s, L_(' '), width - total_written, done);
|
||||
return done;
|
||||
}
|
||||
|
||||
/* For handling long_double and longlong we use the same flag. If
|
||||
`long' and `long long' are effectively the same type define it to
|
||||
@@ -979,7 +1087,6 @@ static const uint8_t jump_table[] =
|
||||
LABEL (form_string): \
|
||||
{ \
|
||||
size_t len; \
|
||||
int string_malloced; \
|
||||
\
|
||||
/* The string argument could in fact be `char *' or `wchar_t *'. \
|
||||
But this should not make a difference here. */ \
|
||||
@@ -991,7 +1098,6 @@ static const uint8_t jump_table[] =
|
||||
/* Entry point for printing other strings. */ \
|
||||
LABEL (print_string): \
|
||||
\
|
||||
string_malloced = 0; \
|
||||
if (string == NULL) \
|
||||
{ \
|
||||
/* Write "(null)" if there's space. */ \
|
||||
@@ -1008,41 +1114,12 @@ static const uint8_t jump_table[] =
|
||||
} \
|
||||
else if (!is_long && spec != L_('S')) \
|
||||
{ \
|
||||
/* This is complicated. We have to transform the multibyte \
|
||||
string into a wide character string. */ \
|
||||
const char *mbs = (const char *) string; \
|
||||
mbstate_t mbstate; \
|
||||
\
|
||||
len = prec != -1 ? __strnlen (mbs, (size_t) prec) : strlen (mbs); \
|
||||
\
|
||||
/* Allocate dynamically an array which definitely is long \
|
||||
enough for the wide character version. Each byte in the \
|
||||
multi-byte string can produce at most one wide character. */ \
|
||||
if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t))) \
|
||||
{ \
|
||||
__set_errno (EOVERFLOW); \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else if (__libc_use_alloca (len * sizeof (wchar_t))) \
|
||||
string = (CHAR_T *) alloca (len * sizeof (wchar_t)); \
|
||||
else if ((string = (CHAR_T *) malloc (len * sizeof (wchar_t))) \
|
||||
== NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
\
|
||||
memset (&mbstate, '\0', sizeof (mbstate_t)); \
|
||||
len = __mbsrtowcs (string, &mbs, len, &mbstate); \
|
||||
if (len == (size_t) -1) \
|
||||
{ \
|
||||
/* Illegal multibyte character. */ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done = outstring_converted_wide_string \
|
||||
(s, (const char *) string, prec, width, left, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
/* The padding has already been written. */ \
|
||||
break; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -1065,8 +1142,6 @@ static const uint8_t jump_table[] =
|
||||
outstring (string, len); \
|
||||
if (left) \
|
||||
PAD (L' '); \
|
||||
if (__glibc_unlikely (string_malloced)) \
|
||||
free (string); \
|
||||
} \
|
||||
break;
|
||||
#else
|
||||
@@ -1115,7 +1190,6 @@ static const uint8_t jump_table[] =
|
||||
LABEL (form_string): \
|
||||
{ \
|
||||
size_t len; \
|
||||
int string_malloced; \
|
||||
\
|
||||
/* The string argument could in fact be `char *' or `wchar_t *'. \
|
||||
But this should not make a difference here. */ \
|
||||
@@ -1127,7 +1201,6 @@ static const uint8_t jump_table[] =
|
||||
/* Entry point for printing other strings. */ \
|
||||
LABEL (print_string): \
|
||||
\
|
||||
string_malloced = 0; \
|
||||
if (string == NULL) \
|
||||
{ \
|
||||
/* Write "(null)" if there's space. */ \
|
||||
@@ -1153,51 +1226,12 @@ static const uint8_t jump_table[] =
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
const wchar_t *s2 = (const wchar_t *) string; \
|
||||
mbstate_t mbstate; \
|
||||
\
|
||||
memset (&mbstate, '\0', sizeof (mbstate_t)); \
|
||||
\
|
||||
if (prec >= 0) \
|
||||
{ \
|
||||
/* The string `s2' might not be NUL terminated. */ \
|
||||
if (__libc_use_alloca (prec)) \
|
||||
string = (char *) alloca (prec); \
|
||||
else if ((string = (char *) malloc (prec)) == NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
len = __wcsrtombs (string, &s2, prec, &mbstate); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
len = __wcsrtombs (NULL, &s2, 0, &mbstate); \
|
||||
if (len != (size_t) -1) \
|
||||
{ \
|
||||
assert (__mbsinit (&mbstate)); \
|
||||
s2 = (const wchar_t *) string; \
|
||||
if (__libc_use_alloca (len + 1)) \
|
||||
string = (char *) alloca (len + 1); \
|
||||
else if ((string = (char *) malloc (len + 1)) == NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
(void) __wcsrtombs (string, &s2, len + 1, &mbstate); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
if (len == (size_t) -1) \
|
||||
{ \
|
||||
/* Illegal wide-character string. */ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done = outstring_converted_wide_string \
|
||||
(s, (const wchar_t *) string, prec, width, left, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
/* The padding has already been written. */ \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if ((width -= len) < 0) \
|
||||
@@ -1211,8 +1245,6 @@ static const uint8_t jump_table[] =
|
||||
outstring (string, len); \
|
||||
if (left) \
|
||||
PAD (' '); \
|
||||
if (__glibc_unlikely (string_malloced)) \
|
||||
free (string); \
|
||||
} \
|
||||
break;
|
||||
#endif
|
||||
@@ -1264,7 +1296,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
|
||||
/* Buffer intermediate results. */
|
||||
CHAR_T work_buffer[WORK_BUFFER_SIZE];
|
||||
CHAR_T *workstart = NULL;
|
||||
CHAR_T *workend;
|
||||
|
||||
/* We have to save the original argument pointer. */
|
||||
@@ -1373,7 +1404,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
UCHAR_T pad = L_(' ');/* Padding character. */
|
||||
CHAR_T spec;
|
||||
|
||||
workstart = NULL;
|
||||
workend = work_buffer + WORK_BUFFER_SIZE;
|
||||
|
||||
/* Get current character in format string. */
|
||||
@@ -1465,31 +1495,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
pad = L_(' ');
|
||||
left = 1;
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
if (width >= WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* We have to use a special buffer. */
|
||||
size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + width + EXTSIZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
JUMP (*f, step1_jumps);
|
||||
|
||||
@@ -1497,31 +1502,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
LABEL (width):
|
||||
width = read_int (&f);
|
||||
|
||||
if (__glibc_unlikely (width == -1
|
||||
|| width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
if (__glibc_unlikely (width == -1))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
if (width >= WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* We have to use a special buffer. */
|
||||
size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + width + EXTSIZ;
|
||||
}
|
||||
}
|
||||
if (*f == L_('$'))
|
||||
/* Oh, oh. The argument comes from a positional parameter. */
|
||||
goto do_positional;
|
||||
@@ -1570,34 +1557,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
}
|
||||
else
|
||||
prec = 0;
|
||||
if (prec > width && prec > WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* Deallocate any previously allocated buffer because it is
|
||||
too small. */
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
size_t needed = ((size_t) prec + EXTSIZ) * sizeof (CHAR_T);
|
||||
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + prec + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + prec + EXTSIZ;
|
||||
}
|
||||
}
|
||||
JUMP (*f, step2_jumps);
|
||||
|
||||
/* Process 'h' modifier. There might another 'h' following. */
|
||||
@@ -1661,10 +1620,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
/* The format is correctly handled. */
|
||||
++nspecs_done;
|
||||
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
|
||||
/* Look for next format specifier. */
|
||||
#ifdef COMPILE_WPRINTF
|
||||
f = __find_specwc ((end_of_spec = ++f));
|
||||
@@ -1682,18 +1637,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
|
||||
/* Hand off processing for positional parameters. */
|
||||
do_positional:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
{
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
}
|
||||
done = printf_positional (s, format, readonly_format, ap, &ap_save,
|
||||
done, nspecs_done, lead_str_end, work_buffer,
|
||||
save_errno, grouping, thousands_sep, mode_flags);
|
||||
|
||||
all_done:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
/* Unlock the stream. */
|
||||
_IO_funlockfile (s);
|
||||
_IO_cleanup_region_end (0);
|
||||
@@ -1737,8 +1685,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
/* Just a counter. */
|
||||
size_t cnt;
|
||||
|
||||
CHAR_T *workstart = NULL;
|
||||
|
||||
if (grouping == (const char *) -1)
|
||||
{
|
||||
#ifdef COMPILE_WPRINTF
|
||||
@@ -1927,7 +1873,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
char pad = specs[nspecs_done].info.pad;
|
||||
CHAR_T spec = specs[nspecs_done].info.spec;
|
||||
|
||||
workstart = NULL;
|
||||
CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
|
||||
|
||||
/* Fill in last information. */
|
||||
@@ -1961,27 +1906,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
prec = specs[nspecs_done].info.prec;
|
||||
}
|
||||
|
||||
/* Maybe the buffer is too small. */
|
||||
if (MAX (prec, width) + EXTSIZ > WORK_BUFFER_SIZE)
|
||||
{
|
||||
if (__libc_use_alloca ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T)))
|
||||
workend = ((CHAR_T *) alloca ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T))
|
||||
+ (MAX (prec, width) + EXTSIZ));
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T));
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + (MAX (prec, width) + EXTSIZ);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process format specifiers. */
|
||||
while (1)
|
||||
{
|
||||
@@ -2055,18 +1979,12 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
break;
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
|
||||
/* Write the following constant string. */
|
||||
outstring (specs[nspecs_done].end_of_fmt,
|
||||
specs[nspecs_done].next_fmt
|
||||
- specs[nspecs_done].end_of_fmt);
|
||||
}
|
||||
all_done:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
scratch_buffer_free (&argsbuf);
|
||||
scratch_buffer_free (&specsbuf);
|
||||
return done;
|
||||
@@ -2189,7 +2107,8 @@ group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
|
||||
copy_rest:
|
||||
/* No further grouping to be done. Copy the rest of the
|
||||
number. */
|
||||
memmove (w, s, (front_ptr -s) * sizeof (CHAR_T));
|
||||
w -= s - front_ptr;
|
||||
memmove (w, front_ptr, (s - front_ptr) * sizeof (CHAR_T));
|
||||
break;
|
||||
}
|
||||
else if (*grouping != '\0')
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define HEADER_MAX 256
|
||||
|
||||
|
||||
+18
-170
@@ -30,156 +30,12 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static char MAGIC_ARGUMENT[] = "run-actual-test";
|
||||
#define MAGIC_STATUS 19
|
||||
|
||||
/* Return a GID which is not our current GID, but is present in the
|
||||
supplementary group list. */
|
||||
static gid_t
|
||||
choose_gid (void)
|
||||
{
|
||||
const int count = 64;
|
||||
gid_t groups[count];
|
||||
int ret = getgroups (count, groups);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf ("getgroups: %m\n");
|
||||
exit (1);
|
||||
}
|
||||
gid_t current = getgid ();
|
||||
for (int i = 0; i < ret; ++i)
|
||||
{
|
||||
if (groups[i] != current)
|
||||
return groups[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Copies the executable into a restricted directory, so that we can
|
||||
safely make it SGID with the TARGET group ID. Then runs the
|
||||
executable. */
|
||||
static int
|
||||
run_executable_sgid (gid_t target)
|
||||
{
|
||||
char *dirname = xasprintf ("%s/secure-getenv.%jd",
|
||||
test_dir, (intmax_t) getpid ());
|
||||
char *execname = xasprintf ("%s/bin", dirname);
|
||||
int infd = -1;
|
||||
int outfd = -1;
|
||||
int ret = -1;
|
||||
if (mkdir (dirname, 0700) < 0)
|
||||
{
|
||||
printf ("mkdir: %m\n");
|
||||
goto err;
|
||||
}
|
||||
infd = open ("/proc/self/exe", O_RDONLY);
|
||||
if (infd < 0)
|
||||
{
|
||||
printf ("open (/proc/self/exe): %m\n");
|
||||
goto err;
|
||||
}
|
||||
outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
|
||||
if (outfd < 0)
|
||||
{
|
||||
printf ("open (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
char buf[4096];
|
||||
for (;;)
|
||||
{
|
||||
ssize_t rdcount = read (infd, buf, sizeof (buf));
|
||||
if (rdcount < 0)
|
||||
{
|
||||
printf ("read: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (rdcount == 0)
|
||||
break;
|
||||
char *p = buf;
|
||||
char *end = buf + rdcount;
|
||||
while (p != end)
|
||||
{
|
||||
ssize_t wrcount = write (outfd, buf, end - p);
|
||||
if (wrcount == 0)
|
||||
errno = ENOSPC;
|
||||
if (wrcount <= 0)
|
||||
{
|
||||
printf ("write: %m\n");
|
||||
goto err;
|
||||
}
|
||||
p += wrcount;
|
||||
}
|
||||
}
|
||||
if (fchown (outfd, getuid (), target) < 0)
|
||||
{
|
||||
printf ("fchown (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (fchmod (outfd, 02750) < 0)
|
||||
{
|
||||
printf ("fchmod (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (close (outfd) < 0)
|
||||
{
|
||||
printf ("close (outfd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (close (infd) < 0)
|
||||
{
|
||||
printf ("close (infd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
int kid = fork ();
|
||||
if (kid < 0)
|
||||
{
|
||||
printf ("fork: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (kid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
char *args[] = { execname, MAGIC_ARGUMENT, NULL };
|
||||
execve (execname, args, environ);
|
||||
printf ("execve (%s): %m\n", execname);
|
||||
_exit (1);
|
||||
}
|
||||
int status;
|
||||
if (waitpid (kid, &status, 0) < 0)
|
||||
{
|
||||
printf ("waitpid: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != MAGIC_STATUS)
|
||||
{
|
||||
printf ("Unexpected exit status %d from child process\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
if (outfd >= 0)
|
||||
close (outfd);
|
||||
if (infd >= 0)
|
||||
close (infd);
|
||||
if (execname)
|
||||
{
|
||||
unlink (execname);
|
||||
free (execname);
|
||||
}
|
||||
if (dirname)
|
||||
{
|
||||
rmdir (dirname);
|
||||
free (dirname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
@@ -201,15 +57,15 @@ do_test (void)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
gid_t target = choose_gid ();
|
||||
if (target == 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Could not find a suitable GID for user %jd, skipping test\n",
|
||||
(intmax_t) getuid ());
|
||||
exit (0);
|
||||
}
|
||||
return run_executable_sgid (target);
|
||||
int status = support_capture_subprogram_self_sgid (MAGIC_ARGUMENT);
|
||||
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status))
|
||||
FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -218,23 +74,15 @@ alternative_main (int argc, char **argv)
|
||||
if (argc == 2 && strcmp (argv[1], MAGIC_ARGUMENT) == 0)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
{
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
exit (MAGIC_STATUS);
|
||||
}
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
if (getenv ("PATH") == NULL)
|
||||
{
|
||||
printf ("PATH variable not present\n");
|
||||
exit (3);
|
||||
}
|
||||
FAIL_EXIT (3, "PATH variable not present\n");
|
||||
if (secure_getenv ("PATH") != NULL)
|
||||
{
|
||||
printf ("PATH variable not filtered out\n");
|
||||
exit (4);
|
||||
}
|
||||
exit (MAGIC_STATUS);
|
||||
FAIL_EXIT (4, "PATH variable not filtered out\n");
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+83
-40
@@ -15,17 +15,13 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This particular implementation was written by Eric Blake, 2008. */
|
||||
|
||||
#ifndef _LIBC
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
/* Specification of memmem. */
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _LIBC
|
||||
# define __builtin_expect(expr, val) (expr)
|
||||
# define __memmem memmem
|
||||
#endif
|
||||
|
||||
@@ -36,51 +32,98 @@
|
||||
|
||||
#undef memmem
|
||||
|
||||
/* Return the first occurrence of NEEDLE in HAYSTACK. Return HAYSTACK
|
||||
if NEEDLE_LEN is 0, otherwise NULL if NEEDLE is not found in
|
||||
HAYSTACK. */
|
||||
/* Hash character pairs so a small shift table can be used. All bits of
|
||||
p[0] are included, but not all bits from p[-1]. So if two equal hashes
|
||||
match on p[-1], p[0] matches too. Hash collisions are harmless and result
|
||||
in smaller shifts. */
|
||||
#define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift))
|
||||
|
||||
/* Fast memmem algorithm with guaranteed linear-time performance.
|
||||
Small needles up to size 2 use a dedicated linear search. Longer needles
|
||||
up to size 256 use a novel modified Horspool algorithm. It hashes pairs
|
||||
of characters to quickly skip past mismatches. The main search loop only
|
||||
exits if the last 2 characters match, avoiding unnecessary calls to memcmp
|
||||
and allowing for a larger skip if there is no match. A self-adapting
|
||||
filtering check is used to quickly detect mismatches in long needles.
|
||||
By limiting the needle length to 256, the shift table can be reduced to 8
|
||||
bits per entry, lowering preprocessing overhead and minimizing cache effects.
|
||||
The limit also implies worst-case performance is linear.
|
||||
Needles larger than 256 characters use the linear-time Two-Way algorithm. */
|
||||
void *
|
||||
__memmem (const void *haystack_start, size_t haystack_len,
|
||||
const void *needle_start, size_t needle_len)
|
||||
__memmem (const void *haystack, size_t hs_len,
|
||||
const void *needle, size_t ne_len)
|
||||
{
|
||||
/* Abstract memory is considered to be an array of 'unsigned char' values,
|
||||
not an array of 'char' values. See ISO C 99 section 6.2.6.1. */
|
||||
const unsigned char *haystack = (const unsigned char *) haystack_start;
|
||||
const unsigned char *needle = (const unsigned char *) needle_start;
|
||||
const unsigned char *hs = (const unsigned char *) haystack;
|
||||
const unsigned char *ne = (const unsigned char *) needle;
|
||||
|
||||
if (needle_len == 0)
|
||||
/* The first occurrence of the empty string is deemed to occur at
|
||||
the beginning of the string. */
|
||||
return (void *) haystack;
|
||||
if (ne_len == 0)
|
||||
return (void *) hs;
|
||||
if (ne_len == 1)
|
||||
return (void *) memchr (hs, ne[0], hs_len);
|
||||
|
||||
/* Sanity check, otherwise the loop might search through the whole
|
||||
memory. */
|
||||
if (__glibc_unlikely (haystack_len < needle_len))
|
||||
/* Ensure haystack length is >= needle length. */
|
||||
if (hs_len < ne_len)
|
||||
return NULL;
|
||||
|
||||
/* Use optimizations in memchr when possible, to reduce the search
|
||||
size of haystack using a linear algorithm with a smaller
|
||||
coefficient. However, avoid memchr for long needles, since we
|
||||
can often achieve sublinear performance. */
|
||||
if (needle_len < LONG_NEEDLE_THRESHOLD)
|
||||
const unsigned char *end = hs + hs_len - ne_len;
|
||||
|
||||
if (ne_len == 2)
|
||||
{
|
||||
haystack = memchr (haystack, *needle, haystack_len);
|
||||
if (!haystack || __builtin_expect (needle_len == 1, 0))
|
||||
return (void *) haystack;
|
||||
haystack_len -= haystack - (const unsigned char *) haystack_start;
|
||||
if (haystack_len < needle_len)
|
||||
return NULL;
|
||||
/* Check whether we have a match. This improves performance since we
|
||||
avoid the initialization overhead of the two-way algorithm. */
|
||||
if (memcmp (haystack, needle, needle_len) == 0)
|
||||
return (void *) haystack;
|
||||
return two_way_short_needle (haystack, haystack_len, needle, needle_len);
|
||||
uint32_t nw = ne[0] << 16 | ne[1], hw = hs[0] << 16 | hs[1];
|
||||
for (hs++; hs <= end && hw != nw; )
|
||||
hw = hw << 16 | *++hs;
|
||||
return hw == nw ? (void *)hs - 1 : NULL;
|
||||
}
|
||||
else
|
||||
return two_way_long_needle (haystack, haystack_len, needle, needle_len);
|
||||
|
||||
/* Use Two-Way algorithm for very long needles. */
|
||||
if (__builtin_expect (ne_len > 256, 0))
|
||||
return two_way_long_needle (hs, hs_len, ne, ne_len);
|
||||
|
||||
uint8_t shift[256];
|
||||
size_t tmp, shift1;
|
||||
size_t m1 = ne_len - 1;
|
||||
size_t offset = 0;
|
||||
|
||||
memset (shift, 0, sizeof (shift));
|
||||
for (int i = 1; i < m1; i++)
|
||||
shift[hash2 (ne + i)] = i;
|
||||
/* Shift1 is the amount we can skip after matching the hash of the
|
||||
needle end but not the full needle. */
|
||||
shift1 = m1 - shift[hash2 (ne + m1)];
|
||||
shift[hash2 (ne + m1)] = m1;
|
||||
|
||||
for ( ; hs <= end; )
|
||||
{
|
||||
/* Skip past character pairs not in the needle. */
|
||||
do
|
||||
{
|
||||
hs += m1;
|
||||
tmp = shift[hash2 (hs)];
|
||||
}
|
||||
while (tmp == 0 && hs <= end);
|
||||
|
||||
/* If the match is not at the end of the needle, shift to the end
|
||||
and continue until we match the hash of the needle end. */
|
||||
hs -= tmp;
|
||||
if (tmp < m1)
|
||||
continue;
|
||||
|
||||
/* Hash of the last 2 characters matches. If the needle is long,
|
||||
try to quickly filter out mismatches. */
|
||||
if (m1 < 15 || memcmp (hs + offset, ne + offset, 8) == 0)
|
||||
{
|
||||
if (memcmp (hs, ne, m1) == 0)
|
||||
return (void *) hs;
|
||||
|
||||
/* Adjust filter offset when it doesn't find the mismatch. */
|
||||
offset = (offset >= 8 ? offset : m1) - 8;
|
||||
}
|
||||
|
||||
/* Skip based on matching the hash of the needle end. */
|
||||
hs += shift1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
libc_hidden_def (__memmem)
|
||||
weak_alias (__memmem, memmem)
|
||||
libc_hidden_weak (memmem)
|
||||
|
||||
#undef LONG_NEEDLE_THRESHOLD
|
||||
|
||||
@@ -221,7 +221,7 @@ critical_factorization (const unsigned char *needle, size_t needle_len,
|
||||
most 2 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching.
|
||||
If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 *
|
||||
HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching. */
|
||||
static RETURN_TYPE
|
||||
static inline RETURN_TYPE
|
||||
two_way_short_needle (const unsigned char *haystack, size_t haystack_len,
|
||||
const unsigned char *needle, size_t needle_len)
|
||||
{
|
||||
@@ -382,8 +382,11 @@ two_way_short_needle (const unsigned char *haystack, size_t haystack_len,
|
||||
and sublinear performance O(HAYSTACK_LEN / NEEDLE_LEN) is possible.
|
||||
If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 *
|
||||
HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching, and
|
||||
sublinear performance is not possible. */
|
||||
static RETURN_TYPE
|
||||
sublinear performance is not possible.
|
||||
|
||||
Since this function is large and complex, block inlining to avoid
|
||||
slowing down the common case of small needles. */
|
||||
__attribute__((noinline)) static RETURN_TYPE
|
||||
two_way_long_needle (const unsigned char *haystack, size_t haystack_len,
|
||||
const unsigned char *needle, size_t needle_len)
|
||||
{
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ __BEGIN_DECLS
|
||||
#include <stddef.h>
|
||||
|
||||
/* Tell the caller that we provide correct C++ prototypes. */
|
||||
#if defined __cplusplus && __GNUC_PREREQ (4, 4)
|
||||
#if defined __cplusplus && (__GNUC_PREREQ (4, 4) \
|
||||
|| __glibc_clang_prereq (3, 5))
|
||||
# define __CORRECT_ISO_CPP_STRING_H_PROTO
|
||||
#endif
|
||||
|
||||
|
||||
+114
-45
@@ -16,29 +16,17 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This particular implementation was written by Eric Blake, 2008. */
|
||||
|
||||
#ifndef _LIBC
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
/* Specification of strstr. */
|
||||
#include <string.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef _LIBC
|
||||
# define __builtin_expect(expr, val) (expr)
|
||||
#endif
|
||||
|
||||
#define RETURN_TYPE char *
|
||||
#define AVAILABLE(h, h_l, j, n_l) \
|
||||
(((j) + (n_l) <= (h_l)) \
|
||||
|| ((h_l) += __strnlen ((void*)((h) + (h_l)), (n_l) + 512), \
|
||||
(j) + (n_l) <= (h_l)))
|
||||
#define CHECK_EOL (1)
|
||||
#define RET0_IF_0(a) if (!a) goto ret0
|
||||
#define FASTSEARCH(S,C,N) (void*) strchr ((void*)(S), (C))
|
||||
#include "str-two-way.h"
|
||||
|
||||
#undef strstr
|
||||
@@ -47,47 +35,128 @@
|
||||
#define STRSTR strstr
|
||||
#endif
|
||||
|
||||
/* Return the first occurrence of NEEDLE in HAYSTACK. Return HAYSTACK
|
||||
if NEEDLE is empty, otherwise NULL if NEEDLE is not found in
|
||||
HAYSTACK. */
|
||||
static inline char *
|
||||
strstr2 (const unsigned char *hs, const unsigned char *ne)
|
||||
{
|
||||
uint32_t h1 = (ne[0] << 16) | ne[1];
|
||||
uint32_t h2 = 0;
|
||||
for (int c = hs[0]; h1 != h2 && c != 0; c = *++hs)
|
||||
h2 = (h2 << 16) | c;
|
||||
return h1 == h2 ? (char *)hs - 2 : NULL;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
strstr3 (const unsigned char *hs, const unsigned char *ne)
|
||||
{
|
||||
uint32_t h1 = ((uint32_t)ne[0] << 24) | (ne[1] << 16) | (ne[2] << 8);
|
||||
uint32_t h2 = 0;
|
||||
for (int c = hs[0]; h1 != h2 && c != 0; c = *++hs)
|
||||
h2 = (h2 | c) << 8;
|
||||
return h1 == h2 ? (char *)hs - 3 : NULL;
|
||||
}
|
||||
|
||||
/* Hash character pairs so a small shift table can be used. All bits of
|
||||
p[0] are included, but not all bits from p[-1]. So if two equal hashes
|
||||
match on p[-1], p[0] matches too. Hash collisions are harmless and result
|
||||
in smaller shifts. */
|
||||
#define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift))
|
||||
|
||||
/* Fast strstr algorithm with guaranteed linear-time performance.
|
||||
Small needles up to size 3 use a dedicated linear search. Longer needles
|
||||
up to size 256 use a novel modified Horspool algorithm. It hashes pairs
|
||||
of characters to quickly skip past mismatches. The main search loop only
|
||||
exits if the last 2 characters match, avoiding unnecessary calls to memcmp
|
||||
and allowing for a larger skip if there is no match. A self-adapting
|
||||
filtering check is used to quickly detect mismatches in long needles.
|
||||
By limiting the needle length to 256, the shift table can be reduced to 8
|
||||
bits per entry, lowering preprocessing overhead and minimizing cache effects.
|
||||
The limit also implies worst-case performance is linear.
|
||||
Needles larger than 256 characters use the linear-time Two-Way algorithm. */
|
||||
char *
|
||||
STRSTR (const char *haystack, const char *needle)
|
||||
{
|
||||
size_t needle_len; /* Length of NEEDLE. */
|
||||
size_t haystack_len; /* Known minimum length of HAYSTACK. */
|
||||
const unsigned char *hs = (const unsigned char *) haystack;
|
||||
const unsigned char *ne = (const unsigned char *) needle;
|
||||
|
||||
/* Handle empty NEEDLE special case. */
|
||||
if (needle[0] == '\0')
|
||||
return (char *) haystack;
|
||||
/* Handle short needle special cases first. */
|
||||
if (ne[0] == '\0')
|
||||
return (char *)hs;
|
||||
hs = (const unsigned char *)strchr ((const char*)hs, ne[0]);
|
||||
if (hs == NULL || ne[1] == '\0')
|
||||
return (char*)hs;
|
||||
if (ne[2] == '\0')
|
||||
return strstr2 (hs, ne);
|
||||
if (ne[3] == '\0')
|
||||
return strstr3 (hs, ne);
|
||||
|
||||
/* Skip until we find the first matching char from NEEDLE. */
|
||||
haystack = strchr (haystack, needle[0]);
|
||||
if (haystack == NULL || needle[1] == '\0')
|
||||
return (char *) haystack;
|
||||
|
||||
/* Ensure HAYSTACK length is at least as long as NEEDLE length.
|
||||
Since a match may occur early on in a huge HAYSTACK, use strnlen
|
||||
/* Ensure haystack length is at least as long as needle length.
|
||||
Since a match may occur early on in a huge haystack, use strnlen
|
||||
and read ahead a few cachelines for improved performance. */
|
||||
needle_len = strlen (needle);
|
||||
haystack_len = __strnlen (haystack, needle_len + 256);
|
||||
if (haystack_len < needle_len)
|
||||
size_t ne_len = strlen ((const char*)ne);
|
||||
size_t hs_len = __strnlen ((const char*)hs, ne_len | 512);
|
||||
if (hs_len < ne_len)
|
||||
return NULL;
|
||||
|
||||
/* Check whether we have a match. This improves performance since we avoid
|
||||
the initialization overhead of the two-way algorithm. */
|
||||
if (memcmp (haystack, needle, needle_len) == 0)
|
||||
return (char *) haystack;
|
||||
/* Check whether we have a match. This improves performance since we
|
||||
avoid initialization overheads. */
|
||||
if (memcmp (hs, ne, ne_len) == 0)
|
||||
return (char *) hs;
|
||||
|
||||
/* Perform the search. Abstract memory is considered to be an array
|
||||
of 'unsigned char' values, not an array of 'char' values. See
|
||||
ISO C 99 section 6.2.6.1. */
|
||||
if (needle_len < LONG_NEEDLE_THRESHOLD)
|
||||
return two_way_short_needle ((const unsigned char *) haystack,
|
||||
haystack_len,
|
||||
(const unsigned char *) needle, needle_len);
|
||||
return two_way_long_needle ((const unsigned char *) haystack, haystack_len,
|
||||
(const unsigned char *) needle, needle_len);
|
||||
/* Use Two-Way algorithm for very long needles. */
|
||||
if (__glibc_unlikely (ne_len > 256))
|
||||
return two_way_long_needle (hs, hs_len, ne, ne_len);
|
||||
|
||||
const unsigned char *end = hs + hs_len - ne_len;
|
||||
uint8_t shift[256];
|
||||
size_t tmp, shift1;
|
||||
size_t m1 = ne_len - 1;
|
||||
size_t offset = 0;
|
||||
|
||||
/* Initialize bad character shift hash table. */
|
||||
memset (shift, 0, sizeof (shift));
|
||||
for (int i = 1; i < m1; i++)
|
||||
shift[hash2 (ne + i)] = i;
|
||||
/* Shift1 is the amount we can skip after matching the hash of the
|
||||
needle end but not the full needle. */
|
||||
shift1 = m1 - shift[hash2 (ne + m1)];
|
||||
shift[hash2 (ne + m1)] = m1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (__glibc_unlikely (hs > end))
|
||||
{
|
||||
end += __strnlen ((const char*)end + m1 + 1, 2048);
|
||||
if (hs > end)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Skip past character pairs not in the needle. */
|
||||
do
|
||||
{
|
||||
hs += m1;
|
||||
tmp = shift[hash2 (hs)];
|
||||
}
|
||||
while (tmp == 0 && hs <= end);
|
||||
|
||||
/* If the match is not at the end of the needle, shift to the end
|
||||
and continue until we match the hash of the needle end. */
|
||||
hs -= tmp;
|
||||
if (tmp < m1)
|
||||
continue;
|
||||
|
||||
/* Hash of the last 2 characters matches. If the needle is long,
|
||||
try to quickly filter out mismatches. */
|
||||
if (m1 < 15 || memcmp (hs + offset, ne + offset, 8) == 0)
|
||||
{
|
||||
if (memcmp (hs, ne, m1) == 0)
|
||||
return (void *) hs;
|
||||
|
||||
/* Adjust filter offset when it doesn't find the mismatch. */
|
||||
offset = (offset >= 8 ? offset : m1) - 8;
|
||||
}
|
||||
|
||||
/* Skip based on matching the hash of the needle end. */
|
||||
hs += shift1;
|
||||
}
|
||||
}
|
||||
libc_hidden_builtin_def (strstr)
|
||||
|
||||
#undef LONG_NEEDLE_THRESHOLD
|
||||
|
||||
+36
-3
@@ -65,8 +65,8 @@ do_one_test (impl_t *impl, const CHAR *s, int c, size_t n, CHAR *exp_res)
|
||||
CHAR *res = CALL (impl, s, c, n);
|
||||
if (res != exp_res)
|
||||
{
|
||||
error (0, 0, "Wrong result in function %s %p %p", impl->name,
|
||||
res, exp_res);
|
||||
error (0, 0, "Wrong result in function %s (%p, %d, %zu) -> %p != %p",
|
||||
impl->name, s, c, n, res, exp_res);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ do_test (size_t align, size_t pos, size_t len, size_t n, int seek_char)
|
||||
}
|
||||
buf[align + len] = 0;
|
||||
|
||||
if (pos < len)
|
||||
if (pos < MIN(n, len))
|
||||
{
|
||||
buf[align + pos] = seek_char;
|
||||
buf[align + len] = -seek_char;
|
||||
@@ -107,6 +107,38 @@ do_test (size_t align, size_t pos, size_t len, size_t n, int seek_char)
|
||||
do_one_test (impl, (CHAR *) (buf + align), seek_char, n, result);
|
||||
}
|
||||
|
||||
static void
|
||||
do_overflow_tests (void)
|
||||
{
|
||||
size_t i, j, len;
|
||||
const size_t one = 1;
|
||||
uintptr_t buf_addr = (uintptr_t) buf1;
|
||||
|
||||
for (i = 0; i < 750; ++i)
|
||||
{
|
||||
do_test (0, i, 751, SIZE_MAX - i, BIG_CHAR);
|
||||
do_test (0, i, 751, i - buf_addr, BIG_CHAR);
|
||||
do_test (0, i, 751, -buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, SIZE_MAX - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, SIZE_MAX - buf_addr + i, BIG_CHAR);
|
||||
|
||||
len = 0;
|
||||
for (j = 8 * sizeof(size_t) - 1; j ; --j)
|
||||
{
|
||||
len |= one << j;
|
||||
do_test (0, i, 751, len - i, BIG_CHAR);
|
||||
do_test (0, i, 751, len + i, BIG_CHAR);
|
||||
do_test (0, i, 751, len - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, len - buf_addr + i, BIG_CHAR);
|
||||
|
||||
do_test (0, i, 751, ~len - i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len + i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len - buf_addr + i, BIG_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_tests (void)
|
||||
{
|
||||
@@ -221,6 +253,7 @@ test_main (void)
|
||||
do_test (page_size / 2 - i, i, i, 1, 0x9B);
|
||||
|
||||
do_random_tests ();
|
||||
do_overflow_tests ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,66 @@ do_test (size_t align1, size_t align2, size_t len1, size_t len2,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_overflow_tests (void)
|
||||
{
|
||||
size_t i, j, len;
|
||||
const size_t one = 1;
|
||||
CHAR *s1, *s2;
|
||||
uintptr_t s1_addr;
|
||||
s1 = (CHAR *) buf1;
|
||||
s2 = (CHAR *) buf2;
|
||||
s1_addr = (uintptr_t)s1;
|
||||
for (j = 0; j < 200; ++j)
|
||||
s2[j] = 32 + 23 * j % (BIG_CHAR - 32);
|
||||
s2[200] = 0;
|
||||
for (i = 0; i < 750; ++i) {
|
||||
for (j = 0; j < i; ++j)
|
||||
s1[j] = 32 + 23 * j % (BIG_CHAR - 32);
|
||||
s1[i] = '\0';
|
||||
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
{
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, i - s1_addr);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, -s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - s1_addr + i);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (j = 8 * sizeof(size_t) - 1; j ; --j)
|
||||
{
|
||||
len |= one << j;
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
{
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len + i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - s1_addr + i);
|
||||
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len + i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - s1_addr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_tests (void)
|
||||
{
|
||||
@@ -316,6 +376,7 @@ test_main (void)
|
||||
}
|
||||
|
||||
do_random_tests ();
|
||||
do_overflow_tests ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user