mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 05:59:27 +08:00
Compare commits
97
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c453bb606 | ||
|
|
d407ace6e1 | ||
|
|
5e45e86fed | ||
|
|
a27376f0dc | ||
|
|
bc76f2aa9b | ||
|
|
552849c43c | ||
|
|
2ba6f4c063 | ||
|
|
0afd4d5feb | ||
|
|
87c2795cf6 | ||
|
|
67db60ee15 | ||
|
|
6ad255db1d | ||
|
|
893379d4ed | ||
|
|
2ea357280d | ||
|
|
8be3551ccb | ||
|
|
7414631f8a | ||
|
|
296fb7f4a2 | ||
|
|
cb4c624480 | ||
|
|
299e1d25c3 | ||
|
|
05dc6da0b4 | ||
|
|
509d819cea | ||
|
|
3c27e5170c | ||
|
|
f2f55eac9e | ||
|
|
5492954033 | ||
|
|
0be5a6a72a | ||
|
|
f6713070c6 | ||
|
|
f671746f6c | ||
|
|
748699d938 | ||
|
|
6cebb0b80f | ||
|
|
97926e9017 | ||
|
|
b866ef2977 | ||
|
|
4ebd33dd77 | ||
|
|
b4bca35ab9 | ||
|
|
3e87cf9be9 | ||
|
|
3ecfa68561 | ||
|
|
12feedaf67 | ||
|
|
f13c1bb0f9 | ||
|
|
a56a2943d2 | ||
|
|
68099ccc94 | ||
|
|
426378547e | ||
|
|
8e863fb1c9 | ||
|
|
1a19d5a507 | ||
|
|
9cd9c90544 | ||
|
|
ebd45473f5 | ||
|
|
937ef7aaf3 | ||
|
|
3989780591 | ||
|
|
912d89a766 | ||
|
|
cbf39c26b2 | ||
|
|
453e6b8dba | ||
|
|
b0ec8fb689 | ||
|
|
f122d0b4d1 | ||
|
|
a1d3294a5b | ||
|
|
8dfb84ad4e | ||
|
|
2a0873aa81 | ||
|
|
efdf4c0c87 | ||
|
|
b11411fe2e | ||
|
|
8aaf4b732d | ||
|
|
2dbf973fe0 | ||
|
|
6b2957cfe8 | ||
|
|
828b8d23f3 | ||
|
|
710d7a2e83 | ||
|
|
0c9430ed97 | ||
|
|
ec041b1f53 | ||
|
|
97297120ce | ||
|
|
17c3eab387 | ||
|
|
de1fe81f47 | ||
|
|
bf499c2a49 | ||
|
|
71874f167a | ||
|
|
256030b984 | ||
|
|
6de12fc9ad | ||
|
|
ab8c1b5d62 | ||
|
|
bf48b17a28 | ||
|
|
18fd689cdc | ||
|
|
46b4e37c9e | ||
|
|
1166170d95 | ||
|
|
e34453cd6a | ||
|
|
e87844ec42 | ||
|
|
7a8f3c6ee4 | ||
|
|
3970785beb | ||
|
|
d0f72b96f2 | ||
|
|
8dbaecbe92 | ||
|
|
d7274d718e | ||
|
|
a96a82c4a5 | ||
|
|
c491dabd8a | ||
|
|
8a726b6304 | ||
|
|
c5476b7907 | ||
|
|
e5754399b5 | ||
|
|
c090b0cb1c | ||
|
|
41a77b78cf | ||
|
|
9ec7a532ff | ||
|
|
9d5bf9c17d | ||
|
|
b38f3f60d5 | ||
|
|
5e298d2d93 | ||
|
|
2fadee5301 | ||
|
|
fd18059c0f | ||
|
|
bc13db7393 | ||
|
|
3ec4dd77f6 | ||
|
|
bdea6c3719 |
@@ -0,0 +1,2 @@
|
||||
For the GNU C Library Security Advisories, see the git master branch:
|
||||
https://sourceware.org/git/?p=glibc.git;a=tree;f=advisories;hb=HEAD
|
||||
@@ -5,6 +5,22 @@ 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.42.1
|
||||
|
||||
The following bugs were resolved with this release:
|
||||
|
||||
[31943] _dl_find_object can fail if ld.so contains gaps between load segments
|
||||
[32994] stdlib: resolve a double lock init issue after fork
|
||||
[33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables
|
||||
[33245] nptl: nptl: error in internal cancellation syscall handling
|
||||
[33356] nptl: creating thread stack with guardsize 0 can erroneously
|
||||
conclude MADV_GUARD_INSTALL is available
|
||||
[33361] nss: Group merge does not react to ERANGE during merge
|
||||
[33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return
|
||||
uninitialized memory
|
||||
[34507] nptl: Pretty-printer tests FAIL instead of UNSUPPORTED when
|
||||
cross-testing without python3 on target
|
||||
|
||||
Version 2.42
|
||||
|
||||
Major new features:
|
||||
|
||||
@@ -423,8 +423,9 @@ py-env := PYTHONPATH=$(py-const-dir):$(..)scripts:$${PYTHONPATH}
|
||||
# The pretty printer files and test_common_printers.py must be present for all.
|
||||
$(tests-printers-out): $(objpfx)%.out: $(objpfx)% %.py %.c $(pretty-printers) \
|
||||
$(..)scripts/test_printers_common.py
|
||||
$(test-wrapper-env) $(py-env) \
|
||||
$(PYTHON) $*.py $*.c $(objpfx)$* $(pretty-printers) > $@; \
|
||||
$(test-wrapper-env) $(py-env) sh -c \
|
||||
'command -v $(firstword $(PYTHON)) > /dev/null 2>&1 || exit 77; \
|
||||
exec $(PYTHON) $*.py $*.c $(objpfx)$* $(pretty-printers)' > $@; \
|
||||
$(evaluate-test)
|
||||
endif
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
printf: incorrect output for integers with thousands separator and width field
|
||||
|
||||
When the printf family of functions is called with a format specifier
|
||||
that uses an <apostrophe> (enable grouping) and a minimum width
|
||||
specifier, the resulting output could be larger than reasonably expected
|
||||
by a caller that computed a tight bound on the buffer size. The
|
||||
resulting larger than expected output could result in a buffer overflow
|
||||
in the printf family of functions.
|
||||
|
||||
CVE-Id: CVE-2023-25139
|
||||
Public-Date: 2023-02-02
|
||||
Vulnerable-Commit: e88b9f0e5cc50cab57a299dc7efe1a4eb385161d (2.37)
|
||||
Fix-Commit: c980549cc6a1c03c23cc2fe3e7b0fe626a0364b0 (2.38)
|
||||
Fix-Commit: 07b9521fc6369d000216b96562ff7c0ed32a16c4 (2.37-4)
|
||||
@@ -1,15 +0,0 @@
|
||||
getaddrinfo: Stack read overflow in no-aaaa mode
|
||||
|
||||
If the system is configured in no-aaaa mode via /etc/resolv.conf,
|
||||
getaddrinfo is called for the AF_UNSPEC address family, and a DNS
|
||||
response is received over TCP that is larger than 2048 bytes,
|
||||
getaddrinfo may potentially disclose stack contents via the returned
|
||||
address data, or crash.
|
||||
|
||||
CVE-Id: CVE-2023-4527
|
||||
Public-Date: 2023-09-12
|
||||
Vulnerable-Commit: f282cdbe7f436c75864e5640a409a10485e9abb2 (2.36)
|
||||
Fix-Commit: bd77dd7e73e3530203be1c52c8a29d08270cb25d (2.39)
|
||||
Fix-Commit: 4ea972b7edd7e36610e8cde18bf7a8149d7bac4f (2.36-113)
|
||||
Fix-Commit: b7529346025a130fee483d42178b5c118da971bb (2.37-38)
|
||||
Fix-Commit: b25508dd774b617f99419bdc3cf2ace4560cd2d6 (2.38-19)
|
||||
@@ -1,15 +0,0 @@
|
||||
getaddrinfo: Potential use-after-free
|
||||
|
||||
When an NSS plugin only implements the _gethostbyname2_r and
|
||||
_getcanonname_r callbacks, getaddrinfo could use memory that was freed
|
||||
during buffer resizing, potentially causing a crash or read or write to
|
||||
arbitrary memory.
|
||||
|
||||
CVE-Id: CVE-2023-4806
|
||||
Public-Date: 2023-09-12
|
||||
Fix-Commit: 973fe93a5675c42798b2161c6f29c01b0e243994 (2.39)
|
||||
Fix-Commit: e09ee267c03e3150c2c9ba28625ab130705a485e (2.34-420)
|
||||
Fix-Commit: e3ccb230a961b4797510e6a1f5f21fd9021853e7 (2.35-270)
|
||||
Fix-Commit: a9728f798ec7f05454c95637ee6581afaa9b487d (2.36-115)
|
||||
Fix-Commit: 6529a7466c935f36e9006b854d6f4e1d4876f942 (2.37-39)
|
||||
Fix-Commit: 00ae4f10b504bc4564e9f22f00907093f1ab9338 (2.38-20)
|
||||
@@ -1,16 +0,0 @@
|
||||
tunables: local privilege escalation through buffer overflow
|
||||
|
||||
If a tunable of the form NAME=NAME=VAL is passed in the environment of a
|
||||
setuid program and NAME is valid, it may result in a buffer overflow,
|
||||
which could be exploited to achieve escalated privileges. This flaw was
|
||||
introduced in glibc 2.34.
|
||||
|
||||
CVE-Id: CVE-2023-4911
|
||||
Public-Date: 2023-10-03
|
||||
Vulnerable-Commit: 2ed18c5b534d9e92fc006202a5af0df6b72e7aca (2.34)
|
||||
Fix-Commit: 1056e5b4c3f2d90ed2b4a55f96add28da2f4c8fa (2.39)
|
||||
Fix-Commit: dcc367f148bc92e7f3778a125f7a416b093964d9 (2.34-423)
|
||||
Fix-Commit: c84018a05aec80f5ee6f682db0da1130b0196aef (2.35-274)
|
||||
Fix-Commit: 22955ad85186ee05834e47e665056148ca07699c (2.36-118)
|
||||
Fix-Commit: b4e23c75aea756b4bddc4abcf27a1c6dca8b6bd3 (2.37-45)
|
||||
Fix-Commit: 750a45a783906a19591fb8ff6b7841470f1f5701 (2.38-27)
|
||||
@@ -1,18 +0,0 @@
|
||||
getaddrinfo: DoS due to memory leak
|
||||
|
||||
The fix for CVE-2023-4806 introduced a memory leak when an application
|
||||
calls getaddrinfo for AF_INET6 with AI_CANONNAME, AI_ALL and AI_V4MAPPED
|
||||
flags set.
|
||||
|
||||
CVE-Id: CVE-2023-5156
|
||||
Public-Date: 2023-09-25
|
||||
Vulnerable-Commit: e09ee267c03e3150c2c9ba28625ab130705a485e (2.34-420)
|
||||
Vulnerable-Commit: e3ccb230a961b4797510e6a1f5f21fd9021853e7 (2.35-270)
|
||||
Vulnerable-Commit: a9728f798ec7f05454c95637ee6581afaa9b487d (2.36-115)
|
||||
Vulnerable-Commit: 6529a7466c935f36e9006b854d6f4e1d4876f942 (2.37-39)
|
||||
Vulnerable-Commit: 00ae4f10b504bc4564e9f22f00907093f1ab9338 (2.38-20)
|
||||
Fix-Commit: 8006457ab7e1cd556b919f477348a96fe88f2e49 (2.34-421)
|
||||
Fix-Commit: 17092c0311f954e6f3c010f73ce3a78c24ac279a (2.35-272)
|
||||
Fix-Commit: 856bac55f98dc840e7c27cfa82262b933385de90 (2.36-116)
|
||||
Fix-Commit: 4473d1b87d04b25cdd0e0354814eeaa421328268 (2.37-42)
|
||||
Fix-Commit: 5ee59ca371b99984232d7584fe2b1a758b4421d3 (2.38-24)
|
||||
@@ -1,15 +0,0 @@
|
||||
syslog: Heap buffer overflow in __vsyslog_internal
|
||||
|
||||
__vsyslog_internal did not handle a case where printing a SYSLOG_HEADER
|
||||
containing a long program name failed to update the required buffer
|
||||
size, leading to the allocation and overflow of a too-small buffer on
|
||||
the heap.
|
||||
|
||||
CVE-Id: CVE-2023-6246
|
||||
Public-Date: 2024-01-30
|
||||
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
|
||||
Fix-Commit: 6bd0e4efcc78f3c0115e5ea9739a1642807450da (2.39)
|
||||
Fix-Commit: 23514c72b780f3da097ecf33a793b7ba9c2070d2 (2.38-42)
|
||||
Fix-Commit: 97a4292aa4a2642e251472b878d0ec4c46a0e59a (2.37-57)
|
||||
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
|
||||
Fix-Commit: d1a83b6767f68b3cb5b4b4ea2617254acd040c82 (2.36-126)
|
||||
@@ -1,15 +0,0 @@
|
||||
syslog: Heap buffer overflow in __vsyslog_internal
|
||||
|
||||
__vsyslog_internal used the return value of snprintf/vsnprintf to
|
||||
calculate buffer sizes for memory allocation. If these functions (for
|
||||
any reason) failed and returned -1, the resulting buffer would be too
|
||||
small to hold output.
|
||||
|
||||
CVE-Id: CVE-2023-6779
|
||||
Public-Date: 2024-01-30
|
||||
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
|
||||
Fix-Commit: 7e5a0c286da33159d47d0122007aac016f3e02cd (2.39)
|
||||
Fix-Commit: d0338312aace5bbfef85e03055e1212dd0e49578 (2.38-43)
|
||||
Fix-Commit: 67062eccd9a65d7fda9976a56aeaaf6c25a80214 (2.37-58)
|
||||
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
|
||||
Fix-Commit: 2bc9d7c002bdac38b5c2a3f11b78e309d7765b83 (2.36-127)
|
||||
@@ -1,13 +0,0 @@
|
||||
syslog: Integer overflow in __vsyslog_internal
|
||||
|
||||
__vsyslog_internal calculated a buffer size by adding two integers, but
|
||||
did not first check if the addition would overflow.
|
||||
|
||||
CVE-Id: CVE-2023-6780
|
||||
Public-Date: 2024-01-30
|
||||
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
|
||||
Fix-Commit: ddf542da94caf97ff43cc2875c88749880b7259b (2.39)
|
||||
Fix-Commit: d37c2b20a4787463d192b32041c3406c2bd91de0 (2.38-44)
|
||||
Fix-Commit: 2b58cba076e912961ceaa5fa58588e4b10f791c0 (2.37-59)
|
||||
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
|
||||
Fix-Commit: b9b7d6a27aa0632f334352fa400771115b3c69b7 (2.36-128)
|
||||
@@ -1,28 +0,0 @@
|
||||
ISO-2022-CN-EXT: fix out-of-bound writes when writing escape sequence
|
||||
|
||||
The iconv() function in the GNU C Library versions 2.39 and older may
|
||||
overflow the output buffer passed to it by up to 4 bytes when converting
|
||||
strings to the ISO-2022-CN-EXT character set, which may be used to
|
||||
crash an application or overwrite a neighbouring variable.
|
||||
|
||||
ISO-2022-CN-EXT uses escape sequences to indicate character set changes
|
||||
(as specified by RFC 1922). While the SOdesignation has the expected
|
||||
bounds checks, neither SS2designation nor SS3designation have its;
|
||||
allowing a write overflow of 1, 2, or 3 bytes with fixed values:
|
||||
'$+I', '$+J', '$+K', '$+L', '$+M', or '$*H'.
|
||||
|
||||
CVE-Id: CVE-2024-2961
|
||||
Public-Date: 2024-04-17
|
||||
Vulnerable-Commit: 755104edc75c53f4a0e7440334e944ad3c6b32fc (2.1.93-169)
|
||||
Fix-Commit: f9dc609e06b1136bb0408be9605ce7973a767ada (2.40)
|
||||
Fix-Commit: 31da30f23cddd36db29d5b6a1c7619361b271fb4 (2.39-31)
|
||||
Fix-Commit: e1135387deded5d73924f6ca20c72a35dc8e1bda (2.38-66)
|
||||
Fix-Commit: 89ce64b269a897a7780e4c73a7412016381c6ecf (2.37-89)
|
||||
Fix-Commit: 4ed98540a7fd19f458287e783ae59c41e64df7b5 (2.36-164)
|
||||
Fix-Commit: 36280d1ce5e245aabefb877fe4d3c6cff95dabfa (2.35-315)
|
||||
Fix-Commit: a8b0561db4b9847ebfbfec20075697d5492a363c (2.34-459)
|
||||
Fix-Commit: ed4f16ff6bed3037266f1fa682ebd32a18fce29c (2.33-263)
|
||||
Fix-Commit: 682ad4c8623e611a971839990ceef00346289cc9 (2.32-140)
|
||||
Fix-Commit: 3703c32a8d304c1ee12126134ce69be965f38000 (2.31-154)
|
||||
|
||||
Reported-By: Charles Fol
|
||||
@@ -1,22 +0,0 @@
|
||||
nscd: Stack-based buffer overflow in netgroup cache
|
||||
|
||||
If the Name Service Cache Daemon's (nscd) fixed size cache is exhausted
|
||||
by client requests then a subsequent client request for netgroup data
|
||||
may result in a stack-based buffer overflow. This flaw was introduced
|
||||
in glibc 2.15 when the cache was added to nscd.
|
||||
|
||||
This vulnerability is only present in the nscd binary.
|
||||
|
||||
CVE-Id: CVE-2024-33599
|
||||
Public-Date: 2024-04-23
|
||||
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
|
||||
Fix-Commit: 69c58d5ef9f584ea198bd00f7964d364d0e6b921 (2.31-155)
|
||||
Fix-Commit: a77064893bfe8a701770e2f53a4d33805bc47a5a (2.32-141)
|
||||
Fix-Commit: 5c75001a96abcd50cbdb74df24c3f013188d076e (2.33-264)
|
||||
Fix-Commit: 52f73e5c4e29b14e79167272297977f360ae1e97 (2.34-460)
|
||||
Fix-Commit: 7a95873543ce225376faf13bb71c43dea6d24f86 (2.35-316)
|
||||
Fix-Commit: caa3151ca460bdd9330adeedd68c3112d97bffe4 (2.36-165)
|
||||
Fix-Commit: f75c298e747b2b8b41b1c2f551c011a52c41bfd1 (2.37-91)
|
||||
Fix-Commit: 5968aebb86164034b8f8421b4abab2f837a5bdaf (2.38-72)
|
||||
Fix-Commit: 1263d583d2e28afb8be53f8d6922f0842036f35d (2.39-35)
|
||||
Fix-Commit: 87801a8fd06db1d654eea3e4f7626ff476a9bdaa (2.40)
|
||||
@@ -1,32 +0,0 @@
|
||||
nscd: Null pointer crash after notfound response
|
||||
|
||||
If the Name Service Cache Daemon's (nscd) cache fails to add a not-found
|
||||
netgroup response to the cache, the client request can result in a null
|
||||
pointer dereference. This flaw was introduced in glibc 2.15 when the
|
||||
cache was added to nscd.
|
||||
|
||||
This vulnerability is only present in the nscd binary.
|
||||
|
||||
CVE-Id: CVE-2024-33600
|
||||
Public-Date: 2024-04-24
|
||||
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
|
||||
Fix-Commit: b048a482f088e53144d26a61c390bed0210f49f2 (2.40)
|
||||
Fix-Commit: 7835b00dbce53c3c87bbbb1754a95fb5e58187aa (2.40)
|
||||
Fix-Commit: c99f886de54446cd4447db6b44be93dabbdc2f8b (2.39-37)
|
||||
Fix-Commit: 5a508e0b508c8ad53bd0d2fb48fd71b242626341 (2.39-36)
|
||||
Fix-Commit: 2ae9446c1b7a3064743b4a51c0bbae668ee43e4c (2.38-74)
|
||||
Fix-Commit: 541ea5172aa658c4bd5c6c6d6fd13903c3d5bb0a (2.38-73)
|
||||
Fix-Commit: a8070b31043c7585c36ba68a74298c4f7af075c3 (2.37-93)
|
||||
Fix-Commit: 5eea50c4402e39588de98aa1d4469a79774703d4 (2.37-92)
|
||||
Fix-Commit: f205b3af56740e3b014915b1bd3b162afe3407ef (2.36-167)
|
||||
Fix-Commit: c34f470a615b136170abd16142da5dd0c024f7d1 (2.36-166)
|
||||
Fix-Commit: bafadc589fbe21ae330e8c2af74db9da44a17660 (2.35-318)
|
||||
Fix-Commit: 4370bef52b0f3f3652c6aa13d7a9bb3ac079746d (2.35-317)
|
||||
Fix-Commit: 1f94122289a9bf7dba573f5d60327aaa2b85cf2e (2.34-462)
|
||||
Fix-Commit: 966d6ac9e40222b84bb21674cc4f83c8d72a5a26 (2.34-461)
|
||||
Fix-Commit: e3eef1b8fbdd3a7917af466ca9c4b7477251ca79 (2.33-266)
|
||||
Fix-Commit: f20a8d696b13c6261b52a6434899121f8b19d5a7 (2.33-265)
|
||||
Fix-Commit: be602180146de37582a3da3a0caa4b719645de9c (2.32-143)
|
||||
Fix-Commit: 394eae338199078b7961b051c191539870742d7b (2.32-142)
|
||||
Fix-Commit: 8d7949183760170c61e55def723c1d8050187874 (2.31-157)
|
||||
Fix-Commit: 304ce5fe466c4762b21b36c26926a4657b59b53e (2.31-156)
|
||||
@@ -1,28 +0,0 @@
|
||||
nscd: netgroup cache may terminate daemon on memory allocation failure
|
||||
|
||||
The Name Service Cache Daemon's (nscd) netgroup cache uses xmalloc or
|
||||
xrealloc and these functions may terminate the process due to a memory
|
||||
allocation failure resulting in a denial of service to the clients. The
|
||||
flaw was introduced in glibc 2.15 when the cache was added to nscd.
|
||||
|
||||
This vulnerability is only present in the nscd binary.
|
||||
|
||||
Subsequent refactoring of the netgroup cache only added more uses of
|
||||
xmalloc and xrealloc. Uses of xmalloc and xrealloc in other parts of
|
||||
nscd only occur during startup of the daemon and so are not affected by
|
||||
client requests that could trigger an out of memory followed by
|
||||
termination.
|
||||
|
||||
CVE-Id: CVE-2024-33601
|
||||
Public-Date: 2024-04-24
|
||||
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
|
||||
Fix-Commit: c04a21e050d64a1193a6daab872bca2528bda44b (2.40)
|
||||
Fix-Commit: a9a8d3eebb145779a18d90e3966009a1daa63cd8 (2.39-38)
|
||||
Fix-Commit: 71af8ca864345d39b746d5cee84b94b430fad5db (2.38-75)
|
||||
Fix-Commit: 6e106dc214d6a033a4e945d1c6cf58061f1c5f1f (2.37-94)
|
||||
Fix-Commit: b6742463694b1dfdd5120b91ee21cf05d15ec2e2 (2.36-168)
|
||||
Fix-Commit: 7a5864cac60e06000394128a5a2817b03542f5a3 (2.35-319)
|
||||
Fix-Commit: 86f1d5f4129c373ac6fb6df5bcf38273838843cb (2.34-463)
|
||||
Fix-Commit: 4d27d4b9a188786fc6a56745506cec2acfc51f83 (2.33-267)
|
||||
Fix-Commit: 3ed195a8ec89da281e3c4bf887a13d281b72d8f4 (2.32-144)
|
||||
Fix-Commit: bbf5a58ccb55679217f94de706164d15372fbbc0 (2.31-158)
|
||||
@@ -1,26 +0,0 @@
|
||||
nscd: netgroup cache assumes NSS callback uses in-buffer strings
|
||||
|
||||
The Name Service Cache Daemon's (nscd) netgroup cache can corrupt memory
|
||||
when the NSS callback does not store all strings in the provided buffer.
|
||||
The flaw was introduced in glibc 2.15 when the cache was added to nscd.
|
||||
|
||||
This vulnerability is only present in the nscd binary.
|
||||
|
||||
There is no guarantee from the NSS callback API that the returned
|
||||
strings are all within the buffer. However, the netgroup cache code
|
||||
assumes that the NSS callback uses in-buffer strings and if it doesn't
|
||||
the buffer resizing logic could lead to potential memory corruption.
|
||||
|
||||
CVE-Id: CVE-2024-33602
|
||||
Public-Date: 2024-04-24
|
||||
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
|
||||
Fix-Commit: c04a21e050d64a1193a6daab872bca2528bda44b (2.40)
|
||||
Fix-Commit: a9a8d3eebb145779a18d90e3966009a1daa63cd8 (2.39-38)
|
||||
Fix-Commit: 71af8ca864345d39b746d5cee84b94b430fad5db (2.38-75)
|
||||
Fix-Commit: 6e106dc214d6a033a4e945d1c6cf58061f1c5f1f (2.37-94)
|
||||
Fix-Commit: b6742463694b1dfdd5120b91ee21cf05d15ec2e2 (2.36-168)
|
||||
Fix-Commit: 7a5864cac60e06000394128a5a2817b03542f5a3 (2.35-319)
|
||||
Fix-Commit: 86f1d5f4129c373ac6fb6df5bcf38273838843cb (2.34-463)
|
||||
Fix-Commit: 4d27d4b9a188786fc6a56745506cec2acfc51f83 (2.33-267)
|
||||
Fix-Commit: 3ed195a8ec89da281e3c4bf887a13d281b72d8f4 (2.32-144)
|
||||
Fix-Commit: bbf5a58ccb55679217f94de706164d15372fbbc0 (2.31-158)
|
||||
@@ -1,40 +0,0 @@
|
||||
assert: Buffer overflow when printing assertion failure message
|
||||
|
||||
When the assert() function fails, it does not allocate enough space for the
|
||||
assertion failure message string and size information, which may lead to a
|
||||
buffer overflow if the message string size aligns to page size.
|
||||
|
||||
This bug can be triggered when an assertion in a program fails. The assertion
|
||||
failure message is allocated to allow developers to see this failure in core
|
||||
dumps and it typically includes, in addition to the invariant assertion
|
||||
string and function name, the name of the program. If the name of the failing
|
||||
program is user controlled, for example on a local system, this could allow an
|
||||
attacker to control the assertion failure to trigger this buffer overflow.
|
||||
|
||||
The only viable vector for exploitation of this bug is local, if a setuid
|
||||
program exists that has an existing bug that results in an assertion failure.
|
||||
No such program has been discovered at the time of publishing this advisory,
|
||||
but the presence of custom setuid programs, although strongly discouraged as a
|
||||
security practice, cannot be discounted.
|
||||
|
||||
CVE-Id: CVE-2025-0395
|
||||
Public-Date: 2025-01-22
|
||||
Vulnerable-Commit: f8a3b5bf8fa1d0c43d2458e03cc109a04fdef194 (2.13-175)
|
||||
Fix-Commit: 68ee0f704cb81e9ad0a78c644a83e1e9cd2ee578 (2.41)
|
||||
Fix-Commit: cdb9ba84191ce72e86346fb8b1d906e7cd930ea2 (2.42)
|
||||
Fix-Commit: 69fda28279b497bd405fdd442a6d8e4d3d5f681b (2.41-7)
|
||||
Fix-Commit: 7d4b6bcae91f29d7b4daf15bab06b66cf1d2217c (2.40-66)
|
||||
Fix-Commit: d6c156c326999f144cb5b73d29982108d549ad8a (2.40-71)
|
||||
Fix-Commit: 808a84a8b81468b517a4d721fdc62069cb8c211f (2.39-146)
|
||||
Fix-Commit: f6d48470aef9264d2d56f4c4533eb76db7f9c2e4 (2.39-150)
|
||||
Fix-Commit: c32fd59314c343db88c3ea4a203870481d33c3d2 (2.38-122)
|
||||
Fix-Commit: f984e2d7e8299726891a1a497a3c36cd5542a0bf (2.38-124)
|
||||
Fix-Commit: a3d7865b098a3a67c44f7812208d9ce4718873ba (2.37-143)
|
||||
Fix-Commit: b989519fe1683c204ac24ec92830e3fe3bfaccad (2.37-146)
|
||||
Fix-Commit: 7971add7ee4171fdd8dfd17e7c04c4ed77a18845 (2.36-216)
|
||||
Fix-Commit: 0487893d5c5bc6710d83d7c3152d888a0339559e (2.36-219)
|
||||
Fix-Commit: 8b5d4be762419c4f6176261c6fea40ac559b88dc (2.35-370)
|
||||
Fix-Commit: 8b3d09dc0d350191985f9d291cc30ce96f034b49 (2.35-373)
|
||||
Fix-Commit: df4e1f4a5096b385c9bcc94424cf2eaa227b3761 (2.34-500)
|
||||
Fix-Commit: 31eb872cb21449832ab47ad5db83281d240e1d03 (2.34-503)
|
||||
Reported-By: Qualys Security Advisory
|
||||
@@ -1,23 +0,0 @@
|
||||
elf: static setuid binary dlopen may incorrectly search LD_LIBRARY_PATH
|
||||
|
||||
A statically linked setuid binary that calls dlopen (including internal
|
||||
dlopen calls after setlocale or calls to NSS functions such as getaddrinfo)
|
||||
may incorrectly search LD_LIBRARY_PATH to determine which library to load,
|
||||
leading to the execution of library code that is attacker controlled.
|
||||
|
||||
The only viable vector for exploitation of this bug is local, if a static
|
||||
setuid program exists, and that program calls dlopen, then it may search
|
||||
LD_LIBRARY_PATH to locate the SONAME to load. No such program has been
|
||||
discovered at the time of publishing this advisory, but the presence of
|
||||
custom setuid programs, although strongly discouraged as a security
|
||||
practice, cannot be discounted.
|
||||
|
||||
CVE-Id: CVE-2025-4802
|
||||
Public-Date: 2025-05-16
|
||||
Vulnerable-Commit: 10e93d968716ab82931d593bada121c17c0a4b93 (2.27)
|
||||
Fix-Commit: 5451fa962cd0a90a0e2ec1d8910a559ace02bba0 (2.39)
|
||||
Fix-Commit: 3be3728df2f1912c80abd3288bc6e3a25ad679e4 (2.38-132)
|
||||
Fix-Commit: 7403ede2d7752e59e0c47d5d33d73c2bf850e7be (2.37-154)
|
||||
Fix-Commit: 2ef7850279b2931caf6d6d6743ebaa91839e1cf7 (2.36-227)
|
||||
Fix-Commit: 621c65ccf12ddd415ceeb2234423bd1acd0fabb3 (2.35-387)
|
||||
Fix-Commit: 35018c0fd20eac9ceaf60060fed2745b3177359d (2.34-517)
|
||||
@@ -1,30 +0,0 @@
|
||||
power10: strcmp fails to save and restore nonvolatile vector registers
|
||||
|
||||
The Power 10 implementation of strcmp in
|
||||
sysdeps/powerpc/powerpc64/le/power10/strcmp.S failed to save/restore
|
||||
nonvolatile vector registers in the 32-byte aligned loop path. This
|
||||
results in callers reading content from those registers in a different
|
||||
context, potentially altering program logic.
|
||||
|
||||
There could be a program context where a user controlled string could
|
||||
leak through strcmp into program code, thus altering its logic. There
|
||||
is also a potential for sensitive strings passed into strcmp leaking
|
||||
through the clobbered registers into parts of the calling program that
|
||||
should otherwise not have had access to those strings.
|
||||
|
||||
The impact of this flaw is limited to applications running on Power 10
|
||||
hardware that use the nonvolatile vector registers, i.e. v20 to v31
|
||||
assuming that they have been treated in accordance with the OpenPower
|
||||
psABI. It is possible to work around the issue for those specific
|
||||
applications by setting the glibc.cpu.hwcaps tunable to "-arch_3_1" like
|
||||
so:
|
||||
|
||||
export GLIBC_TUNABLES=glibc.cpu.hwcaps=-arch_3_1
|
||||
|
||||
CVE-Id: CVE-2025-5702
|
||||
Public-Date: 2025-06-04
|
||||
Vulnerable-Commit: 3367d8e180848030d1646f088759f02b8dfe0d6f (2.39)
|
||||
Fix-Commit: 15808c77b35319e67ee0dc8f984a9a1a434701bc (2.42)
|
||||
Fix-Commit: 0c76c951620f9e12df2a89b2c684878b55bb6795 (2.41-60)
|
||||
Fix-Commit: 7e12550b8e3a11764a4a9090ce6bd3fc23fc8a8e (2.40-139)
|
||||
Fix-Commit: 06a70769fd0b2e1f2a3085ad50ab620282bd77b3 (2.39-209)
|
||||
@@ -1,29 +0,0 @@
|
||||
power10: strncmp fails to save and restore nonvolatile vector registers
|
||||
|
||||
The Power 10 implementation of strncmp in
|
||||
sysdeps/powerpc/powerpc64/le/power10/strncmp.S failed to save/restore
|
||||
nonvolatile vector registers in the 32-byte aligned loop path. This
|
||||
results in callers reading content from those registers in a different
|
||||
context, potentially altering program logic.
|
||||
|
||||
There could be a program context where a user controlled string could
|
||||
leak through strncmp into program code, thus altering its logic. There
|
||||
is also a potential for sensitive strings passed into strncmp leaking
|
||||
through the clobbered registers into parts of the calling program that
|
||||
should otherwise not have had access to those strings.
|
||||
|
||||
The impact of this flaw is limited to applications running on Power 10
|
||||
hardware that use the nonvolatile vector registers, i.e. v20 to v31
|
||||
assuming that they have been treated in accordance with the OpenPower
|
||||
psABI. It is possible to work around the issue for those specific
|
||||
applications by setting the glibc.cpu.hwcaps tunable to "-arch_3_1" like
|
||||
so:
|
||||
|
||||
export GLIBC_TUNABLES=glibc.cpu.hwcaps=-arch_3_1
|
||||
|
||||
CVE-Id: CVE-2025-5745
|
||||
Public-Date: 2025-06-05
|
||||
Vulnerable-Commit: 23f0d81608d0ca6379894ef81670cf30af7fd081 (2.40)
|
||||
Fix-Commit: 63c60101ce7c5eac42be90f698ba02099b41b965 (2.42)
|
||||
Fix-Commit: 84bdbf8a6f2fdafd3661489dbb7f79835a52da82 (2.41-57)
|
||||
Fix-Commit: 42a5a940c974d02540c8da26d6374c744d148cb9 (2.40-136)
|
||||
@@ -1,14 +0,0 @@
|
||||
posix: Fix double-free after allocation failure in regcomp
|
||||
|
||||
The regcomp function in the GNU C library version from 2.4 to 2.41 is
|
||||
subject to a double free if some previous allocation fails. It can be
|
||||
accomplished either by a malloc failure or by using an interposed
|
||||
malloc that injects random malloc failures. The double free can allow
|
||||
buffer manipulation depending of how the regex is constructed.
|
||||
This issue affects all architectures and ABIs supported by the GNU C
|
||||
library.
|
||||
|
||||
CVE-Id: CVE-2025-8058
|
||||
Public-Date: 2025-07-22
|
||||
Vulnerable-Commit: 963d8d782fc98fb6dc3a66f0068795f9920c269d (2.3.3-1596)
|
||||
Fix-Commit: 7ea06e994093fa0bcca0d0ee2c1db271d8d7885d (2.42)
|
||||
@@ -1,77 +0,0 @@
|
||||
GNU C Library Security Advisory Format
|
||||
======================================
|
||||
|
||||
Security advisories in this directory follow a simple git commit log
|
||||
format, with a heading and free-format description augmented with tags
|
||||
to allow parsing key information. References to code changes are
|
||||
specific to the glibc repository and follow a specific format:
|
||||
|
||||
Tag-name: <commit-ref> (release-version)
|
||||
|
||||
The <commit-ref> indicates a specific commit in the repository. The
|
||||
release-version indicates the publicly consumable release in which this
|
||||
commit is known to exist. The release-version is derived from the
|
||||
git-describe format, (i.e. stripped out from glibc-2.34.NNN-gxxxx) and
|
||||
is of the form 2.34-NNN. If the -NNN suffix is absent, it means that
|
||||
the change is in that release tarball, otherwise the change is on the
|
||||
release/2.YY/master branch and not in any released tarball.
|
||||
|
||||
The following tags are currently being used:
|
||||
|
||||
CVE-Id:
|
||||
This is the CVE-Id assigned under the CVE Program
|
||||
(https://www.cve.org/).
|
||||
|
||||
Public-Date:
|
||||
The date this issue became publicly known.
|
||||
|
||||
Vulnerable-Commit:
|
||||
The commit that introduced this vulnerability. There could be multiple
|
||||
entries, one for each release branch in the glibc repository; the
|
||||
release-version portion of this tag should tell you which branch this is
|
||||
on.
|
||||
|
||||
Fix-Commit:
|
||||
The commit that fixed this vulnerability. There could be multiple
|
||||
entries for each release branch in the glibc repository, indicating that
|
||||
all of those commits contributed to fixing that issue in each of those
|
||||
branches.
|
||||
|
||||
Reported-By:
|
||||
The entity that reported this issue. There could be multiple entries, one for
|
||||
each reporter.
|
||||
|
||||
Adding an Advisory
|
||||
------------------
|
||||
|
||||
An advisory for a CVE needs to be added on the master branch in two steps:
|
||||
|
||||
1. Add the text of the advisory without any Fix-Commit tags along with
|
||||
the fix for the CVE. Add the Vulnerable-Commit tag, if applicable.
|
||||
The advisories directory does not exist in release branches, so keep
|
||||
the advisory text commit distinct from the code changes, to ease
|
||||
backports. Ask for the GLIBC-SA advisory number from the security
|
||||
team.
|
||||
|
||||
2. Finish all backports on release branches and then back on the msater
|
||||
branch, add all commit refs to the advisory using the Fix-Commit
|
||||
tags. Don't bother adding the release-version subscript since the
|
||||
next step will overwrite it.
|
||||
|
||||
3. Run the process-advisories.sh script in the scripts directory on the
|
||||
advisory:
|
||||
|
||||
scripts/process-advisories.sh update GLIBC-SA-YYYY-NNNN
|
||||
|
||||
(replace YYYY-NNNN with the actual advisory number).
|
||||
|
||||
4. Verify the updated advisory and push the result.
|
||||
|
||||
Getting a NEWS snippet from advisories
|
||||
--------------------------------------
|
||||
|
||||
Run:
|
||||
|
||||
scripts/process-advisories.sh news
|
||||
|
||||
and copy the content into the NEWS file.
|
||||
+3
-3
@@ -24,11 +24,11 @@
|
||||
__ctype_init before user code runs, but this does not happen for
|
||||
threads in secondary namespaces. With the initializers, secondary
|
||||
namespaces at least get locale data from the C locale. */
|
||||
__thread const uint16_t * __libc_tsd_CTYPE_B
|
||||
__thread const uint16_t * __libc_tsd_CTYPE_B attribute_tls_model_ie
|
||||
= (const uint16_t *) _nl_C_LC_CTYPE_class + 128;
|
||||
__thread const int32_t * __libc_tsd_CTYPE_TOLOWER
|
||||
__thread const int32_t * __libc_tsd_CTYPE_TOLOWER attribute_tls_model_ie
|
||||
= (const int32_t *) _nl_C_LC_CTYPE_tolower + 128;
|
||||
__thread const int32_t * __libc_tsd_CTYPE_TOUPPER
|
||||
__thread const int32_t * __libc_tsd_CTYPE_TOUPPER attribute_tls_model_ie
|
||||
= (const int32_t *) _nl_C_LC_CTYPE_toupper + 128;
|
||||
|
||||
|
||||
|
||||
@@ -543,6 +543,8 @@ tests-internal += \
|
||||
tst-dl_find_object-threads \
|
||||
tst-dlmopen2 \
|
||||
tst-hash-collision3 \
|
||||
tst-link-map-contiguous-ldso \
|
||||
tst-link-map-contiguous-libc \
|
||||
tst-ptrguard1 \
|
||||
tst-stackguard1 \
|
||||
tst-tls-surplus \
|
||||
@@ -554,6 +556,10 @@ tests-internal += \
|
||||
unload2 \
|
||||
# tests-internal
|
||||
|
||||
ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
tests-internal += tst-link-map-contiguous-main
|
||||
endif
|
||||
|
||||
tests-container += \
|
||||
tst-dlopen-self-container \
|
||||
tst-dlopen-tlsmodid-container \
|
||||
|
||||
+45
-29
@@ -465,6 +465,37 @@ _dl_find_object (void *pc1, struct dl_find_object *result)
|
||||
}
|
||||
rtld_hidden_def (_dl_find_object)
|
||||
|
||||
/* Subroutine of _dlfo_process_initial to split out noncontigous link
|
||||
maps. NODELETE is the number of used _dlfo_nodelete_mappings
|
||||
elements. It is incremented as needed, and the new NODELETE value
|
||||
is returned. */
|
||||
static size_t
|
||||
_dlfo_process_initial_noncontiguous_map (struct link_map *map,
|
||||
size_t nodelete)
|
||||
{
|
||||
struct dl_find_object_internal dlfo;
|
||||
_dl_find_object_from_map (map, &dlfo);
|
||||
|
||||
/* PT_LOAD segments for a non-contiguous link map are added to the
|
||||
non-closeable mappings. */
|
||||
const ElfW(Phdr) *ph = map->l_phdr;
|
||||
const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum;
|
||||
for (; ph < ph_end; ++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
{
|
||||
/* Second pass only. */
|
||||
_dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
ElfW(Addr) start = ph->p_vaddr + map->l_addr;
|
||||
_dlfo_nodelete_mappings[nodelete].map_start = start;
|
||||
_dlfo_nodelete_mappings[nodelete].map_end = start + ph->p_memsz;
|
||||
}
|
||||
++nodelete;
|
||||
}
|
||||
return nodelete;
|
||||
}
|
||||
|
||||
/* _dlfo_process_initial is called twice. First to compute the array
|
||||
sizes from the initial loaded mappings. Second to fill in the
|
||||
bases and infos arrays with the (still unsorted) data. Returns the
|
||||
@@ -476,29 +507,8 @@ _dlfo_process_initial (void)
|
||||
|
||||
size_t nodelete = 0;
|
||||
if (!main_map->l_contiguous)
|
||||
{
|
||||
struct dl_find_object_internal dlfo;
|
||||
_dl_find_object_from_map (main_map, &dlfo);
|
||||
|
||||
/* PT_LOAD segments for a non-contiguous are added to the
|
||||
non-closeable mappings. */
|
||||
for (const ElfW(Phdr) *ph = main_map->l_phdr,
|
||||
*ph_end = main_map->l_phdr + main_map->l_phnum;
|
||||
ph < ph_end; ++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
{
|
||||
/* Second pass only. */
|
||||
_dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
_dlfo_nodelete_mappings[nodelete].map_start
|
||||
= ph->p_vaddr + main_map->l_addr;
|
||||
_dlfo_nodelete_mappings[nodelete].map_end
|
||||
= _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz;
|
||||
}
|
||||
++nodelete;
|
||||
}
|
||||
}
|
||||
/* Contiguous case already handled in _dl_find_object_init. */
|
||||
nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete);
|
||||
|
||||
size_t loaded = 0;
|
||||
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
|
||||
@@ -510,11 +520,18 @@ _dlfo_process_initial (void)
|
||||
/* lt_library link maps are implicitly NODELETE. */
|
||||
if (l->l_type == lt_library || l->l_nodelete_active)
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, _dlfo_nodelete_mappings + nodelete);
|
||||
++nodelete;
|
||||
/* The kernel may have loaded ld.so with gaps. */
|
||||
if (!l->l_contiguous && is_rtld_link_map (l))
|
||||
nodelete
|
||||
= _dlfo_process_initial_noncontiguous_map (l, nodelete);
|
||||
else
|
||||
{
|
||||
if (_dlfo_nodelete_mappings != NULL)
|
||||
/* Second pass only. */
|
||||
_dl_find_object_from_map
|
||||
(l, _dlfo_nodelete_mappings + nodelete);
|
||||
++nodelete;
|
||||
}
|
||||
}
|
||||
else if (l->l_type == lt_loaded)
|
||||
{
|
||||
@@ -764,7 +781,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count)
|
||||
/* Prefer newly loaded link map. */
|
||||
assert (loaded_index1 > 0);
|
||||
_dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo);
|
||||
loaded[loaded_index1 - 1]->l_find_object_processed = 1;
|
||||
--loaded_index1;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ _dl_find_object_to_external (struct dl_find_object_internal *internal,
|
||||
}
|
||||
|
||||
/* Extract the object location data from a link map and writes it to
|
||||
*RESULT using relaxed MO stores. */
|
||||
*RESULT using relaxed MO stores. Set L->l_find_object_processed. */
|
||||
static void __attribute__ ((unused))
|
||||
_dl_find_object_from_map (struct link_map *l,
|
||||
struct dl_find_object_internal *result)
|
||||
@@ -141,8 +141,11 @@ _dl_find_object_from_map (struct link_map *l,
|
||||
break;
|
||||
}
|
||||
if (read_seg == 3)
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
l->l_find_object_processed = 1;
|
||||
}
|
||||
|
||||
/* Called by the dynamic linker to set up the data structures for the
|
||||
|
||||
+55
-27
@@ -1239,6 +1239,60 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
return has_interp;
|
||||
}
|
||||
|
||||
/* Set up the program header information for the dynamic linker
|
||||
itself. It can be accessed via _r_debug and dl_iterate_phdr
|
||||
callbacks, and it is used by _dl_find_object. */
|
||||
static void
|
||||
rtld_setup_phdr (void)
|
||||
{
|
||||
/* Starting from binutils-2.23, the linker will define the magic
|
||||
symbol __ehdr_start to point to our own ELF header if it is
|
||||
visible in a segment that also includes the phdrs. */
|
||||
|
||||
const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start;
|
||||
assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
|
||||
const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
|
||||
_dl_rtld_map.l_phdr = rtld_phdr;
|
||||
_dl_rtld_map.l_phnum = rtld_ehdr->e_phnum;
|
||||
|
||||
|
||||
_dl_rtld_map.l_contiguous = 1;
|
||||
/* The linker may not have produced a contiguous object. The kernel
|
||||
will load the object with actual gaps (unlike the glibc loader
|
||||
for shared objects, which always produces a contiguous mapping).
|
||||
See similar logic in rtld_setup_main_map above. */
|
||||
{
|
||||
ElfW(Addr) expected_load_address = 0;
|
||||
for (const ElfW(Phdr) *ph = rtld_phdr; ph < &rtld_phdr[rtld_ehdr->e_phnum];
|
||||
++ph)
|
||||
if (ph->p_type == PT_LOAD)
|
||||
{
|
||||
ElfW(Addr) mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
|
||||
if (_dl_rtld_map.l_contiguous && expected_load_address != 0
|
||||
&& expected_load_address != mapstart)
|
||||
_dl_rtld_map.l_contiguous = 0;
|
||||
ElfW(Addr) allocend = ph->p_vaddr + ph->p_memsz;
|
||||
/* The next expected address is the page following this load
|
||||
segment. */
|
||||
expected_load_address = ((allocend + GLRO(dl_pagesize) - 1)
|
||||
& ~(GLRO(dl_pagesize) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* PT_GNU_RELRO is usually the last phdr. */
|
||||
size_t cnt = rtld_ehdr->e_phnum;
|
||||
while (cnt-- > 0)
|
||||
if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
{
|
||||
_dl_rtld_map.l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
_dl_rtld_map.l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjusts the contents of the stack and related globals for the user
|
||||
entry point. The ld.so processed skip_args arguments and bumped
|
||||
_dl_argv and _dl_argc accordingly. Those arguments are removed from
|
||||
@@ -1705,33 +1759,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
++GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
|
||||
++GL(dl_load_adds);
|
||||
|
||||
/* Starting from binutils-2.23, the linker will define the magic symbol
|
||||
__ehdr_start to point to our own ELF header if it is visible in a
|
||||
segment that also includes the phdrs. If that's not available, we use
|
||||
the old method that assumes the beginning of the file is part of the
|
||||
lowest-addressed PT_LOAD segment. */
|
||||
|
||||
/* Set up the program header information for the dynamic linker
|
||||
itself. It is needed in the dl_iterate_phdr callbacks. */
|
||||
const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start;
|
||||
assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
|
||||
const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
|
||||
_dl_rtld_map.l_phdr = rtld_phdr;
|
||||
_dl_rtld_map.l_phnum = rtld_ehdr->e_phnum;
|
||||
|
||||
|
||||
/* PT_GNU_RELRO is usually the last phdr. */
|
||||
size_t cnt = rtld_ehdr->e_phnum;
|
||||
while (cnt-- > 0)
|
||||
if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
{
|
||||
_dl_rtld_map.l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
_dl_rtld_map.l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
break;
|
||||
}
|
||||
rtld_setup_phdr ();
|
||||
|
||||
/* Add the dynamic linker to the TLS list if it also uses TLS. */
|
||||
if (_dl_rtld_map.l_tls_blocksize != 0)
|
||||
|
||||
+40
-1
@@ -38,6 +38,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <intprops.h>
|
||||
|
||||
/* Get libc version number. */
|
||||
#include "../version.h"
|
||||
@@ -410,6 +411,7 @@ load_shobj (const char *name)
|
||||
int fd;
|
||||
ElfW(Shdr) *shdr;
|
||||
size_t pagesize = getpagesize ();
|
||||
struct stat st;
|
||||
|
||||
/* Since we use dlopen() we must be prepared to work around the sometimes
|
||||
strange lookup rules for the shared objects. If we have a file foo.so
|
||||
@@ -550,14 +552,39 @@ load_shobj (const char *name)
|
||||
error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
|
||||
map->l_name);
|
||||
|
||||
if (fstat (fd, &st) < 0)
|
||||
error (EXIT_FAILURE, errno, _("stat(%s) failure"), map->l_name);
|
||||
|
||||
/* We're depending on data that's being read from the file, so be a
|
||||
bit paranoid here and make sure the requests are reasonable -
|
||||
i.e. both size and offset are nonnegative and smaller than the
|
||||
file size, as well as the offset of the end of the data. PREAD
|
||||
would have failed anyway, but this is more robust and explains
|
||||
what happened better. Note that SZ must be unsigned and OFF may
|
||||
be signed or unsigned. */
|
||||
#define PCHECK(sz1,off1) { \
|
||||
size_t sz = sz1, end_off; \
|
||||
off_t off = off1; \
|
||||
if (sz > st.st_size \
|
||||
|| off < 0 || off > st.st_size \
|
||||
|| INT_ADD_WRAPV (sz, off, &end_off) \
|
||||
|| end_off > st.st_size) \
|
||||
error (EXIT_FAILURE, ERANGE, \
|
||||
_("read outside of file extents %zu + %jd > %jd"), \
|
||||
sz, (intmax_t) off, (intmax_t) st.st_size); \
|
||||
}
|
||||
|
||||
/* Map the section header. */
|
||||
size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
|
||||
shdr = (ElfW(Shdr) *) alloca (size);
|
||||
PCHECK (size, ehdr->e_shoff);
|
||||
if (pread (fd, shdr, size, ehdr->e_shoff) != size)
|
||||
error (EXIT_FAILURE, errno, _("reading of section headers failed"));
|
||||
|
||||
/* Get the section header string table. */
|
||||
char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
|
||||
PCHECK (shdr[ehdr->e_shstrndx].sh_size,
|
||||
shdr[ehdr->e_shstrndx].sh_offset);
|
||||
if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
|
||||
shdr[ehdr->e_shstrndx].sh_offset)
|
||||
!= shdr[ehdr->e_shstrndx].sh_size)
|
||||
@@ -585,6 +612,7 @@ load_shobj (const char *name)
|
||||
size_t size = debuglink_entry->sh_size;
|
||||
char *debuginfo_fname = (char *) alloca (size + 1);
|
||||
debuginfo_fname[size] = '\0';
|
||||
PCHECK (size, debuglink_entry->sh_offset);
|
||||
if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
|
||||
!= size)
|
||||
{
|
||||
@@ -638,21 +666,32 @@ load_shobj (const char *name)
|
||||
if (fd2 != -1)
|
||||
{
|
||||
ElfW(Ehdr) ehdr2;
|
||||
struct stat st;
|
||||
|
||||
if (fstat (fd2, &st) < 0)
|
||||
error (EXIT_FAILURE, errno, _("stat(%s) failure"), workbuf);
|
||||
|
||||
/* Read the ELF header. */
|
||||
PCHECK (sizeof (ehdr2), 0);
|
||||
if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
|
||||
error (EXIT_FAILURE, errno,
|
||||
_("reading of ELF header failed"));
|
||||
|
||||
/* Map the section header. */
|
||||
size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
|
||||
size_t size;
|
||||
if (INT_MULTIPLY_WRAPV (ehdr2.e_shnum, sizeof (ElfW(Shdr)), &size))
|
||||
error (EXIT_FAILURE, errno, _("too many section headers"));
|
||||
|
||||
ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
|
||||
PCHECK (size, ehdr2.e_shoff);
|
||||
if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
|
||||
error (EXIT_FAILURE, errno,
|
||||
_("reading of section headers failed"));
|
||||
|
||||
/* Get the section header string table. */
|
||||
shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
|
||||
PCHECK (shdr2[ehdr2.e_shstrndx].sh_size,
|
||||
shdr2[ehdr2.e_shstrndx].sh_offset);
|
||||
if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
|
||||
shdr2[ehdr2.e_shstrndx].sh_offset)
|
||||
!= shdr2[ehdr2.e_shstrndx].sh_size)
|
||||
|
||||
+22
-4
@@ -40,6 +40,8 @@ static char SETGID_CHILD[] = "setgid-child";
|
||||
# define PROFILE_LIB "tst-sonamemove-runmod2.so"
|
||||
#endif
|
||||
|
||||
#define LD_DEBUG_OUTPUT "/tmp/some-file"
|
||||
|
||||
struct envvar_t
|
||||
{
|
||||
const char *env;
|
||||
@@ -61,7 +63,7 @@ static const struct envvar_t filtered_envvars[] =
|
||||
{ "MALLOC_TRIM_THRESHOLD_", FILTERED_VALUE },
|
||||
{ "RES_OPTIONS", FILTERED_VALUE },
|
||||
{ "LD_DEBUG", "all" },
|
||||
{ "LD_DEBUG_OUTPUT", "/tmp/some-file" },
|
||||
{ "LD_DEBUG_OUTPUT", LD_DEBUG_OUTPUT },
|
||||
{ "LD_WARN", FILTERED_VALUE },
|
||||
{ "LD_VERBOSE", FILTERED_VALUE },
|
||||
{ "LD_BIND_NOW", "0" },
|
||||
@@ -74,6 +76,14 @@ static const struct envvar_t unfiltered_envvars[] =
|
||||
{ "LD_ASSUME_KERNEL", UNFILTERED_VALUE },
|
||||
};
|
||||
|
||||
static void
|
||||
unlink_ld_debug_output (pid_t pid)
|
||||
{
|
||||
char *output = xasprintf ("%s.%d", LD_DEBUG_OUTPUT, pid);
|
||||
unlink (output);
|
||||
free (output);
|
||||
}
|
||||
|
||||
static int
|
||||
test_child (void)
|
||||
{
|
||||
@@ -138,13 +148,21 @@ do_test (int argc, char **argv)
|
||||
/* Setgid child process. */
|
||||
if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
|
||||
{
|
||||
pid_t ppid = getppid ();
|
||||
|
||||
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 ());
|
||||
{
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
unlink_ld_debug_output (ppid);
|
||||
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
}
|
||||
|
||||
int ret = test_child ();
|
||||
|
||||
unlink_ld_debug_output (ppid);
|
||||
|
||||
if (ret != 0)
|
||||
exit (1);
|
||||
return 0;
|
||||
|
||||
@@ -46,7 +46,7 @@ do_test (void)
|
||||
|
||||
{
|
||||
/* Install the default implementation of libmarkermod1.so. */
|
||||
char *conf_path = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
|
||||
char *conf_path = xasprintf ("%s/ld.so.conf.d/hwcaps.conf", support_sysconfdir_prefix);
|
||||
xmkdirp (support_sysconfdir_prefix, 0777);
|
||||
support_write_file_string (conf_path, "/glibc-test/lib\n");
|
||||
free (conf_path);
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/* Check that _dl_find_object behavior matches up with gaps.
|
||||
Copyright (C) 2025 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 <dlfcn.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <inttypes.h>
|
||||
#include <link.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xdlfcn.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <support/xstdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Slow path in case we cannot find a gap with mmap (when the runtime has
|
||||
mapped all the pages in the gap for some reason). */
|
||||
static bool
|
||||
find_gap_with_proc_self_map (const struct link_map *l)
|
||||
{
|
||||
int pagesize = getpagesize ();
|
||||
|
||||
support_need_proc ("Reads /proc/self/maps to find gap in ld.so mapping");
|
||||
|
||||
/* Parse /proc/self/maps and find all the mappings in the ld.so range
|
||||
but not from ld.so. */
|
||||
FILE *f = xfopen ("/proc/self/maps", "r");
|
||||
char *line = NULL, *path_ldso = NULL;
|
||||
size_t len;
|
||||
bool found = false;
|
||||
while (xgetline (&line, &len, f))
|
||||
{
|
||||
uintptr_t from, to;
|
||||
char *path = NULL;
|
||||
int r = sscanf (line, "%" SCNxPTR "-%" SCNxPTR "%*s%*s%*s%*s%ms",
|
||||
&from, &to, &path);
|
||||
|
||||
TEST_VERIFY (r == 2 || r == 3);
|
||||
TEST_COMPARE (from % pagesize, 0);
|
||||
TEST_COMPARE (to % pagesize, 0);
|
||||
|
||||
if (path_ldso == NULL && l->l_map_start == from)
|
||||
{
|
||||
TEST_COMPARE (r, 3);
|
||||
path_ldso = path;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from > l->l_map_start && to < l->l_map_end
|
||||
&& (r == 2 || (path_ldso != NULL && strcmp (path, path_ldso))))
|
||||
{
|
||||
if (r == 2)
|
||||
printf ("info: anonymous mapping found at 0x%" PRIxPTR " - 0x%"
|
||||
PRIxPTR "\n", from, to);
|
||||
else
|
||||
printf ("info: object \"%s\" found at 0x%" PRIxPTR " - 0x%"
|
||||
PRIxPTR "\n", path, from, to);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
free (path);
|
||||
}
|
||||
|
||||
free (path_ldso);
|
||||
free (line);
|
||||
xfclose (f);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct link_map *l = xdlopen (LD_SO, RTLD_NOW);
|
||||
if (!l->l_contiguous)
|
||||
{
|
||||
puts ("info: ld.so link map is not contiguous");
|
||||
|
||||
/* Try to find holes by probing with mmap. */
|
||||
int pagesize = getpagesize ();
|
||||
bool gap_found = false;
|
||||
ElfW(Addr) addr = l->l_map_start;
|
||||
TEST_COMPARE (addr % pagesize, 0);
|
||||
while (addr < l->l_map_end)
|
||||
{
|
||||
void *expected = (void *) addr;
|
||||
void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
struct dl_find_object dlfo;
|
||||
int dlfo_ret = _dl_find_object (expected, &dlfo);
|
||||
if (ptr == expected)
|
||||
{
|
||||
if (dlfo_ret < 0)
|
||||
{
|
||||
TEST_COMPARE (dlfo_ret, -1);
|
||||
printf ("info: hole without mapping data found at %p\n", ptr);
|
||||
}
|
||||
else
|
||||
FAIL ("object \"%s\" found in gap at %p",
|
||||
dlfo.dlfo_link_map->l_name, ptr);
|
||||
gap_found = true;
|
||||
}
|
||||
else if (dlfo_ret == 0)
|
||||
{
|
||||
if ((void *) dlfo.dlfo_link_map != (void *) l)
|
||||
{
|
||||
printf ("info: object \"%s\" found at %p\n",
|
||||
dlfo.dlfo_link_map->l_name, expected);
|
||||
gap_found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
TEST_COMPARE (dlfo_ret, -1);
|
||||
|
||||
xmunmap (ptr, 1);
|
||||
addr += pagesize;
|
||||
}
|
||||
|
||||
if (!gap_found && !find_gap_with_proc_self_map (l))
|
||||
FAIL ("no ld.so gap found");
|
||||
}
|
||||
else
|
||||
{
|
||||
puts ("info: ld.so link map is contiguous");
|
||||
|
||||
/* Assert that ld.so is truly contiguous in memory. */
|
||||
volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
while (p < end)
|
||||
{
|
||||
*p;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
xdlclose (l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Check that the entire libc.so program image is readable if contiguous.
|
||||
Copyright (C) 2025 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 <gnu/lib-names.h>
|
||||
#include <link.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xdlfcn.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct link_map *l = xdlopen (LIBC_SO, RTLD_NOW);
|
||||
|
||||
/* The dynamic loader fills holes with PROT_NONE mappings. */
|
||||
if (!l->l_contiguous)
|
||||
FAIL_EXIT1 ("libc.so link map is not contiguous");
|
||||
|
||||
/* Direct probing does not work because not everything is readable
|
||||
due to PROT_NONE mappings. */
|
||||
int pagesize = getpagesize ();
|
||||
ElfW(Addr) addr = l->l_map_start;
|
||||
TEST_COMPARE (addr % pagesize, 0);
|
||||
while (addr < l->l_map_end)
|
||||
{
|
||||
void *expected = (void *) addr;
|
||||
void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
if (ptr == expected)
|
||||
FAIL ("hole in libc.so memory image after %lu bytes",
|
||||
(unsigned long int) (addr - l->l_map_start));
|
||||
xmunmap (ptr, 1);
|
||||
addr += pagesize;
|
||||
}
|
||||
|
||||
xdlclose (l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Check that the entire main program image is readable if contiguous.
|
||||
Copyright (C) 2025 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 <link.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xdlfcn.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct link_map *l = xdlopen ("", RTLD_NOW);
|
||||
if (!l->l_contiguous)
|
||||
FAIL_UNSUPPORTED ("main link map is not contiguous");
|
||||
|
||||
/* This check only works if the kernel loaded the main program. The
|
||||
dynamic loader replaces gaps with PROT_NONE mappings, resulting
|
||||
in faults. */
|
||||
volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
while (p < end)
|
||||
{
|
||||
*p;
|
||||
++p;
|
||||
}
|
||||
|
||||
xdlclose (l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include <support/test-driver.c>
|
||||
@@ -39,8 +39,8 @@ struct protoent_data {};
|
||||
#include <nss/nss_files/files-parse.c>
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->p_name, isspace, 1);
|
||||
INT_FIELD (result->p_proto, isspace, 1, 10,);
|
||||
STRING_FIELD (result->p_name, isspace, true);
|
||||
INT_FIELD (result->p_proto, isspace, true, 10,);
|
||||
)
|
||||
|
||||
enum nss_status
|
||||
|
||||
@@ -39,9 +39,9 @@ struct servent_data {};
|
||||
#define ISSC_OR_SPACE(c) ((c) == ';' || isspace (c))
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->s_name, ISSC_OR_SPACE, 1);
|
||||
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, 1);
|
||||
INT_FIELD (result->s_port, ISSC_OR_SPACE, 10, 0, htons);
|
||||
STRING_FIELD (result->s_name, ISSC_OR_SPACE, true);
|
||||
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, true);
|
||||
INT_FIELD (result->s_port, ISSC_OR_SPACE, false, 10, htons);
|
||||
)
|
||||
|
||||
enum nss_status
|
||||
|
||||
+2
-1
@@ -138,7 +138,8 @@ $(objpfx)test-iconvconfig.out: $(objpfx)iconvconfig
|
||||
rm -f $$tmp) > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog
|
||||
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog \
|
||||
$(gen-locales)
|
||||
$(BASH) $< $(common-objdir) '$(test-wrapper-env)' \
|
||||
'$(run-program-env)' > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+4
-2
@@ -144,8 +144,10 @@
|
||||
if (irreversible == NULL) \
|
||||
{ \
|
||||
/* This means we are in call from __gconv_transliterate. In this \
|
||||
case we are not doing any error recovery ourselves. */ \
|
||||
result = __gconv_mark_illegal_input (step_data); \
|
||||
case we are not doing any error recovery ourselves. Do not create \
|
||||
a persistent error state. If __gconv_transliterate exhausts all \
|
||||
alternatives, it will call __gconv_mark_illegal_input itself. */ \
|
||||
result = __GCONV_ILLEGAL_INPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
|
||||
+20
-10
@@ -27,10 +27,10 @@ LIBPATH=$codir:$codir/iconvdata
|
||||
|
||||
# How the start the iconv(1) program. $from is not defined/expanded yet.
|
||||
ICONV='
|
||||
$test_wrapper_env $run_program_env
|
||||
$codir/elf/ld.so --library-path $LIBPATH --inhibit-rpath ${from}.so
|
||||
$codir/iconv/iconv_prog
|
||||
'
|
||||
ICONV="$test_wrapper_env $run_program_env $ICONV"
|
||||
|
||||
TIMEOUTFACTOR=${TIMEOUTFACTOR:-1}
|
||||
|
||||
@@ -218,6 +218,7 @@ testarray=(
|
||||
"\x00\x00;;INVALID;UTF-8;1"
|
||||
"\x00\x00;;UTF-8;INVALID;1"
|
||||
"\xc3\xa9;;UTF-8;ASCII//TRANSLIT;0"
|
||||
"X\xc2\xbdY;;UTF-8;ASCII//TRANSLIT;0"
|
||||
)
|
||||
|
||||
# Requires $twobyte input, $c flag, $from, and $to to be set; sets $ret
|
||||
@@ -278,12 +279,21 @@ check_errtest_result ()
|
||||
fi
|
||||
}
|
||||
|
||||
for testcommand in "${testarray[@]}"; do
|
||||
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
|
||||
c="$(echo "$testcommand" | cut -d";" -f 2)"
|
||||
from="$(echo "$testcommand" | cut -d";" -f 3)"
|
||||
to="$(echo "$testcommand" | cut -d";" -f 4)"
|
||||
eret="$(echo "$testcommand" | cut -d";" -f 5)"
|
||||
execute_test
|
||||
check_errtest_result
|
||||
done
|
||||
run_test_array ()
|
||||
{
|
||||
for testcommand in "${testarray[@]}"; do
|
||||
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
|
||||
c="$(echo "$testcommand" | cut -d";" -f 2)"
|
||||
from="$(echo "$testcommand" | cut -d";" -f 3)"
|
||||
to="$(echo "$testcommand" | cut -d";" -f 4)"
|
||||
eret="$(echo "$testcommand" | cut -d";" -f 5)"
|
||||
execute_test
|
||||
check_errtest_result
|
||||
done
|
||||
}
|
||||
|
||||
echo "info: testing C locale"
|
||||
run_test_array
|
||||
echo "info: testing en_US.UTF-8 locale"
|
||||
run_program_env="$run_program_env LC_ALL=en_US.UTF-8"
|
||||
run_test_array
|
||||
|
||||
+6
-1
@@ -76,7 +76,8 @@ 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 tst-iconv-big5-hkscs-to-2ucs4 \
|
||||
bug-iconv13 bug-iconv14 bug-iconv15 \
|
||||
tst-iconv-iso-2022-cn-ext
|
||||
tst-iconv-iso-2022-cn-ext tst-bug33980 \
|
||||
tst-jisx0213-progress
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -333,6 +334,10 @@ $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)tst-bug33980.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)tst-jisx0213-progress.out: \
|
||||
$(addprefix $(objpfx), $(gconv-modules)) $(addprefix $(objpfx),$(modules.so))
|
||||
|
||||
$(objpfx)iconv-test.out: run-iconv-test.sh \
|
||||
$(addprefix $(objpfx), $(gconv-modules)) \
|
||||
|
||||
@@ -407,7 +407,7 @@ static const char from_ucs4[][2] =
|
||||
is also available. */ \
|
||||
uint32_t ch2; \
|
||||
\
|
||||
if (inptr + 1 >= inend) \
|
||||
if (inend - inptr <= 1) \
|
||||
{ \
|
||||
/* The second character is not available. */ \
|
||||
result = __GCONV_INCOMPLETE_INPUT; \
|
||||
|
||||
@@ -224,6 +224,9 @@
|
||||
STANDARD_FROM_LOOP_ERR_HANDLER (1); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
/* There was a pending character. Clear it. */ \
|
||||
*statep = 0; \
|
||||
\
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
|
||||
+55
-15
@@ -67,12 +67,29 @@
|
||||
|
||||
/* 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. */
|
||||
flushing. For the to-internal direction (FROM_DIRECTION is true),
|
||||
there may be a pending character that needs flushing. */
|
||||
#define EMIT_SHIFT_TO_INIT \
|
||||
if ((data->__statep->__count & ~7) != sb) \
|
||||
{ \
|
||||
if (FROM_DIRECTION) \
|
||||
data->__statep->__count &= 7; \
|
||||
{ \
|
||||
uint32_t ch = data->__statep->__count >> 7; \
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
if (__glibc_unlikely (outend - outbuf < 4)) \
|
||||
status = __GCONV_FULL_OUTPUT; \
|
||||
else \
|
||||
{ \
|
||||
put32 (outbuf, ch); \
|
||||
outbuf += 4; \
|
||||
/* Clear character and db bit. */ \
|
||||
data->__statep->__count &= 7; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
data->__statep->__count &= 7; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* We are not in the initial state. To switch back we have \
|
||||
@@ -99,11 +116,13 @@
|
||||
*curcsp = save_curcs
|
||||
|
||||
|
||||
/* Current codeset type. */
|
||||
/* Current codeset type. The bit is stored in the __count variable of
|
||||
the conversion state. If the db bit is set, bit 7 and above store
|
||||
a pending UCS-4 code point if non-zero. */
|
||||
enum
|
||||
{
|
||||
sb = 0,
|
||||
db = 64
|
||||
sb = 0, /* Single byte mode. */
|
||||
db = 64 /* Double byte mode. */
|
||||
};
|
||||
|
||||
|
||||
@@ -119,21 +138,29 @@ enum
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* This is a combined character. Make sure we have room. */ \
|
||||
if (__glibc_unlikely (outptr + 8 > outend)) \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
const struct divide *cmbp \
|
||||
= &DB_TO_UCS4_COMB[ch - __TO_UCS4_COMBINED_MIN]; \
|
||||
assert (cmbp->res1 != 0 && cmbp->res2 != 0); \
|
||||
\
|
||||
put32 (outptr, cmbp->res1); \
|
||||
outptr += 4; \
|
||||
put32 (outptr, cmbp->res2); \
|
||||
outptr += 4; \
|
||||
\
|
||||
/* See whether we have room for the second character. */ \
|
||||
if (outend - outptr >= 4) \
|
||||
{ \
|
||||
put32 (outptr, cmbp->res2); \
|
||||
outptr += 4; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* Otherwise store only the first character now, and \
|
||||
put the second one into the queue. */ \
|
||||
curcs |= cmbp->res2 << 7; \
|
||||
inptr += 2; \
|
||||
/* Tell the caller why we terminate the loop. */ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
@@ -153,7 +180,20 @@ enum
|
||||
#define LOOPFCT FROM_LOOP
|
||||
#define BODY \
|
||||
{ \
|
||||
uint32_t ch = *inptr; \
|
||||
uint32_t ch; \
|
||||
\
|
||||
ch = curcs >> 7; \
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
/* Remove the pending character, but preserve state bits. */ \
|
||||
curcs &= (1 << 7) - 1; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise read the next input byte. */ \
|
||||
ch = *inptr; \
|
||||
\
|
||||
if (__builtin_expect (ch, 0) == SO) \
|
||||
{ \
|
||||
|
||||
@@ -226,6 +226,9 @@
|
||||
STANDARD_FROM_LOOP_ERR_HANDLER (1); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
/* There was a pending character. Clear it. */ \
|
||||
*statep = 0; \
|
||||
\
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/* Test for bug 33980: combining characters in IBM1390/IBM1399.
|
||||
Copyright (C) 2026 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 <alloc_buffer.h>
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes
|
||||
starting at OUTBUF. OUTBUF should be right before an unmapped page
|
||||
so that writing past the end will fault. Skip SHIFT bytes at the
|
||||
start of the input and output, to exercise different buffer
|
||||
alignment. TRUNCATE indicates skipped bytes at the end of
|
||||
input (0 and 1 a valid). */
|
||||
static void
|
||||
test_one (const char *encoding, unsigned int shift, unsigned int truncate,
|
||||
char *outbuf, size_t outbufsize)
|
||||
{
|
||||
/* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two
|
||||
Unicode code points when translated. */
|
||||
static char input[] =
|
||||
{
|
||||
/* 8 letters X. */
|
||||
0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
|
||||
/* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */
|
||||
0x0e, 0xec, 0xb5, 0x0f
|
||||
};
|
||||
|
||||
/* Expected output after UTF-8 conversion. */
|
||||
static char expected[] =
|
||||
{
|
||||
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
|
||||
/* U+304B (HIRAGANA LETTER KA). */
|
||||
0xe3, 0x81, 0x8b,
|
||||
/* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */
|
||||
0xe3, 0x82, 0x9a
|
||||
};
|
||||
|
||||
iconv_t cd = iconv_open ("UTF-8", encoding);
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
char result_storage[64];
|
||||
struct alloc_buffer result_buf
|
||||
= alloc_buffer_create (result_storage, sizeof (result_storage));
|
||||
|
||||
char *inptr = &input[shift];
|
||||
size_t inleft = sizeof (input) - shift - truncate;
|
||||
|
||||
while (inleft > 0)
|
||||
{
|
||||
char *outptr = outbuf;
|
||||
size_t outleft = outbufsize;
|
||||
size_t inleft_before = inleft;
|
||||
|
||||
size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
|
||||
size_t produced = outptr - outbuf;
|
||||
alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
|
||||
|
||||
if (ret == (size_t) -1 && errno == E2BIG)
|
||||
{
|
||||
if (produced == 0 && inleft == inleft_before)
|
||||
{
|
||||
/* Output buffer too small to make progress. This is
|
||||
expected for very small output buffer sizes. */
|
||||
TEST_VERIFY_EXIT (outbufsize < 3);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ret == (size_t) -1)
|
||||
FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Flush any pending state (e.g. a buffered combined character).
|
||||
With outbufsize < 3, we could not store the first character, so
|
||||
the second character did not become pending, and there is nothing
|
||||
to flush. */
|
||||
{
|
||||
char *outptr = outbuf;
|
||||
size_t outleft = outbufsize;
|
||||
|
||||
size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
|
||||
TEST_VERIFY_EXIT (ret == 0);
|
||||
size_t produced = outptr - outbuf;
|
||||
alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
|
||||
|
||||
/* Second flush does not provide more data. */
|
||||
outptr = outbuf;
|
||||
outleft = outbufsize;
|
||||
ret = iconv (cd, NULL, NULL, &outptr, &outleft);
|
||||
TEST_VERIFY_EXIT (ret == 0);
|
||||
TEST_VERIFY (outptr == outbuf);
|
||||
}
|
||||
|
||||
TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf));
|
||||
size_t result_used
|
||||
= sizeof (result_storage) - alloc_buffer_size (&result_buf);
|
||||
|
||||
if (outbufsize >= 3)
|
||||
{
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (result_used, sizeof (expected) - shift);
|
||||
TEST_COMPARE_BLOB (result_storage, result_used,
|
||||
&expected[shift], sizeof (expected) - shift);
|
||||
}
|
||||
else
|
||||
/* If the buffer is too small, only the leading X could be converted. */
|
||||
TEST_COMPARE (result_used, 8 - shift);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct support_next_to_fault ntf
|
||||
= support_next_to_fault_allocate (8);
|
||||
|
||||
for (int shift = 0; shift <= 8; ++shift)
|
||||
for (int truncate = 0; truncate < 2; ++truncate)
|
||||
for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++)
|
||||
{
|
||||
char *outbuf = ntf.buffer + ntf.length - outbufsize;
|
||||
test_one ("IBM1390", shift, truncate, outbuf, outbufsize);
|
||||
test_one ("IBM1399", shift, truncate, outbuf, outbufsize);
|
||||
}
|
||||
|
||||
support_next_to_fault_free (&ntf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,124 @@
|
||||
/* Test JISX0213 combining character conversion progress (bug 34556, bug 34568).
|
||||
Copyright (C) 2026 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/>. */
|
||||
|
||||
/* Certain JISX0213 byte sequences map to a combining sequence, for
|
||||
example U+304B (HIRAGANA LETTER KA) followed by U+309A (COMBINING
|
||||
SEMI-VOICED SOUND MARK). When converting to internal encoding
|
||||
(actually UTF-32) with a small output buffer, the first code point
|
||||
is emitted and the second is queued in the converter state. This
|
||||
test verifies that the queued code point is consumed exactly once
|
||||
on retry, so that the conversion makes progress and terminates. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static void
|
||||
test_one (const char *charset, const char *input, size_t outbufsize)
|
||||
{
|
||||
printf ("info: %s: testing output buffer size %zu\n", charset, outbufsize);
|
||||
|
||||
/* Expected UTF-32 output. */
|
||||
static const wchar_t expected[] = { 0x304b, 0x309a, 'A' };
|
||||
|
||||
/* Use WCHAR_T encoding to avoid the BOM. */
|
||||
iconv_t cd = iconv_open ("WCHAR_T", charset);
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
char result[64];
|
||||
size_t result_len = 0;
|
||||
|
||||
char *inptr = (char *) input;
|
||||
size_t inleft = strlen (input);
|
||||
|
||||
char outbuf[64];
|
||||
|
||||
int iterations = 0;
|
||||
while (inleft > 0)
|
||||
{
|
||||
char *outptr = outbuf;
|
||||
size_t outleft = outbufsize;
|
||||
size_t inleft_before = inleft;
|
||||
|
||||
size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
|
||||
size_t produced = outptr - outbuf;
|
||||
|
||||
TEST_VERIFY_EXIT (result_len + produced <= sizeof (result));
|
||||
memcpy (result + result_len, outbuf, produced);
|
||||
result_len += produced;
|
||||
|
||||
if (ret == (size_t) -1 && errno == E2BIG)
|
||||
{
|
||||
if (produced == 0 && inleft == inleft_before)
|
||||
{
|
||||
/* Output buffer too small for a single code point. */
|
||||
TEST_VERIFY_EXIT (outbufsize < 4);
|
||||
break;
|
||||
}
|
||||
/* Bound iterations to detect non-progress bugs. */
|
||||
if (++iterations < 10)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
FAIL ("%s: no progress", charset);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (ret == (size_t) -1)
|
||||
FAIL_EXIT1 ("outbufsize %zu: iconv: %m", outbufsize);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Flush pending converter state. */
|
||||
{
|
||||
char *outptr = outbuf;
|
||||
size_t outleft = outbufsize;
|
||||
size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
|
||||
TEST_VERIFY (ret == 0);
|
||||
size_t produced = outptr - outbuf;
|
||||
memcpy (result + result_len, outbuf, produced);
|
||||
result_len += produced;
|
||||
}
|
||||
|
||||
if (outbufsize >= 4)
|
||||
{
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE_BLOB (result, result_len,
|
||||
expected, sizeof (expected));
|
||||
}
|
||||
|
||||
out:
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
for (size_t outbufsize = 1; outbufsize <= 16; outbufsize++)
|
||||
{
|
||||
test_one ("EUC-JISX0213", "\244\367A", outbufsize);
|
||||
test_one ("SHIFT_JISX0213", "\202\365A", outbufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -70,6 +70,11 @@ libc_hidden_proto (__libc_res_nameinquery)
|
||||
extern __typeof (__res_queriesmatch) __libc_res_queriesmatch;
|
||||
libc_hidden_proto (__libc_res_queriesmatch)
|
||||
|
||||
extern const struct res_sym __p_class_syms[];
|
||||
libresolv_hidden_proto (__p_class_syms)
|
||||
extern const struct res_sym __p_type_syms[];
|
||||
libresolv_hidden_proto (__p_type_syms)
|
||||
|
||||
/* Variant of res_hnok which operates on binary (but uncompressed) names. */
|
||||
bool __res_binary_hnok (const unsigned char *dn) attribute_hidden;
|
||||
|
||||
|
||||
@@ -45,15 +45,15 @@ __NTH (inet_pton (int __af,
|
||||
__fortify_clang_warning_only_if_bos0_lt
|
||||
(4, __dst, "inet_pton called with destination buffer size less than 4")
|
||||
{
|
||||
size_t sz = 0;
|
||||
size_t __sz = 0;
|
||||
if (__af == AF_INET)
|
||||
sz = sizeof (struct in_addr);
|
||||
__sz = sizeof (struct in_addr);
|
||||
else if (__af == AF_INET6)
|
||||
sz = sizeof (struct in6_addr);
|
||||
__sz = sizeof (struct in6_addr);
|
||||
else
|
||||
return __inet_pton_alias (__af, __src, __dst);
|
||||
|
||||
return __glibc_fortify (inet_pton, sz, sizeof (char),
|
||||
return __glibc_fortify (inet_pton, __sz, sizeof (char),
|
||||
__glibc_objsize (__dst),
|
||||
__af, __src, __dst);
|
||||
};
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef __O_CLOEXEC
|
||||
# error __O_CLOEXEC not defined by fcntl.h/cloexec.h
|
||||
#endif
|
||||
|
||||
/* Perform file control operations on FD. */
|
||||
int
|
||||
__fcntl (int fd, int cmd, ...)
|
||||
|
||||
@@ -83,6 +83,7 @@ tests = \
|
||||
bug-ungetwc1 \
|
||||
bug-ungetwc2 \
|
||||
bug-wfflush \
|
||||
bug-wgenops-bz33998 \
|
||||
bug-wmemstream1 \
|
||||
bug-wsetpos \
|
||||
test-fmemopen \
|
||||
@@ -106,6 +107,7 @@ tests = \
|
||||
tst-fgetc-after-eof \
|
||||
tst-fgetwc \
|
||||
tst-fgetws \
|
||||
tst-fopen-ccs-empty \
|
||||
tst-fopenloc2 \
|
||||
tst-fputws \
|
||||
tst-freopen \
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Regression test for ungetwc operating on byte stream (BZ #33998)
|
||||
Copyright (C) 2026 The GNU Toolchain Authors.
|
||||
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 "support/temp_file.h"
|
||||
#include "support/xstdio.h"
|
||||
#include "support/xunistd.h"
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *filename;
|
||||
int fd = create_temp_file ("tst-bz33998-", &filename);
|
||||
TEST_VERIFY (fd != -1);
|
||||
xwrite (fd, "A", sizeof ("A")); // write "A\0" by design
|
||||
xclose (fd);
|
||||
|
||||
FILE *fp = xfopen (filename, "r+");
|
||||
TEST_COMPARE (getwc (fp), L'A');
|
||||
/* If the bug is fixed, then ungetwc should not touch byte stream.
|
||||
If the bug is not fixed, ungetwc firstly match last read char, L'A',
|
||||
failed, then the pbackfail branch, matching last read char in byte
|
||||
stream, that is, '\0' (initialized when setup wide stream). */
|
||||
char *old_read_ptr = fp->_IO_read_ptr;
|
||||
TEST_COMPARE (ungetwc (L'\0', fp), L'\0');
|
||||
TEST_VERIFY (fp->_IO_read_ptr == old_read_ptr);
|
||||
|
||||
xfclose (fp);
|
||||
free (filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+7
-5
@@ -339,12 +339,14 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const char *mode,
|
||||
*((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
|
||||
strip (ccs, ccs);
|
||||
|
||||
if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0'
|
||||
? upstr (ccs, cs + 5) : ccs) != 0)
|
||||
/* After stripping, ccs[2] == '\0' means the charset name is empty.
|
||||
This is not a valid charset and would cause problems downstream.
|
||||
Reject it with EINVAL (BZ #34574, CVE-2026-18374). */
|
||||
if (ccs[2] == '\0' || __wcsmbs_named_conv (&fcts, ccs) != 0)
|
||||
{
|
||||
/* Something went wrong, we cannot load the conversion modules.
|
||||
This means we cannot proceed since the user explicitly asked
|
||||
for these. */
|
||||
/* Either the charset name is empty after strip(), or conversion
|
||||
modules cannot be loaded. This means we cannot proceed since
|
||||
the user explicitly asked for character conversion. */
|
||||
(void) _IO_file_close_it (fp);
|
||||
free (ccs);
|
||||
__set_errno (EINVAL);
|
||||
|
||||
+3
-3
@@ -168,11 +168,11 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
|
||||
#ifdef __USE_GNU
|
||||
/* Flags for renameat2. */
|
||||
# define RENAME_NOREPLACE (1 << 0)
|
||||
# define AT_RENAME_NOREPLACE RENAME_NOREPLACE
|
||||
# define AT_RENAME_NOREPLACE 0x0001
|
||||
# define RENAME_EXCHANGE (1 << 1)
|
||||
# define AT_RENAME_EXCHANGE RENAME_EXCHANGE
|
||||
# define AT_RENAME_EXCHANGE 0x0002
|
||||
# define RENAME_WHITEOUT (1 << 2)
|
||||
# define AT_RENAME_WHITEOUT RENAME_WHITEOUT
|
||||
# define AT_RENAME_WHITEOUT 0x0004
|
||||
|
||||
/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
|
||||
additional flags. */
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* Test fopen with an empty ",ccs=" value in the mode string (bug 34574).
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
static void
|
||||
check_fopen_fails (const char *path, const char *mode)
|
||||
{
|
||||
errno = 0;
|
||||
FILE *fp = fopen (path, mode);
|
||||
TEST_VERIFY (fp == NULL);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
if (fp != NULL)
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *path;
|
||||
xclose (create_temp_file ("tst-fopen-ccs-empty", &path));
|
||||
|
||||
/* The value is blank and the mode string continues well past it. */
|
||||
enum { size = 1024 * 1024 };
|
||||
char *mode = xmalloc (size);
|
||||
memset (mode, 'X', size);
|
||||
mode[size - 1] = '\0';
|
||||
static const char prefix[] = "w,ccs= ,";
|
||||
memcpy (mode, prefix, sizeof (prefix) - 1);
|
||||
check_fopen_fails (path, mode);
|
||||
free (mode);
|
||||
|
||||
check_fopen_fails (path, "w,ccs=");
|
||||
check_fopen_fails (path, "w,ccs=,");
|
||||
|
||||
free (path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+2
-2
@@ -108,8 +108,8 @@ _IO_wdefault_pbackfail (FILE *fp, wint_t c)
|
||||
{
|
||||
if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
|
||||
&& !_IO_in_backup (fp)
|
||||
&& (wint_t) fp->_IO_read_ptr[-1] == c)
|
||||
--fp->_IO_read_ptr;
|
||||
&& (wint_t) fp->_wide_data->_IO_read_ptr[-1] == c)
|
||||
--fp->_wide_data->_IO_read_ptr;
|
||||
else
|
||||
{
|
||||
/* Need to handle a filebuf in write mode (switch to read mode). FIXME!*/
|
||||
|
||||
@@ -236,6 +236,8 @@ tests = \
|
||||
bug-iconv-trans \
|
||||
bug-setlocale1 \
|
||||
bug-usesetlocale \
|
||||
tst-bz12701-lc \
|
||||
tst-bz12701-lc2 \
|
||||
tst-bz13988 \
|
||||
tst-c-utf8-consistency \
|
||||
tst-digits \
|
||||
|
||||
@@ -248,8 +248,8 @@ reorder-end
|
||||
END LC_COLLATE
|
||||
|
||||
LC_MONETARY
|
||||
int_curr_symbol "BGN "
|
||||
currency_symbol "лв."
|
||||
int_curr_symbol "EUR "
|
||||
currency_symbol "€"
|
||||
mon_decimal_point ","
|
||||
mon_thousands_sep " "
|
||||
mon_grouping 3
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/* Verify scanf field width handling with the 'lc' conversion (BZ #12701).
|
||||
Copyright (C) 2025-2026 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 <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <libc-diag.h>
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
#include <support/xstdio.h>
|
||||
|
||||
/* Compare character-wise the initial part of the wide character object
|
||||
pointed to by WS corresponding to wide characters obtained by the
|
||||
conversion of first N bytes of the multibyte character object pointed
|
||||
to by S. */
|
||||
|
||||
static int
|
||||
tst_bz12701_lc_memcmp (const wchar_t *ds, const char *s, size_t n)
|
||||
{
|
||||
size_t nc = mbsnrtowcs (NULL, &s, n, 0, NULL);
|
||||
|
||||
struct support_next_to_fault ntf;
|
||||
ntf = support_next_to_fault_allocate (nc * sizeof (wchar_t));
|
||||
wchar_t *ss = (wchar_t *) ntf.buffer;
|
||||
|
||||
mbsnrtowcs (ss, &s, n, nc, NULL);
|
||||
int r = wmemcmp (ds, ss, nc);
|
||||
|
||||
support_next_to_fault_free (&ntf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Verify various aspects of field width handling, including the data
|
||||
obtained, the number of bytes consumed, and the stream position. */
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
if (setlocale (LC_ALL, "pl_PL.UTF-8") == NULL)
|
||||
FAIL_EXIT1 ("setlocale (LC_ALL, \"pl_PL.UTF-8\")");
|
||||
|
||||
/* Part of a tongue-twister in Polish, which says:
|
||||
"On a rainy morning cuckoos and warblers, rather than starting
|
||||
on earthworms, stuffed themselves fasted with the flesh of cress." */
|
||||
static const char s[126] = "Dżdżystym rankiem gżegżółki i piegże, "
|
||||
"zamiast wziąć się za dżdżownice, "
|
||||
"nażarły się na czczo miąższu rzeżuchy";
|
||||
|
||||
const char *sp = s;
|
||||
size_t nc;
|
||||
TEST_VERIFY_EXIT ((nc = mbsnrtowcs (NULL, &sp, sizeof (s), 0, NULL)) == 108);
|
||||
|
||||
struct support_next_to_fault ntfo, ntfi;
|
||||
ntfo = support_next_to_fault_allocate (nc * sizeof (wchar_t));
|
||||
ntfi = support_next_to_fault_allocate (sizeof (s));
|
||||
wchar_t *e = (wchar_t *) ntfo.buffer + nc;
|
||||
char *b = ntfi.buffer;
|
||||
|
||||
wchar_t *c;
|
||||
FILE *f;
|
||||
int ic;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
memcpy (ntfi.buffer, s, sizeof (s));
|
||||
|
||||
ic = i = 0;
|
||||
f = xfmemopen (b, sizeof (s), "r");
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
/* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%0lc%n", c, &n) == 1);
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 1;
|
||||
i += n;
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 1;
|
||||
i += n;
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%1lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 1;
|
||||
i += n;
|
||||
|
||||
c = e - 2;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 3);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 2;
|
||||
i += n;
|
||||
|
||||
c = e - 4;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%4lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 4);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 4;
|
||||
i += n;
|
||||
|
||||
c = e - 8;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%8lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 8);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 8;
|
||||
i += n;
|
||||
|
||||
c = e - 16;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%16lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 20);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 16;
|
||||
i += n;
|
||||
|
||||
c = e - 32;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%32lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 38);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 32;
|
||||
i += n;
|
||||
|
||||
c = e - (nc - ic);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_COMPARE (fscanf (f, "%64lc%n", c, &n), 1);
|
||||
TEST_COMPARE (n , 49);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, sizeof (s) - i) == 0);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == sizeof (s));
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
ic = i = 0;
|
||||
f = xfmemopen (b, 3, "r");
|
||||
|
||||
c = e - 2;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 3);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 2;
|
||||
i += n;
|
||||
|
||||
c = e - (nc - ic);
|
||||
TEST_VERIFY_EXIT (feof (f) == 0);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == EOF);
|
||||
TEST_VERIFY_EXIT (n == 3);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == 3);
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
ic = i = 0;
|
||||
f = xfmemopen (b, 3, "r");
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
|
||||
ic += 1;
|
||||
i += n;
|
||||
|
||||
c = e - (nc - ic);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, 3 - i) == 0);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == 3);
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
support_next_to_fault_free (&ntfi);
|
||||
support_next_to_fault_free (&ntfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
|
||||
Copyright (C) 2026 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 <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libc-diag.h>
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
#include <support/xstdio.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
wchar_t *c = NULL;
|
||||
int i;
|
||||
|
||||
TEST_VERIFY (sscanf ("1234", "%30mlc", &c) == 1);
|
||||
|
||||
TEST_VERIFY (c != NULL);
|
||||
TEST_COMPARE_BLOB (c, 5 * sizeof (wchar_t),
|
||||
L"1234\0", 5 * sizeof (wchar_t));
|
||||
for (i = 5; i < 30; i ++)
|
||||
TEST_VERIFY (c[i] == L'\0');
|
||||
|
||||
TEST_VERIFY (malloc_usable_size (c) >= 30 * sizeof(wchar_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+32
-16
@@ -230,6 +230,9 @@
|
||||
/* For uintptr_t. */
|
||||
#include <stdint.h>
|
||||
|
||||
/* For stdc_count_ones. */
|
||||
#include <stdbit.h>
|
||||
|
||||
/* For va_arg, va_start, va_end. */
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -294,9 +297,9 @@
|
||||
# define TCACHE_SMALL_BINS 64
|
||||
# define TCACHE_LARGE_BINS 12 /* Up to 4M chunks */
|
||||
# define TCACHE_MAX_BINS (TCACHE_SMALL_BINS + TCACHE_LARGE_BINS)
|
||||
# define MAX_TCACHE_SMALL_SIZE tidx2usize (TCACHE_SMALL_BINS-1)
|
||||
# define MAX_TCACHE_SMALL_SIZE tidx2csize (TCACHE_SMALL_BINS-1)
|
||||
|
||||
/* Only used to pre-fill the tunables. */
|
||||
# define tidx2csize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE)
|
||||
# define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
|
||||
|
||||
/* When "x" is from chunksize(). */
|
||||
@@ -1932,7 +1935,7 @@ static struct malloc_par mp_ =
|
||||
,
|
||||
.tcache_count = TCACHE_FILL_COUNT,
|
||||
.tcache_small_bins = TCACHE_SMALL_BINS,
|
||||
.tcache_max_bytes = MAX_TCACHE_SMALL_SIZE,
|
||||
.tcache_max_bytes = MAX_TCACHE_SMALL_SIZE + 1,
|
||||
.tcache_unsorted_limit = 0 /* No limit. */
|
||||
#endif
|
||||
};
|
||||
@@ -3152,6 +3155,19 @@ tcache_key_initialize (void)
|
||||
if (__getrandom_nocancel_nostatus_direct (&tcache_key, sizeof(tcache_key),
|
||||
GRND_NONBLOCK)
|
||||
!= sizeof (tcache_key))
|
||||
tcache_key = 0;
|
||||
|
||||
/* We need tcache_key to be non-zero (otherwise tcache_double_free_verify's
|
||||
clearing of e->key would go unnoticed and it would loop getting called
|
||||
through __libc_free), and we want tcache_key not to be a
|
||||
commonly-occurring value in memory, so ensure a minimum amount of one and
|
||||
zero bits. */
|
||||
int minimum_bits = __WORDSIZE / 4;
|
||||
int maximum_bits = __WORDSIZE - minimum_bits;
|
||||
|
||||
while (labs ((intptr_t) tcache_key) <= 0x1000000
|
||||
|| stdc_count_ones (tcache_key) < minimum_bits
|
||||
|| stdc_count_ones (tcache_key) > maximum_bits)
|
||||
{
|
||||
tcache_key = random_bits ();
|
||||
#if __WORDSIZE == 64
|
||||
@@ -3208,11 +3224,10 @@ tcache_get_n (size_t tc_idx, tcache_entry **ep, bool mangled)
|
||||
if (__glibc_unlikely (misaligned_mem (e)))
|
||||
malloc_printerr ("malloc(): unaligned tcache chunk detected");
|
||||
|
||||
void *ne = e == NULL ? NULL : REVEAL_PTR (e->next);
|
||||
if (!mangled)
|
||||
*ep = ne;
|
||||
*ep = REVEAL_PTR (e->next);
|
||||
else
|
||||
*ep = PROTECT_PTR (ep, ne);
|
||||
*ep = PROTECT_PTR (ep, REVEAL_PTR (e->next));
|
||||
|
||||
++(tcache->num_slots[tc_idx]);
|
||||
e->key = 0;
|
||||
@@ -3229,7 +3244,7 @@ tcache_put (mchunkptr chunk, size_t tc_idx)
|
||||
static __always_inline void *
|
||||
tcache_get (size_t tc_idx)
|
||||
{
|
||||
return tcache_get_n (tc_idx, & tcache->entries[tc_idx], false);
|
||||
return tcache_get_n (tc_idx, &tcache->entries[tc_idx], false);
|
||||
}
|
||||
|
||||
static __always_inline tcache_entry **
|
||||
@@ -5152,7 +5167,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
|
||||
INTERNAL_SIZE_T size;
|
||||
|
||||
nb = checked_request2size (bytes);
|
||||
if (nb == 0)
|
||||
if (nb == 0 || alignment > PTRDIFF_MAX)
|
||||
{
|
||||
__set_errno (ENOMEM);
|
||||
return NULL;
|
||||
@@ -5168,7 +5183,10 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
|
||||
we don't find anything in those bins, the common malloc code will
|
||||
scan starting at 2x. */
|
||||
|
||||
/* Call malloc with worst case padding to hit alignment. */
|
||||
/* Call malloc with worst case padding to hit alignment. ALIGNMENT is a
|
||||
power of 2, so it tops out at (PTRDIFF_MAX >> 1) + 1, leaving plenty of
|
||||
space to add MINSIZE and whatever checked_request2size adds to BYTES to
|
||||
get NB. Consequently, total below also does not overflow. */
|
||||
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
|
||||
|
||||
if (m == NULL)
|
||||
@@ -5587,15 +5605,13 @@ do_set_arena_max (size_t value)
|
||||
static __always_inline int
|
||||
do_set_tcache_max (size_t value)
|
||||
{
|
||||
if (value > PTRDIFF_MAX)
|
||||
return 0;
|
||||
|
||||
size_t nb = request2size (value);
|
||||
size_t tc_idx = csize2tidx (nb);
|
||||
|
||||
/* To check that value is not too big and request2size does not return an
|
||||
overflown value. */
|
||||
if (value > nb)
|
||||
return 0;
|
||||
|
||||
if (nb > MAX_TCACHE_SMALL_SIZE)
|
||||
if (tc_idx >= TCACHE_SMALL_BINS)
|
||||
tc_idx = large_csize2tidx (nb);
|
||||
|
||||
LIBC_PROBE (memory_tunable_tcache_max_bytes, 2, value, mp_.tcache_max_bytes);
|
||||
@@ -5604,7 +5620,7 @@ do_set_tcache_max (size_t value)
|
||||
{
|
||||
if (tc_idx < TCACHE_SMALL_BINS)
|
||||
mp_.tcache_small_bins = tc_idx + 1;
|
||||
mp_.tcache_max_bytes = nb;
|
||||
mp_.tcache_max_bytes = nb + 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ test_large_allocations (size_t size)
|
||||
}
|
||||
|
||||
|
||||
static long pagesize;
|
||||
|
||||
/* This function tests the following aligned memory allocation functions
|
||||
using several valid alignments and precedes each allocation test with a
|
||||
@@ -171,8 +170,8 @@ test_large_aligned_allocations (size_t size)
|
||||
|
||||
/* All aligned memory allocation functions expect an alignment that is a
|
||||
power of 2. Given this, we test each of them with every valid
|
||||
alignment from 1 thru PAGESIZE. */
|
||||
for (align = 1; align <= pagesize; align *= 2)
|
||||
alignment for the type of ALIGN, i.e. until it wraps to 0. */
|
||||
for (align = 1; align > 0; align <<= 1)
|
||||
{
|
||||
test_setup ();
|
||||
#if __GNUC_PREREQ (7, 0)
|
||||
@@ -265,11 +264,6 @@ do_test (void)
|
||||
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
|
||||
#endif
|
||||
|
||||
/* Aligned memory allocation functions need to be tested up to alignment
|
||||
size equivalent to page size, which should be a power of 2. */
|
||||
pagesize = sysconf (_SC_PAGESIZE);
|
||||
TEST_VERIFY_EXIT (powerof2 (pagesize));
|
||||
|
||||
/* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
|
||||
in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
|
||||
|
||||
|
||||
+11
-20
@@ -85,6 +85,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdalign.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <search.h>
|
||||
@@ -406,12 +407,13 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
|
||||
int cmp;
|
||||
node *rootp = (node *) vrootp;
|
||||
node root, unchained;
|
||||
/* Stack of nodes so we remember the parents without recursion. It's
|
||||
_very_ unlikely that there are paths longer than 40 nodes. The tree
|
||||
would need to have around 250.000 nodes. */
|
||||
int stacksize = 40;
|
||||
/* Stack of nodes so we remember the parents without recursion. The
|
||||
stack size is a conservative approximation of the maximum height
|
||||
of a red-black tree, based on size of the address space.
|
||||
Actual numbers are closer to 57 (32 bit) and 117 (63 bit). */
|
||||
enum { stacksize = 2 * UINTPTR_WIDTH };
|
||||
int sp = 0;
|
||||
node **nodestack = alloca (sizeof (node *) * stacksize);
|
||||
node *nodestack[stacksize];
|
||||
|
||||
if (rootp == NULL)
|
||||
return NULL;
|
||||
@@ -424,14 +426,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
|
||||
root = DEREFNODEPTR(rootp);
|
||||
while ((cmp = (*compar) (key, root->key)) != 0)
|
||||
{
|
||||
if (sp == stacksize)
|
||||
{
|
||||
node **newstack;
|
||||
stacksize += 20;
|
||||
newstack = alloca (sizeof (node *) * stacksize);
|
||||
nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
|
||||
}
|
||||
|
||||
assert (sp < stacksize);
|
||||
nodestack[sp++] = rootp;
|
||||
p = DEREFNODEPTR(rootp);
|
||||
if (cmp < 0)
|
||||
@@ -470,13 +465,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
|
||||
node upn;
|
||||
for (;;)
|
||||
{
|
||||
if (sp == stacksize)
|
||||
{
|
||||
node **newstack;
|
||||
stacksize += 20;
|
||||
newstack = alloca (sizeof (node *) * stacksize);
|
||||
nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
|
||||
}
|
||||
assert (sp < stacksize);
|
||||
nodestack[sp++] = parentp;
|
||||
parentp = up;
|
||||
upn = DEREFNODEPTR(up);
|
||||
@@ -541,6 +530,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
|
||||
SETNODEPTR(pp,q);
|
||||
/* Make sure pp is right if the case below tries to use
|
||||
it. */
|
||||
assert (sp < stacksize);
|
||||
nodestack[sp++] = pp = LEFTPTR(q);
|
||||
q = RIGHT(p);
|
||||
}
|
||||
@@ -625,6 +615,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
|
||||
SETLEFT(p,RIGHT(q));
|
||||
SETRIGHT(q,p);
|
||||
SETNODEPTR(pp,q);
|
||||
assert (sp < stacksize);
|
||||
nodestack[sp++] = pp = RIGHTPTR(q);
|
||||
q = LEFT(p);
|
||||
}
|
||||
|
||||
@@ -109,9 +109,8 @@ do_test_with_invalid_iov (void)
|
||||
static void
|
||||
do_test_with_invalid_flags (void)
|
||||
{
|
||||
/* Set the next bit from the mask of all supported flags. */
|
||||
int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2;
|
||||
invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
|
||||
/* Set all the bits that are not used by the supported flags. */
|
||||
int invalid_flag = ~RWF_SUPPORTED;
|
||||
|
||||
char buf[32];
|
||||
const struct iovec vec = { .iov_base = buf, .iov_len = sizeof (buf) };
|
||||
|
||||
+17
-5
@@ -240,7 +240,7 @@ setup_stack_prot (char *mem, size_t size, struct pthread *pd,
|
||||
/* Update the guard area of the thread stack MEM of size SIZE with the new
|
||||
GUARDISZE. It uses the method defined by PD stack_mode. */
|
||||
static inline bool
|
||||
adjust_stack_prot (char *mem, size_t size, const struct pthread *pd,
|
||||
adjust_stack_prot (char *mem, size_t size, struct pthread *pd,
|
||||
size_t guardsize, size_t pagesize_m1)
|
||||
{
|
||||
/* The required guard area is larger than the current one. For
|
||||
@@ -258,11 +258,23 @@ adjust_stack_prot (char *mem, size_t size, const struct pthread *pd,
|
||||
so use the new guard placement with the new size. */
|
||||
if (guardsize > pd->guardsize)
|
||||
{
|
||||
/* There was no need to previously setup a guard page, so we need
|
||||
to check whether the kernel supports guard advise. */
|
||||
char *guard = guard_position (mem, size, guardsize, pd, pagesize_m1);
|
||||
if (pd->stack_mode == ALLOCATE_GUARD_MADV_GUARD)
|
||||
return __madvise (guard, guardsize, MADV_GUARD_INSTALL) == 0;
|
||||
else if (pd->stack_mode == ALLOCATE_GUARD_PROT_NONE)
|
||||
return __mprotect (guard, guardsize, PROT_NONE) == 0;
|
||||
if (atomic_load_relaxed (&allocate_stack_mode)
|
||||
== ALLOCATE_GUARD_MADV_GUARD)
|
||||
{
|
||||
if (__madvise (guard, guardsize, MADV_GUARD_INSTALL) == 0)
|
||||
{
|
||||
pd->stack_mode = ALLOCATE_GUARD_MADV_GUARD;
|
||||
return true;
|
||||
}
|
||||
atomic_store_relaxed (&allocate_stack_mode,
|
||||
ALLOCATE_GUARD_PROT_NONE);
|
||||
}
|
||||
|
||||
pd->stack_mode = ALLOCATE_GUARD_PROT_NONE;
|
||||
return __mprotect (guard, guardsize, PROT_NONE) == 0;
|
||||
}
|
||||
/* The current guard area is larger than the required one. For
|
||||
_STACK_GROWS_DOWN is means change the guard as:
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@ __syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
|
||||
__syscall_arg_t a5, __syscall_arg_t a6,
|
||||
__SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr)
|
||||
{
|
||||
int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
|
||||
__SYSCALL_CANCEL7_ARG nr);
|
||||
long int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
|
||||
__SYSCALL_CANCEL7_ARG nr);
|
||||
return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r))
|
||||
? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r))
|
||||
: r;
|
||||
|
||||
@@ -48,7 +48,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;
|
||||
@@ -71,7 +72,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. */
|
||||
|
||||
+40
-20
@@ -21,6 +21,7 @@
|
||||
#include <setjmp.h>
|
||||
#include <stackinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/check.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xsignal.h>
|
||||
@@ -202,7 +203,7 @@ tf (void *closure)
|
||||
|
||||
/* Test 1: caller provided stack without guard. */
|
||||
static void
|
||||
do_test1 (void)
|
||||
do_test1 (void *closure)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
@@ -227,7 +228,7 @@ do_test1 (void)
|
||||
|
||||
/* Test 2: same as 1., but with a guard area. */
|
||||
static void
|
||||
do_test2 (void)
|
||||
do_test2 (void *closure)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
@@ -250,18 +251,9 @@ do_test2 (void)
|
||||
xmunmap (stack, stacksize);
|
||||
}
|
||||
|
||||
/* Test 3: pthread_create with default values. */
|
||||
/* Test 3: pthread_create without a guard area. */
|
||||
static void
|
||||
do_test3 (void)
|
||||
{
|
||||
pthread_t t = xpthread_create (NULL, tf, NULL);
|
||||
void *status = xpthread_join (t);
|
||||
TEST_VERIFY (status == 0);
|
||||
}
|
||||
|
||||
/* Test 4: pthread_create without a guard area. */
|
||||
static void
|
||||
do_test4 (void)
|
||||
do_test3 (void *closure)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
@@ -277,9 +269,18 @@ do_test4 (void)
|
||||
xpthread_attr_destroy (&attr);
|
||||
}
|
||||
|
||||
/* Test 4: pthread_create with default values. */
|
||||
static void
|
||||
do_test4 (void *closure)
|
||||
{
|
||||
pthread_t t = xpthread_create (NULL, tf, NULL);
|
||||
void *status = xpthread_join (t);
|
||||
TEST_VERIFY (status == 0);
|
||||
}
|
||||
|
||||
/* Test 5: pthread_create with non default stack and guard size value. */
|
||||
static void
|
||||
do_test5 (void)
|
||||
do_test5 (void *closure)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
@@ -299,7 +300,7 @@ do_test5 (void)
|
||||
test 3, but with a larger guard area. The pthread_create will need to
|
||||
increase the guard area. */
|
||||
static void
|
||||
do_test6 (void)
|
||||
do_test6 (void *closure)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
xpthread_attr_init (&attr);
|
||||
@@ -320,7 +321,7 @@ do_test6 (void)
|
||||
pthread_create should use the cached stack from previous tests, but it
|
||||
would require to reduce the guard area. */
|
||||
static void
|
||||
do_test7 (void)
|
||||
do_test7 (void *closure)
|
||||
{
|
||||
pthread_t t = xpthread_create (NULL, tf, NULL);
|
||||
void *status = xpthread_join (t);
|
||||
@@ -346,21 +347,40 @@ do_test (void)
|
||||
|
||||
static const struct {
|
||||
const char *descr;
|
||||
void (*test)(void);
|
||||
void (*test) (void *);
|
||||
} tests[] = {
|
||||
{ "user provided stack without guard", do_test1 },
|
||||
{ "user provided stack with guard", do_test2 },
|
||||
{ "default attribute", do_test3 },
|
||||
{ "default attribute without guard", do_test4 },
|
||||
/* N.B: do_test3 should be before do_test4 to check if a new thread
|
||||
that uses the thread stack previously allocated without a guard
|
||||
page correctly sets up the guard pages even on a kernel without
|
||||
MADV_GUARD_INSTALL support (BZ 33356). */
|
||||
{ "default attribute without guard", do_test3 },
|
||||
{ "default attribute", do_test4 },
|
||||
/* Also checks if the guard is correctly removed from the cache thread
|
||||
stack. */
|
||||
{ "default attribute without guard", do_test3 },
|
||||
{ "non default stack and guard sizes", do_test5 },
|
||||
{ "reused stack with larger guard", do_test6 },
|
||||
{ "reused stack with smaller guard", do_test7 },
|
||||
};
|
||||
|
||||
/* Run each test with a clean state. */
|
||||
for (int i = 0; i < array_length (tests); i++)
|
||||
{
|
||||
printf ("debug: fork: test%01d: %s\n", i, tests[i].descr);
|
||||
struct support_capture_subprocess result =
|
||||
support_capture_subprocess (tests[i].test, NULL);
|
||||
support_capture_subprocess_check (&result, tests[i].descr, 0,
|
||||
sc_allow_none);
|
||||
support_capture_subprocess_free (&result);
|
||||
}
|
||||
|
||||
/* And now run the same tests along with the thread stack cache. */
|
||||
for (int i = 0; i < array_length (tests); i++)
|
||||
{
|
||||
printf ("debug: test%01d: %s\n", i, tests[i].descr);
|
||||
tests[i].test();
|
||||
tests[i].test ( NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -326,6 +326,7 @@ tests := \
|
||||
tst-gshadow \
|
||||
tst-nss-getpwent \
|
||||
tst-nss-hash \
|
||||
tst-nss-malloc-failure-getlogin_r \
|
||||
tst-nss-test1 \
|
||||
tst-nss-test2 \
|
||||
tst-nss-test4 \
|
||||
|
||||
+7
-11
@@ -157,19 +157,15 @@ __merge_einval (LOOKUP_TYPE *a,
|
||||
|
||||
#define CHECK_MERGE(err, status) \
|
||||
({ \
|
||||
do \
|
||||
if (err) \
|
||||
{ \
|
||||
if (err) \
|
||||
{ \
|
||||
__set_errno (err); \
|
||||
if (err == ERANGE) \
|
||||
status = NSS_STATUS_TRYAGAIN; \
|
||||
else \
|
||||
status = NSS_STATUS_UNAVAIL; \
|
||||
break; \
|
||||
} \
|
||||
__set_errno (err); \
|
||||
if (err == ERANGE) \
|
||||
status = NSS_STATUS_TRYAGAIN; \
|
||||
else \
|
||||
status = NSS_STATUS_UNAVAIL; \
|
||||
break; \
|
||||
} \
|
||||
while (0); \
|
||||
})
|
||||
|
||||
/* Type of the lookup function we need here. */
|
||||
|
||||
+13
-8
@@ -56,7 +56,6 @@ global_state_allocate (void *closure)
|
||||
{
|
||||
result->data.nsswitch_conf.size = -1; /* Force reload. */
|
||||
memset (result->data.services, 0, sizeof (result->data.services));
|
||||
result->data.initialized = true;
|
||||
result->data.reload_disabled = false;
|
||||
__libc_lock_init (result->lock);
|
||||
result->root_ino = 0;
|
||||
@@ -251,9 +250,12 @@ __nss_configure_lookup (const char *dbname, const char *service_line)
|
||||
|
||||
/* Force any load/cache/read whatever to happen, so we can override
|
||||
it. */
|
||||
__nss_database_get (db, &result);
|
||||
if (!__nss_database_get (db, &result))
|
||||
return -1;
|
||||
|
||||
local = nss_database_state_get ();
|
||||
if (local == NULL)
|
||||
return -1;
|
||||
|
||||
result = __nss_action_parse (service_line);
|
||||
if (result == NULL)
|
||||
@@ -451,8 +453,8 @@ nss_database_check_reload_and_get (struct nss_database_state *local,
|
||||
/* Avoid overwriting the global configuration until we have loaded
|
||||
everything successfully. Otherwise, if the file change
|
||||
information changes back to what is in the global configuration,
|
||||
the lookups would use the partially-written configuration. */
|
||||
struct nss_database_data staging = { .initialized = true, };
|
||||
the lookups would use the partially-written configuration. */
|
||||
struct nss_database_data staging = { };
|
||||
|
||||
bool ok = nss_database_reload (&staging, &initial);
|
||||
|
||||
@@ -478,6 +480,8 @@ bool
|
||||
__nss_database_get (enum nss_database db, nss_action_list *actions)
|
||||
{
|
||||
struct nss_database_state *local = nss_database_state_get ();
|
||||
if (local == NULL)
|
||||
return false;
|
||||
return nss_database_check_reload_and_get (local, actions, db);
|
||||
}
|
||||
libc_hidden_def (__nss_database_get)
|
||||
@@ -503,7 +507,7 @@ __nss_database_freeres (void)
|
||||
}
|
||||
|
||||
void
|
||||
__nss_database_fork_prepare_parent (struct nss_database_data *data)
|
||||
__nss_database_fork_prepare_parent (struct nss_database_for_fork *data)
|
||||
{
|
||||
/* Do not use allocate_once to trigger loading unnecessarily. */
|
||||
struct nss_database_state *local = atomic_load_acquire (&global_database_state);
|
||||
@@ -515,20 +519,21 @@ __nss_database_fork_prepare_parent (struct nss_database_data *data)
|
||||
because it avoids acquiring the lock during the actual
|
||||
fork. */
|
||||
__libc_lock_lock (local->lock);
|
||||
*data = local->data;
|
||||
data->data = local->data;
|
||||
__libc_lock_unlock (local->lock);
|
||||
data->initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__nss_database_fork_subprocess (struct nss_database_data *data)
|
||||
__nss_database_fork_subprocess (struct nss_database_for_fork *data)
|
||||
{
|
||||
struct nss_database_state *local = atomic_load_acquire (&global_database_state);
|
||||
if (data->initialized)
|
||||
{
|
||||
/* Restore the state at the point of the fork. */
|
||||
assert (local != NULL);
|
||||
local->data = *data;
|
||||
local->data = data->data;
|
||||
__libc_lock_init (local->lock);
|
||||
}
|
||||
else if (local != NULL)
|
||||
|
||||
+9
-3
@@ -70,15 +70,21 @@ struct nss_database_data
|
||||
struct file_change_detection nsswitch_conf;
|
||||
nss_action_list services[NSS_DATABASE_COUNT];
|
||||
int reload_disabled; /* Actually bool; int for atomic access. */
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/* Use to store a consistent state snapshot across fork. */
|
||||
struct nss_database_for_fork
|
||||
{
|
||||
bool initialized; /* Set to true if the data field below is initialized. */
|
||||
struct nss_database_data data;
|
||||
};
|
||||
|
||||
/* Called by fork in the parent process, before forking. */
|
||||
void __nss_database_fork_prepare_parent (struct nss_database_data *data)
|
||||
void __nss_database_fork_prepare_parent (struct nss_database_for_fork *)
|
||||
attribute_hidden;
|
||||
|
||||
/* Called by fork in the new subprocess, after forking. */
|
||||
void __nss_database_fork_subprocess (struct nss_database_data *data)
|
||||
void __nss_database_fork_subprocess (struct nss_database_for_fork *)
|
||||
attribute_hidden;
|
||||
|
||||
#endif /* _NSS_DATABASE_H */
|
||||
|
||||
@@ -36,16 +36,16 @@ LINE_PARSER
|
||||
unsigned int number;
|
||||
|
||||
if (cnt < 5)
|
||||
INT_FIELD (number, ISCOLON , 0, 16, (unsigned int))
|
||||
INT_FIELD (number, ISCOLON , false, 16, (unsigned int))
|
||||
else
|
||||
INT_FIELD (number, isspace, 1, 16, (unsigned int))
|
||||
INT_FIELD (number, isspace, true, 16, (unsigned int))
|
||||
|
||||
if (number > 0xff)
|
||||
return 0;
|
||||
result->e_addr.ether_addr_octet[cnt] = number;
|
||||
}
|
||||
};
|
||||
STRING_FIELD (result->e_name, isspace, 1);
|
||||
STRING_FIELD (result->e_name, isspace, true);
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ LINE_PARSER
|
||||
{
|
||||
char *addr;
|
||||
|
||||
STRING_FIELD (addr, isspace, 1);
|
||||
STRING_FIELD (addr, isspace, true);
|
||||
|
||||
/* Parse address. */
|
||||
if (__inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
|
||||
@@ -96,7 +96,7 @@ LINE_PARSER
|
||||
entdata->h_addr_ptrs[1] = NULL;
|
||||
result->h_addr_list = entdata->h_addr_ptrs;
|
||||
|
||||
STRING_FIELD (result->h_name, isspace, 1);
|
||||
STRING_FIELD (result->h_name, isspace, true);
|
||||
})
|
||||
|
||||
#define EXTRA_ARGS_VALUE , AF_INET, 0
|
||||
|
||||
@@ -38,9 +38,9 @@ LINE_PARSER
|
||||
char *cp;
|
||||
int n = 1;
|
||||
|
||||
STRING_FIELD (result->n_name, isspace, 1);
|
||||
STRING_FIELD (result->n_name, isspace, true);
|
||||
|
||||
STRING_FIELD (addr, isspace, 1);
|
||||
STRING_FIELD (addr, isspace, true);
|
||||
/* 'inet_network' does not add zeroes at the end if the network number
|
||||
does not contain four byte values. We shift result ourselves if
|
||||
necessary. */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <nss_files.h>
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ struct protoent_data {};
|
||||
#include "files-parse.c"
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->p_name, isspace, 1);
|
||||
INT_FIELD (result->p_proto, isspace, 1, 10,);
|
||||
STRING_FIELD (result->p_name, isspace, true);
|
||||
INT_FIELD (result->p_proto, isspace, true, 10,);
|
||||
)
|
||||
|
||||
#include GENERIC
|
||||
|
||||
@@ -29,8 +29,8 @@ struct rpcent_data {};
|
||||
#include "files-parse.c"
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->r_name, isspace, 1);
|
||||
INT_FIELD (result->r_number, isspace, 1, 10,);
|
||||
STRING_FIELD (result->r_name, isspace, true);
|
||||
INT_FIELD (result->r_number, isspace, true, 10,);
|
||||
)
|
||||
|
||||
#include GENERIC
|
||||
|
||||
@@ -31,9 +31,9 @@ struct servent_data {};
|
||||
#define ISSLASH(c) ((c) == '/')
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->s_name, isspace, 1);
|
||||
INT_FIELD (result->s_port, ISSLASH, 10, 0, htons);
|
||||
STRING_FIELD (result->s_proto, isspace, 1);
|
||||
STRING_FIELD (result->s_name, isspace, true);
|
||||
INT_FIELD (result->s_port, ISSLASH, false, 10, htons);
|
||||
STRING_FIELD (result->s_proto, isspace, true);
|
||||
)
|
||||
|
||||
#include GENERIC
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/* Test NSS/getlogin_r with injected allocation failures (bug 28940).
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <netdb.h>
|
||||
#include <nss.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xstdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* This test calls getpwuid_r via getlogin_r (on Linux).
|
||||
|
||||
This test uses the NSS system configuration to exercise that code
|
||||
path. It means that it can fail (crash) if malloc failure is not
|
||||
handled by NSS modules for the passwd database. */
|
||||
|
||||
/* Data structure allocated via MAP_SHARED, so that writes from the
|
||||
subprocess are visible. */
|
||||
struct shared_data
|
||||
{
|
||||
/* Number of tracked allocations performed so far. */
|
||||
volatile unsigned int allocation_count;
|
||||
|
||||
/* If this number is reached, one allocation fails. */
|
||||
volatile unsigned int failing_allocation;
|
||||
|
||||
/* The number of allocations performed during initialization
|
||||
(before the actual getlogin_r call). */
|
||||
volatile unsigned int init_allocation_count;
|
||||
|
||||
/* Error code of an expected getlogin_r failure. */
|
||||
volatile int expected_failure;
|
||||
|
||||
/* The subprocess stores the expected name here. */
|
||||
char name[100];
|
||||
};
|
||||
|
||||
/* Allocation count in shared mapping. */
|
||||
static struct shared_data *shared;
|
||||
|
||||
/* Returns true if a failure should be injected for this allocation. */
|
||||
static bool
|
||||
fail_this_allocation (void)
|
||||
{
|
||||
if (shared != NULL)
|
||||
{
|
||||
unsigned int count = shared->allocation_count;
|
||||
shared->allocation_count = count + 1;
|
||||
return count == shared->failing_allocation;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Failure-injecting wrappers for allocation functions used by glibc. */
|
||||
|
||||
void *
|
||||
malloc (size_t size)
|
||||
{
|
||||
if (fail_this_allocation ())
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
extern __typeof (malloc) __libc_malloc;
|
||||
return __libc_malloc (size);
|
||||
}
|
||||
|
||||
void *
|
||||
calloc (size_t a, size_t b)
|
||||
{
|
||||
if (fail_this_allocation ())
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
extern __typeof (calloc) __libc_calloc;
|
||||
return __libc_calloc (a, b);
|
||||
}
|
||||
|
||||
void *
|
||||
realloc (void *ptr, size_t size)
|
||||
{
|
||||
if (fail_this_allocation ())
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
extern __typeof (realloc) __libc_realloc;
|
||||
return __libc_realloc (ptr, size);
|
||||
}
|
||||
|
||||
/* No-op subprocess to verify that support_isolate_in_subprocess does
|
||||
not perform any heap allocations. */
|
||||
static void
|
||||
no_op (void *ignored)
|
||||
{
|
||||
}
|
||||
|
||||
/* Perform a getlogin_r call in a subprocess, to obtain the number of
|
||||
allocations used and the expected result of a successful call. */
|
||||
static void
|
||||
initialize (void *configure_lookup)
|
||||
{
|
||||
shared->init_allocation_count = 0;
|
||||
if (configure_lookup != NULL)
|
||||
{
|
||||
TEST_COMPARE (__nss_configure_lookup ("passwd", configure_lookup), 0);
|
||||
shared->init_allocation_count = shared->allocation_count;
|
||||
}
|
||||
|
||||
shared->name[0] = '\0';
|
||||
int ret = getlogin_r (shared->name, sizeof (shared->name));
|
||||
if (ret != 0)
|
||||
{
|
||||
printf ("info: getlogin_r failed: %s (%d)\n",
|
||||
strerrorname_np (ret), ret);
|
||||
shared->expected_failure = ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
shared->expected_failure = 0;
|
||||
if (shared->name[0] == '\0')
|
||||
FAIL ("error: getlogin_r succeeded without result\n");
|
||||
else
|
||||
printf ("info: getlogin_r: \"%s\"\n", shared->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform getlogin_r in a subprocess with fault injection. */
|
||||
static void
|
||||
test_in_subprocess (void *configure_lookup)
|
||||
{
|
||||
if (configure_lookup != NULL
|
||||
&& __nss_configure_lookup ("passwd", configure_lookup) < 0)
|
||||
{
|
||||
printf ("info: __nss_configure_lookup failed: %s (%d)\n",
|
||||
strerrorname_np (errno), errno);
|
||||
TEST_COMPARE (errno, ENOMEM);
|
||||
TEST_VERIFY (shared->allocation_count <= shared->init_allocation_count);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int inject_at = shared->failing_allocation;
|
||||
char name[sizeof (shared->name)] = "name not set";
|
||||
int ret = getlogin_r (name, sizeof (name));
|
||||
shared->failing_allocation = ~0U;
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
TEST_COMPARE (shared->expected_failure, 0);
|
||||
TEST_COMPARE_STRING (name, shared->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("info: allocation %u failure results in error %s (%d)\n",
|
||||
inject_at, strerrorname_np (ret), ret);
|
||||
|
||||
if (ret != ENOMEM)
|
||||
{
|
||||
if (shared->expected_failure != 0)
|
||||
TEST_COMPARE (ret, shared->expected_failure);
|
||||
else if (configure_lookup == NULL)
|
||||
/* The ENOENT failure can happen due to an issue related
|
||||
to bug 22041: dlopen failure does not result in ENOMEM. */
|
||||
TEST_COMPARE (ret, ENOENT);
|
||||
else
|
||||
FAIL ("unexpected getlogin_r error");
|
||||
}
|
||||
}
|
||||
|
||||
if (shared->expected_failure == 0)
|
||||
{
|
||||
/* The second call should succeed. */
|
||||
puts ("info: about to perform second getlogin_r call");
|
||||
ret = getlogin_r (name, sizeof (name));
|
||||
if (configure_lookup == NULL)
|
||||
{
|
||||
/* This check can fail due to bug 22041 if the malloc error
|
||||
injection causes a failure internally in dlopen. */
|
||||
if (ret != 0)
|
||||
{
|
||||
printf ("warning: second getlogin_r call failed with %s (%d)\n",
|
||||
strerrorname_np (ret), ret);
|
||||
TEST_COMPARE (ret, ENOENT);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* If __nss_configure_lookup has been called, the error caching
|
||||
bug does not happen because nss_files is built-in, and the
|
||||
second getlogin_r is expected to succeed. */
|
||||
TEST_COMPARE (ret, 0);
|
||||
if (ret == 0)
|
||||
TEST_COMPARE_STRING (name, shared->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set by the --failing-allocation command line option. Together with
|
||||
--direct, this can be used to trigger an allocation failure in the
|
||||
original process, which may help with debugging. */
|
||||
static int option_failing_allocation = -1;
|
||||
|
||||
/* Set by --override, to be used with --failing-allocation. Turns on
|
||||
the __nss_configure_lookup call for passwd/files, which is disabled
|
||||
by default. */
|
||||
static int option_override = 0;
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char files[] = "files";
|
||||
|
||||
if (option_failing_allocation >= 0)
|
||||
{
|
||||
/* The test was invoked with --failing-allocation. Perform just
|
||||
one test, using the original nsswitch.conf. This is a
|
||||
condensed version of the probing/testing loop below. */
|
||||
printf ("info: testing with failing allocation %d\n",
|
||||
option_failing_allocation);
|
||||
shared = support_shared_allocate (sizeof (*shared));
|
||||
shared->failing_allocation = ~0U;
|
||||
char *configure_lookup = option_override ? files : NULL;
|
||||
support_isolate_in_subprocess (initialize, configure_lookup);
|
||||
shared->allocation_count = 0;
|
||||
shared->failing_allocation = option_failing_allocation;
|
||||
test_in_subprocess (configure_lookup); /* No subprocess. */
|
||||
support_shared_free (shared);
|
||||
shared = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool any_success = false;
|
||||
|
||||
for (int do_configure_lookup = 0; do_configure_lookup < 2;
|
||||
++do_configure_lookup)
|
||||
{
|
||||
if (do_configure_lookup)
|
||||
puts ("info: testing with nsswitch.conf override");
|
||||
else
|
||||
puts ("info: testing with original nsswitch.conf");
|
||||
|
||||
char *configure_lookup = do_configure_lookup ? files : NULL;
|
||||
|
||||
shared = support_shared_allocate (sizeof (*shared));
|
||||
|
||||
/* Disable fault injection. */
|
||||
shared->failing_allocation = ~0U;
|
||||
|
||||
support_isolate_in_subprocess (no_op, NULL);
|
||||
TEST_COMPARE (shared->allocation_count, 0);
|
||||
|
||||
support_isolate_in_subprocess (initialize, configure_lookup);
|
||||
|
||||
if (shared->name[0] != '\0')
|
||||
any_success = true;
|
||||
|
||||
/* The number of allocations in the successful case. Once the
|
||||
number of expected allocations is exceeded, injecting further
|
||||
failures does not make a difference (assuming that the number
|
||||
of malloc calls is deterministic). */
|
||||
unsigned int maximum_allocation_count = shared->allocation_count;
|
||||
printf ("info: initial getlogin_r performed %u allocations\n",
|
||||
maximum_allocation_count);
|
||||
|
||||
for (unsigned int inject_at = 0; inject_at <= maximum_allocation_count;
|
||||
++inject_at)
|
||||
{
|
||||
printf ("info: running fault injection at allocation %u\n",
|
||||
inject_at);
|
||||
shared->allocation_count = 0;
|
||||
shared->failing_allocation = inject_at;
|
||||
support_isolate_in_subprocess (test_in_subprocess, configure_lookup);
|
||||
}
|
||||
|
||||
support_shared_free (shared);
|
||||
shared = NULL;
|
||||
}
|
||||
|
||||
{
|
||||
FILE *fp = fopen (_PATH_NSSWITCH_CONF, "r");
|
||||
if (fp == NULL)
|
||||
printf ("info: no %s file\n", _PATH_NSSWITCH_CONF);
|
||||
else
|
||||
{
|
||||
printf ("info: %s contents follows\n", _PATH_NSSWITCH_CONF);
|
||||
int last_ch = '\n';
|
||||
while (true)
|
||||
{
|
||||
int ch = fgetc (fp);
|
||||
if (ch == EOF)
|
||||
break;
|
||||
putchar (ch);
|
||||
last_ch = ch;
|
||||
}
|
||||
if (last_ch != '\n')
|
||||
putchar ('\n');
|
||||
printf ("(end of %s contents)\n", _PATH_NSSWITCH_CONF);
|
||||
xfclose (fp);
|
||||
}
|
||||
}
|
||||
|
||||
support_record_failure_barrier ();
|
||||
|
||||
if (!any_success)
|
||||
FAIL_UNSUPPORTED ("no successful getlogin_r calls");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cmdline_process (int c)
|
||||
{
|
||||
if (c == 'F')
|
||||
option_failing_allocation = atoi (optarg);
|
||||
}
|
||||
|
||||
#define CMDLINE_OPTIONS \
|
||||
{ "failing-allocation", required_argument, NULL, 'F' }, \
|
||||
{ "override", no_argument, &option_override, 1 },
|
||||
|
||||
#define CMDLINE_PROCESS cmdline_process
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -326,7 +326,9 @@ tests := \
|
||||
tst-wait3 \
|
||||
tst-wait4 \
|
||||
tst-waitid \
|
||||
tst-wordexp-append \
|
||||
tst-wordexp-nocmd \
|
||||
tst-wordexp-reuse \
|
||||
tstgetopt \
|
||||
# tests
|
||||
|
||||
@@ -355,6 +357,7 @@ tests-internal := \
|
||||
tests-container := \
|
||||
bug-ga2 \
|
||||
tst-vfork3 \
|
||||
tst-wordexp-tilde \
|
||||
# tests-container
|
||||
|
||||
tests-time64 := \
|
||||
@@ -457,6 +460,8 @@ generated += \
|
||||
tst-rxspencer-no-utf8.mtrace \
|
||||
tst-vfork3-mem.out \
|
||||
tst-vfork3.mtrace \
|
||||
tst-wordexp-reuse-mem.out \
|
||||
tst-wordexp-reuse.mtrace \
|
||||
# generated
|
||||
endif
|
||||
endif
|
||||
@@ -492,6 +497,7 @@ tests-special += \
|
||||
$(objpfx)tst-pcre-mem.out \
|
||||
$(objpfx)tst-rxspencer-no-utf8-mem.out \
|
||||
$(objpfx)tst-vfork3-mem.out \
|
||||
$(objpfx)tst-wordexp-reuse-mem.out \
|
||||
# tests-special
|
||||
endif
|
||||
endif
|
||||
@@ -775,3 +781,10 @@ $(objpfx)posix-conf-vars-def.h: $(..)scripts/gen-posix-conf-vars.awk \
|
||||
$(make-target-directory)
|
||||
$(AWK) -f $(filter-out Makefile, $^) > $@.tmp
|
||||
mv -f $@.tmp $@
|
||||
|
||||
tst-wordexp-reuse-ENV += MALLOC_TRACE=$(objpfx)tst-wordexp-reuse.mtrace \
|
||||
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
|
||||
|
||||
$(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ __libc_fork (void)
|
||||
|
||||
lastrun = __run_prefork_handlers (multiple_threads);
|
||||
|
||||
struct nss_database_data nss_database_data;
|
||||
struct nss_database_for_fork nss_database_data;
|
||||
|
||||
/* If we are not running multiple threads, we do not have to
|
||||
preserve lock state. If fork runs from a signal handler, only
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/* Test for wordexp with WRDE_APPEND flag.
|
||||
Copyright (C) 2026 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 <wordexp.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static unsigned int relocating_reallocs;
|
||||
|
||||
/* w_addword grows we_wordv with realloc, make every call guaranteed to
|
||||
relocate the block. This makes BZ 34090 regression more deterministic. */
|
||||
void *
|
||||
realloc (void *ptr, size_t size)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return malloc (size);
|
||||
if (size == 0)
|
||||
{
|
||||
free (ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *new = malloc (size);
|
||||
if (new == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Copy only what is valid in the old block to avoid reading past it. */
|
||||
size_t old = malloc_usable_size (ptr);
|
||||
memcpy (new, ptr, old < size ? old : size);
|
||||
/* Clobber the old block so that a stale we_wordv pointer restored on the
|
||||
error path reads garbage instead of the old contents, which might
|
||||
otherwise survive intact and mask the bug. */
|
||||
memset (ptr, 0x5a, old);
|
||||
free (ptr);
|
||||
relocating_reallocs++;
|
||||
return new;
|
||||
}
|
||||
|
||||
/* Verify that all words in we match the expected NULL-terminated
|
||||
array. */
|
||||
static void
|
||||
check_words (const wordexp_t *we, const char *const *expected)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; expected[i] != NULL; i++)
|
||||
{
|
||||
TEST_VERIFY (i < we->we_wordc);
|
||||
TEST_COMPARE_STRING (we->we_wordv[we->we_offs + i], expected[i]);
|
||||
}
|
||||
TEST_COMPARE (we->we_wordc, i);
|
||||
}
|
||||
|
||||
#define CHECK_WORDS(we, ...) \
|
||||
do { \
|
||||
const char *const expected_[] = { __VA_ARGS__, NULL }; \
|
||||
check_words (we, expected_); \
|
||||
} while (0)
|
||||
|
||||
/* Test 1: WRDE_APPEND + WRDE_BADCHAR preserves we_wordc. */
|
||||
static void
|
||||
test_append_badchar_preserves_count (void)
|
||||
{
|
||||
printf ("info: test_append_badchar_preserves_count\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("one two three", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 3);
|
||||
|
||||
size_t saved_count = we.we_wordc;
|
||||
|
||||
/* ')' triggers WRDE_BADCHAR and "extra" would be a new word if the
|
||||
expansion succeeded, exercising the w_addword path before the error
|
||||
is detected. */
|
||||
TEST_COMPARE (wordexp ("extra )", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 2: WRDE_APPEND + WRDE_BADCHAR preserves the we_wordv pointer even
|
||||
when internal realloc would move the buffer. */
|
||||
static void
|
||||
test_append_badchar_preserves_pointer (void)
|
||||
{
|
||||
printf ("info: test_append_badchar_preserves_pointer\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
/* Use many words so that the initial we_wordv allocation is
|
||||
non-trivial and a later realloc is more likely to move it. */
|
||||
TEST_COMPARE (wordexp ("a b c d e f g h", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 8);
|
||||
|
||||
char **saved_wordv = we.we_wordv;
|
||||
size_t saved_count = we.we_wordc;
|
||||
unsigned int saved_reallocs = relocating_reallocs;
|
||||
|
||||
/* The interposed realloc guarantees the internal we_wordv buffer moves
|
||||
during parsing, so the pointer-stability check below is meaningful. */
|
||||
TEST_COMPARE (wordexp ("append )", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
/* Verify that a relocating realloc actually happened during the failed
|
||||
call, otherwise the pointer-stability check is vacuous. */
|
||||
TEST_VERIFY (relocating_reallocs > saved_reallocs);
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
TEST_VERIFY (we.we_wordv == saved_wordv);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 3: After a failed WRDE_APPEND the original words are still accessible
|
||||
and correct. */
|
||||
static void
|
||||
test_append_badchar_words_intact (void)
|
||||
{
|
||||
printf ("info: test_append_badchar_words_intact\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("alpha beta gamma", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "alpha", "beta", "gamma");
|
||||
|
||||
TEST_COMPARE (wordexp ("delta )", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
|
||||
/* Words must still be intact. */
|
||||
CHECK_WORDS (&we, "alpha", "beta", "gamma");
|
||||
/* The NULL terminator must still be present. */
|
||||
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 4: Successful WRDE_APPEND still works (regression test). */
|
||||
static void
|
||||
test_append_success (void)
|
||||
{
|
||||
printf ("info: test_append_success\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("hello", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
|
||||
char **saved_wordv = we.we_wordv;
|
||||
|
||||
TEST_COMPARE (wordexp ("world", &we, WRDE_APPEND), 0);
|
||||
TEST_COMPARE (we.we_wordc, 2);
|
||||
/* A successful append works on a fresh copy of the array, so the
|
||||
caller-visible pointer must have changed. */
|
||||
TEST_VERIFY (we.we_wordv != saved_wordv);
|
||||
CHECK_WORDS (&we, "hello", "world");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 5: Successful append after a failed append — the implementation must
|
||||
recover and allow further use of the wordexp_t. */
|
||||
static void
|
||||
test_append_success_after_failure (void)
|
||||
{
|
||||
printf ("info: test_append_success_after_failure\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("first", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "first");
|
||||
|
||||
TEST_COMPARE (wordexp ("bad |", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
|
||||
/* State must be exactly as before the failed call. */
|
||||
CHECK_WORDS (&we, "first");
|
||||
|
||||
/* A subsequent successful append must work. */
|
||||
TEST_COMPARE (wordexp ("second third", &we, WRDE_APPEND), 0);
|
||||
CHECK_WORDS (&we, "first", "second", "third");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 6: Multiple consecutive failed appends do not corrupt state. */
|
||||
static void
|
||||
test_append_multiple_failures (void)
|
||||
{
|
||||
printf ("info: test_append_multiple_failures\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("keep this", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "keep", "this");
|
||||
|
||||
size_t saved_count = we.we_wordc;
|
||||
char **saved_wordv = we.we_wordv;
|
||||
|
||||
/* Each of these bad characters must leave the state unchanged. */
|
||||
TEST_COMPARE (wordexp ("x )", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (wordexp ("x |", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (wordexp ("x ;", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (wordexp ("x &", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (wordexp ("x <", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (wordexp ("x >", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
TEST_VERIFY (we.we_wordv == saved_wordv);
|
||||
CHECK_WORDS (&we, "keep", "this");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 7: WRDE_APPEND with WRDE_SYNTAX error (unterminated quote) also
|
||||
preserves state. */
|
||||
static void
|
||||
test_append_syntax_error (void)
|
||||
{
|
||||
printf ("info: test_append_syntax_error\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("original", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "original");
|
||||
|
||||
char **saved_wordv = we.we_wordv;
|
||||
size_t saved_count = we.we_wordc;
|
||||
|
||||
/* Unterminated double quote triggers WRDE_SYNTAX. */
|
||||
TEST_COMPARE (wordexp ("\"unterminated", &we, WRDE_APPEND), WRDE_SYNTAX);
|
||||
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
TEST_VERIFY (we.we_wordv == saved_wordv);
|
||||
CHECK_WORDS (&we, "original");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 8: Error without WRDE_APPEND still works (regression test for the
|
||||
non-APPEND code path in do_error). */
|
||||
static void
|
||||
test_no_append_error (void)
|
||||
{
|
||||
printf ("info: test_no_append_error\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
/* Simple failure without WRDE_APPEND. */
|
||||
TEST_COMPARE (wordexp ("bad |", &we, 0), WRDE_BADCHAR);
|
||||
|
||||
/* After failure without WRDE_APPEND the struct should be safe to
|
||||
reuse — start fresh. */
|
||||
TEST_COMPARE (wordexp ("ok", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "ok");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 9: WRDE_BADCHAR on the very first character (no partial words added
|
||||
before the error). */
|
||||
static void
|
||||
test_append_badchar_immediate (void)
|
||||
{
|
||||
printf ("info: test_append_badchar_immediate\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
TEST_COMPARE (wordexp ("hello world", &we, 0), 0);
|
||||
CHECK_WORDS (&we, "hello", "world");
|
||||
|
||||
char **saved_wordv = we.we_wordv;
|
||||
size_t saved_count = we.we_wordc;
|
||||
|
||||
/* The bad character is the very first byte — no w_addword call happens
|
||||
before the error. */
|
||||
TEST_COMPARE (wordexp ("|", &we, WRDE_APPEND), WRDE_BADCHAR);
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
TEST_VERIFY (we.we_wordv == saved_wordv);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 10: WRDE_APPEND into an empty wordexp_t (initial call uses WRDE_APPEND
|
||||
with a zeroed struct — unusual but allowed). */
|
||||
static void
|
||||
test_append_into_empty (void)
|
||||
{
|
||||
printf ("info: test_append_into_empty\n");
|
||||
wordexp_t we = { 0 };
|
||||
|
||||
/* First call with WRDE_APPEND on a zeroed struct. The implementation
|
||||
must handle we_wordv == NULL gracefully. */
|
||||
TEST_COMPARE (wordexp ("solo", &we, WRDE_APPEND), 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
CHECK_WORDS (&we, "solo");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Verify that the leading we_offs slots are all NULL. */
|
||||
static void
|
||||
check_offs_null (const wordexp_t *we)
|
||||
{
|
||||
for (size_t i = 0; i < we->we_offs; i++)
|
||||
TEST_VERIFY (we->we_wordv[i] == NULL);
|
||||
}
|
||||
|
||||
/* Test 11: successful WRDE_APPEND with WRDE_DOOFFS and a non-zero we_offs.
|
||||
The leading offset slots must stay NULL and words must land at
|
||||
we_wordv[we_offs + i] across both the initial and the appended call. */
|
||||
static void
|
||||
test_dooffs_append_success (void)
|
||||
{
|
||||
printf ("info: test_dooffs_append_success\n");
|
||||
wordexp_t we = { 0 };
|
||||
we.we_offs = 2;
|
||||
|
||||
TEST_COMPARE (wordexp ("one two", &we, WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (we.we_offs, 2);
|
||||
check_offs_null (&we);
|
||||
CHECK_WORDS (&we, "one", "two");
|
||||
|
||||
TEST_COMPARE (wordexp ("three", &we, WRDE_APPEND | WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (we.we_offs, 2);
|
||||
check_offs_null (&we);
|
||||
CHECK_WORDS (&we, "one", "two", "three");
|
||||
/* The NULL terminator must sit right after the last word. */
|
||||
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 12: failed WRDE_APPEND with WRDE_DOOFFS preserves we_wordc, the
|
||||
we_wordv pointer, the words and the leading NULL offset slots. This
|
||||
exercises the we_offs arithmetic in the array duplication and in the
|
||||
error-path cleanup (we_wordv[we_offs + --we_wordc]). */
|
||||
static void
|
||||
test_dooffs_append_error_preserves_state (void)
|
||||
{
|
||||
printf ("info: test_dooffs_append_error_preserves_state\n");
|
||||
wordexp_t we = { 0 };
|
||||
we.we_offs = 3;
|
||||
|
||||
TEST_COMPARE (wordexp ("alpha beta", &we, WRDE_DOOFFS), 0);
|
||||
check_offs_null (&we);
|
||||
CHECK_WORDS (&we, "alpha", "beta");
|
||||
|
||||
char **saved_wordv = we.we_wordv;
|
||||
size_t saved_count = we.we_wordc;
|
||||
unsigned int saved_reallocs = relocating_reallocs;
|
||||
|
||||
/* "gamma" is a partial word added via w_addword (forcing a relocating
|
||||
realloc of we_wordv) before ')' triggers WRDE_BADCHAR. */
|
||||
TEST_COMPARE (wordexp ("gamma )", &we, WRDE_APPEND | WRDE_DOOFFS),
|
||||
WRDE_BADCHAR);
|
||||
TEST_VERIFY (relocating_reallocs > saved_reallocs);
|
||||
|
||||
TEST_COMPARE (we.we_offs, 3);
|
||||
TEST_COMPARE (we.we_wordc, saved_count);
|
||||
TEST_VERIFY (we.we_wordv == saved_wordv);
|
||||
check_offs_null (&we);
|
||||
CHECK_WORDS (&we, "alpha", "beta");
|
||||
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
test_append_badchar_preserves_count ();
|
||||
test_append_badchar_preserves_pointer ();
|
||||
test_append_badchar_words_intact ();
|
||||
test_append_success ();
|
||||
test_append_success_after_failure ();
|
||||
test_append_multiple_failures ();
|
||||
test_append_syntax_error ();
|
||||
test_no_append_error ();
|
||||
test_append_badchar_immediate ();
|
||||
test_append_into_empty ();
|
||||
test_dooffs_append_success ();
|
||||
test_dooffs_append_error_preserves_state ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Test for wordexp with WRDE_REUSE flag.
|
||||
Copyright (C) 2026 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 <wordexp.h>
|
||||
#include <mcheck.h>
|
||||
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
|
||||
{
|
||||
wordexp_t p = { 0 };
|
||||
TEST_COMPARE (wordexp ("one", &p, 0), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[0], "one");
|
||||
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[0], "two");
|
||||
wordfree (&p);
|
||||
}
|
||||
|
||||
{
|
||||
wordexp_t p = { .we_offs = 2 };
|
||||
TEST_COMPARE (wordexp ("one", &p, 0), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[0], "one");
|
||||
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
|
||||
wordfree (&p);
|
||||
}
|
||||
|
||||
{
|
||||
wordexp_t p = { 0 };
|
||||
TEST_COMPARE (wordexp ("one", &p, 0), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[0], "one");
|
||||
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_APPEND), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[0], "two");
|
||||
wordfree (&p);
|
||||
}
|
||||
|
||||
{
|
||||
wordexp_t p = { .we_offs = 2 };
|
||||
TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
|
||||
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
|
||||
| WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
|
||||
wordfree (&p);
|
||||
}
|
||||
|
||||
{
|
||||
wordexp_t p = { .we_offs = 2 };
|
||||
TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
|
||||
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
|
||||
| WRDE_DOOFFS | WRDE_APPEND), 0);
|
||||
TEST_COMPARE (p.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
|
||||
wordfree (&p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,244 @@
|
||||
/* Test wordexp tilde expansion with large usernames (BZ 34091).
|
||||
Copyright (C) 2026 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 <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wordexp.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <support/namespace.h>
|
||||
|
||||
typedef void (*func_callback_t)(void);
|
||||
|
||||
static void
|
||||
subprocess_small_stack (void *closure)
|
||||
{
|
||||
struct rlimit rl;
|
||||
TEST_COMPARE (getrlimit (RLIMIT_STACK, &rl), 0);
|
||||
rl.rlim_cur = 512 * 1024;
|
||||
TEST_COMPARE (setrlimit (RLIMIT_STACK, &rl), 0);
|
||||
|
||||
func_callback_t func_test = closure;
|
||||
func_test ();
|
||||
}
|
||||
|
||||
/* Build a string "~<padding>/tail" where <padding> is LEN bytes of the
|
||||
character CH. The caller must free the result. */
|
||||
static char *
|
||||
make_tilde_input (char ch, size_t len, const char *tail)
|
||||
{
|
||||
/* ~ + len + / + tail + \0 */
|
||||
size_t taillen = tail != NULL ? strlen (tail) : 0;
|
||||
size_t total = 1 + len + 1 + taillen + 1;
|
||||
char *buf = xmalloc (total);
|
||||
buf[0] = '~';
|
||||
memset (buf + 1, ch, len);
|
||||
buf[1 + len] = '/';
|
||||
if (tail != NULL)
|
||||
memcpy (buf + 1 + len + 1, tail, taillen);
|
||||
buf[total - 1] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Test 1: A very long username must not crash. The username will not match
|
||||
any real user, so wordexp returns ~<long>/rest. */
|
||||
static void
|
||||
test_long_username (void)
|
||||
{
|
||||
printf ("info: test_long_username_no_crash\n");
|
||||
|
||||
static const char REST[] = "rest";
|
||||
|
||||
/* 1 MiB username — well beyond any reasonable stack frame. */
|
||||
const size_t long_len = 1024 * 1024;
|
||||
char *input = make_tilde_input ('A', long_len, REST);
|
||||
|
||||
wordexp_t we = { 0 };
|
||||
int ret = wordexp (input, &we, 0);
|
||||
/* The (non-existent) username is invalid, so wordexp falls back to
|
||||
literal output: ~AAA…/rest. */
|
||||
TEST_COMPARE (ret, 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
|
||||
/* Verify prefix: '~' followed by long_len 'A's. */
|
||||
const char *result = we.we_wordv[0];
|
||||
TEST_COMPARE (result[0], '~');
|
||||
TEST_COMPARE (strlen (result),
|
||||
1 /* ~ */ + long_len + sizeof (REST));
|
||||
for (size_t j = 1; j <= long_len; j++)
|
||||
if (result[j] != 'A')
|
||||
{
|
||||
printf (" mismatch at position %zu: expected 'A', got '%c'\n",
|
||||
j, result[j]);
|
||||
support_record_failure ();
|
||||
break;
|
||||
}
|
||||
/* Verify the tail after the username. */
|
||||
TEST_COMPARE_STRING (result + 1 + long_len, "/rest");
|
||||
|
||||
wordfree (&we);
|
||||
free (input);
|
||||
}
|
||||
|
||||
/* Test 2: A username that just exceeds the default scratch_buffer inline
|
||||
size (1024 bytes) exercises the scratch_buffer_set_array_size growth path
|
||||
without being excessively large. */
|
||||
static void
|
||||
test_scratch_buffer_growth (void)
|
||||
{
|
||||
printf ("info: test_scratch_buffer_growth\n");
|
||||
|
||||
const size_t len = 2048;
|
||||
char *input = make_tilde_input ('x', len, NULL);
|
||||
|
||||
wordexp_t we = { 0 };
|
||||
int ret = wordexp (input, &we, 0);
|
||||
TEST_COMPARE (ret, 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
|
||||
/* ~xxx…/ — the trailing slash makes a separate empty component, but
|
||||
wordexp merges it into the single token ~xxx…/. */
|
||||
const char *result = we.we_wordv[0];
|
||||
TEST_COMPARE (result[0], '~');
|
||||
for (size_t j = 1; j <= len; j++)
|
||||
if (result[j] != 'x')
|
||||
{
|
||||
printf (" mismatch at position %zu\n", j);
|
||||
support_record_failure ();
|
||||
break;
|
||||
}
|
||||
TEST_COMPARE (result[1 + len], '/');
|
||||
|
||||
wordfree (&we);
|
||||
free (input);
|
||||
}
|
||||
|
||||
/* Test 3: ~root still resolves to the correct home directory through the
|
||||
__getpwnam_r path. */
|
||||
static void
|
||||
test_known_user (void)
|
||||
{
|
||||
printf ("info: test_known_user\n");
|
||||
|
||||
/* Look up root's home directory for comparison. */
|
||||
struct passwd *pw = getpwnam ("root");
|
||||
if (pw == NULL || pw->pw_dir == NULL)
|
||||
{
|
||||
printf (" SKIP: cannot look up root\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *expected = xasprintf ("%s/file", pw->pw_dir);
|
||||
|
||||
wordexp_t we = { 0 };
|
||||
TEST_COMPARE (wordexp ("~root/file", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (we.we_wordv[0], expected);
|
||||
|
||||
wordfree (&we);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
/* Test 4: Bare tilde expands to $HOME. */
|
||||
static void
|
||||
test_bare_tilde (void)
|
||||
{
|
||||
printf ("info: test_bare_tilde\n");
|
||||
|
||||
const char *home = getenv ("HOME");
|
||||
if (home == NULL)
|
||||
{
|
||||
printf (" SKIP: HOME is not set\n");
|
||||
return;
|
||||
}
|
||||
|
||||
wordexp_t we = { 0 };
|
||||
TEST_COMPARE (wordexp ("~", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (we.we_wordv[0], home);
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 5: Short non-existent username falls back to literal ~username output,
|
||||
exercising the invalid-login-name path. */
|
||||
static void
|
||||
test_unknown_user (void)
|
||||
{
|
||||
printf ("info: test_unknown_user\n");
|
||||
|
||||
/* Pick a username that is extremely unlikely to exist. */
|
||||
wordexp_t we = { 0 };
|
||||
TEST_COMPARE (wordexp ("~no_such_user_xyzzy42", &we, 0), 0);
|
||||
TEST_COMPARE (we.we_wordc, 1);
|
||||
TEST_COMPARE_STRING (we.we_wordv[0], "~no_such_user_xyzzy42");
|
||||
|
||||
wordfree (&we);
|
||||
}
|
||||
|
||||
/* Test 6: Tilde with username and WRDE_APPEND — exercises parse_tilde's
|
||||
interaction with the WRDE_APPEND word list. */
|
||||
static void
|
||||
test_tilde_with_append (void)
|
||||
{
|
||||
printf ("info: test_tilde_with_append\n");
|
||||
|
||||
const char *home = getenv ("HOME");
|
||||
if (home == NULL)
|
||||
{
|
||||
printf (" SKIP: HOME is not set\n");
|
||||
return;
|
||||
}
|
||||
|
||||
wordexp_t we = { 0 };
|
||||
TEST_COMPARE (wordexp ("first", &we, 0), 0);
|
||||
|
||||
TEST_COMPARE (wordexp ("~/path", &we, WRDE_APPEND), 0);
|
||||
TEST_COMPARE (we.we_wordc, 2);
|
||||
TEST_COMPARE_STRING (we.we_wordv[0], "first");
|
||||
|
||||
char *expected = xasprintf ("%s/path", home);
|
||||
TEST_COMPARE_STRING (we.we_wordv[1], expected);
|
||||
|
||||
wordfree (&we);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
test_known_user ();
|
||||
test_bare_tilde ();
|
||||
test_unknown_user ();
|
||||
test_tilde_with_append ();
|
||||
|
||||
support_isolate_in_subprocess (subprocess_small_stack,
|
||||
test_long_username);
|
||||
|
||||
support_isolate_in_subprocess (subprocess_small_stack,
|
||||
test_scratch_buffer_growth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1 @@
|
||||
root:x:0:
|
||||
@@ -0,0 +1,3 @@
|
||||
passwd: files
|
||||
group: files
|
||||
shadow: files
|
||||
@@ -0,0 +1 @@
|
||||
root:x:0:0:root:/root:/bin/sh
|
||||
+77
-15
@@ -335,17 +335,29 @@ parse_tilde (char **word, size_t *word_length, size_t *max_length,
|
||||
else
|
||||
{
|
||||
/* Look up user name in database to get home directory */
|
||||
char *user = strndupa (&words[1 + *offset], i - (1 + *offset));
|
||||
struct passwd pwd, *tpwd;
|
||||
int result;
|
||||
size_t userlen = i - (1 + *offset);
|
||||
/* tmpbuf contains both the user and the __getpwnam_r working area. */
|
||||
struct scratch_buffer tmpbuf;
|
||||
scratch_buffer_init (&tmpbuf);
|
||||
if (!scratch_buffer_set_array_size (&tmpbuf, userlen + 1, 1))
|
||||
return WRDE_NOSPACE;
|
||||
char *user = tmpbuf.data;
|
||||
memcpy (user, &words[1 + *offset], userlen);
|
||||
user[userlen] = '\0';
|
||||
|
||||
while ((result = __getpwnam_r (user, &pwd, tmpbuf.data, tmpbuf.length,
|
||||
struct passwd pwd, *tpwd;
|
||||
int result;
|
||||
while ((result = __getpwnam_r (user,
|
||||
&pwd,
|
||||
tmpbuf.data + userlen + 1,
|
||||
tmpbuf.length - userlen - 1,
|
||||
&tpwd)) != 0
|
||||
&& errno == ERANGE)
|
||||
if (!scratch_buffer_grow (&tmpbuf))
|
||||
return WRDE_NOSPACE;
|
||||
{
|
||||
if (!scratch_buffer_grow_preserve (&tmpbuf))
|
||||
return WRDE_NOSPACE;
|
||||
user = tmpbuf.data;
|
||||
}
|
||||
|
||||
if (result == 0 && tpwd != NULL && pwd.pw_dir)
|
||||
*word = w_addstr (*word, word_length, max_length, pwd.pw_dir);
|
||||
@@ -2212,11 +2224,19 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
char ifs_white[4];
|
||||
wordexp_t old_word = *pwordexp;
|
||||
|
||||
/* When WRDE_APPEND is set we work on a copy of the we_wordv array so that
|
||||
the caller's original pointer is never invalidated by realloc inside
|
||||
w_addword. The saved_wordv keeps the original; on success we free it,
|
||||
on non-NOSPACE error we free the working copy and restore the original. */
|
||||
char **saved_wordv = NULL;
|
||||
|
||||
if (flags & WRDE_REUSE)
|
||||
{
|
||||
/* Minimal implementation of WRDE_REUSE for now */
|
||||
wordfree (pwordexp);
|
||||
old_word.we_wordc = 0;
|
||||
old_word.we_wordv = NULL;
|
||||
pwordexp->we_wordc = 0;
|
||||
}
|
||||
|
||||
if ((flags & WRDE_APPEND) == 0)
|
||||
@@ -2244,6 +2264,21 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
pwordexp->we_offs = 0;
|
||||
}
|
||||
}
|
||||
else if (pwordexp->we_wordv != NULL)
|
||||
{
|
||||
/* WRDE_APPEND with an existing word list: duplicate the array so that
|
||||
realloc during parsing does not invalidate the caller's pointer. The
|
||||
strings themselves are shared an the array already holds
|
||||
'we_offs + we_wordc + 1 pointers' (so the size computation cannot
|
||||
overflow). */
|
||||
size_t num_p = pwordexp->we_offs + pwordexp->we_wordc + 1;
|
||||
char **dup = malloc (num_p * sizeof (char *));
|
||||
if (dup == NULL)
|
||||
return WRDE_NOSPACE;
|
||||
memcpy (dup, pwordexp->we_wordv, num_p * sizeof (char *));
|
||||
saved_wordv = pwordexp->we_wordv;
|
||||
pwordexp->we_wordv = dup;
|
||||
}
|
||||
|
||||
/* Find out what the field separators are.
|
||||
* There are two types: whitespace and non-whitespace.
|
||||
@@ -2324,7 +2359,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
error = w_addword (pwordexp, NULL);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
goto do_error;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -2342,7 +2377,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
error = w_addword (pwordexp, NULL);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
goto do_error;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -2408,10 +2443,18 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
|
||||
/* There was a word separator at the end */
|
||||
if (word == NULL) /* i.e. w_newword */
|
||||
return 0;
|
||||
{
|
||||
free (saved_wordv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* There was no field separator at the end */
|
||||
return w_addword (pwordexp, word);
|
||||
/* There was no field separator at the end. The only possible error
|
||||
from w_addword is WRDE_NOSPACE. */
|
||||
error = w_addword (pwordexp, word);
|
||||
if (error != 0)
|
||||
goto do_error;
|
||||
free (saved_wordv);
|
||||
return 0;
|
||||
|
||||
do_error:
|
||||
/* Error:
|
||||
@@ -2422,11 +2465,30 @@ do_error:
|
||||
free (word);
|
||||
|
||||
if (error == WRDE_NOSPACE)
|
||||
return WRDE_NOSPACE;
|
||||
{
|
||||
/* we_wordc and we_wordv are updated to reflect any words that were
|
||||
successfully expanded. The old array is obsolete. */
|
||||
free (saved_wordv);
|
||||
return WRDE_NOSPACE;
|
||||
}
|
||||
|
||||
if ((flags & WRDE_APPEND) == 0)
|
||||
wordfree (pwordexp);
|
||||
if (flags & WRDE_APPEND)
|
||||
{
|
||||
/* POSIX 2024 states that for in other error cases, if the WRDE_APPEND
|
||||
flag was specified, we_wordc and we_wordv shall not be modified.
|
||||
|
||||
Free strings appended during this call, discard the working copy of
|
||||
we_wordv, and restore the caller's original pointer. */
|
||||
while (pwordexp->we_wordc > old_word.we_wordc)
|
||||
free (pwordexp->we_wordv[pwordexp->we_offs + --pwordexp->we_wordc]);
|
||||
free (pwordexp->we_wordv);
|
||||
pwordexp->we_wordv = saved_wordv;
|
||||
}
|
||||
else
|
||||
{
|
||||
wordfree (pwordexp);
|
||||
*pwordexp = old_word;
|
||||
}
|
||||
|
||||
*pwordexp = old_word;
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -98,14 +98,17 @@ tests += \
|
||||
tst-ns_name \
|
||||
tst-ns_name_compress \
|
||||
tst-ns_name_pton \
|
||||
tst-ns_sprintrr \
|
||||
tst-res_hconf_reorder \
|
||||
tst-res_hnok \
|
||||
tst-resolv-aliases \
|
||||
tst-resolv-basic \
|
||||
tst-resolv-binary \
|
||||
tst-resolv-byaddr \
|
||||
tst-resolv-dns-section \
|
||||
tst-resolv-edns \
|
||||
tst-resolv-invalid-cname \
|
||||
tst-resolv-invalid-ptr \
|
||||
tst-resolv-network \
|
||||
tst-resolv-noaaaa \
|
||||
tst-resolv-noaaaa-vc \
|
||||
@@ -115,6 +118,7 @@ tests += \
|
||||
tst-resolv-semi-failure \
|
||||
tst-resolv-short-response \
|
||||
tst-resolv-trailing \
|
||||
# tests
|
||||
|
||||
# This test calls __res_context_send directly, which is not exported
|
||||
# from libresolv.
|
||||
@@ -293,6 +297,8 @@ $(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-dns-section: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so
|
||||
@@ -302,6 +308,8 @@ $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-invalid-ptr: $(objpfx)libresolv.so \
|
||||
$(shared-thread-library)
|
||||
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
@@ -324,5 +332,6 @@ $(objpfx)tst-ns_name: $(objpfx)libresolv.so
|
||||
$(objpfx)tst-ns_name.out: tst-ns_name.data
|
||||
$(objpfx)tst-ns_name_compress: $(objpfx)libresolv.so
|
||||
$(objpfx)tst-ns_name_pton: $(objpfx)libresolv.so
|
||||
$(objpfx)tst-ns_sprintrr: $(objpfx)libresolv.so
|
||||
$(objpfx)tst-res_hnok: $(objpfx)libresolv.so
|
||||
$(objpfx)tst-p_secstodate: $(objpfx)libresolv.so
|
||||
|
||||
+53
-117
@@ -78,6 +78,24 @@ ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
|
||||
}
|
||||
libresolv_hidden_def (ns_sprintrr)
|
||||
|
||||
/* Writes the class/type symbol NUMBER to *BUF, using the name from
|
||||
*SYMS if possible. If NUMBER is not found in *SYMS, print the
|
||||
number with PREFIX. */
|
||||
static int
|
||||
addsym (const struct res_sym *syms, int number, const char *prefix,
|
||||
char **buf, size_t *buflen)
|
||||
{
|
||||
for (; syms->name != NULL; syms++)
|
||||
if (number == syms->number)
|
||||
{
|
||||
T (addstr (" ", 1, buf, buflen));
|
||||
return addstr (syms->name, strlen (syms->name), buf, buflen);
|
||||
}
|
||||
char tmp[20];
|
||||
int len = snprintf (tmp, sizeof (tmp), " %s%d", prefix, number);
|
||||
return addstr (tmp, len, buf, buflen);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Convert the fields of an RR into presentation format.
|
||||
*
|
||||
@@ -97,7 +115,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
|
||||
const char *comment;
|
||||
char tmp[100];
|
||||
char errbuf[40];
|
||||
int len, x;
|
||||
|
||||
/*
|
||||
@@ -128,11 +145,21 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
/*
|
||||
* TTL, Class, Type.
|
||||
*/
|
||||
T(x = ns_format_ttl(ttl, buf, buflen));
|
||||
addlen(x, &buf, &buflen);
|
||||
len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
|
||||
{
|
||||
char *start = buf;
|
||||
|
||||
T (x = ns_format_ttl (ttl, buf, buflen));
|
||||
addlen (x, &buf, &buflen);
|
||||
T (addsym (__p_class_syms, class, "CLASS", &buf, &buflen));
|
||||
if (type == ns_t_a6)
|
||||
/* A6 is not part of __p_type_syms, which is exported.
|
||||
Adding A6 there would change its size. Handle it here. */
|
||||
T (addstr (" A6", 3, &buf, &buflen));
|
||||
else
|
||||
T (addsym (__p_type_syms, type, "TYPE", &buf, &buflen));
|
||||
|
||||
T (spaced = addtab(buf - start, 16, spaced, &buf, &buflen));
|
||||
}
|
||||
|
||||
/*
|
||||
* RData.
|
||||
@@ -140,8 +167,9 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
switch (type) {
|
||||
case ns_t_a:
|
||||
if (rdlen != (size_t)NS_INADDRSZ)
|
||||
goto formerr;
|
||||
(void) inet_ntop(AF_INET, rdata, buf, buflen);
|
||||
goto formerr;
|
||||
if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL)
|
||||
return -1;
|
||||
addlen(strlen(buf), &buf, &buflen);
|
||||
break;
|
||||
|
||||
@@ -307,16 +335,18 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
}
|
||||
|
||||
case ns_t_aaaa:
|
||||
if (rdlen != (size_t)NS_IN6ADDRSZ)
|
||||
goto formerr;
|
||||
(void) inet_ntop(AF_INET6, rdata, buf, buflen);
|
||||
if (rdlen != (size_t)NS_IN6ADDRSZ)
|
||||
goto formerr;
|
||||
if (inet_ntop (AF_INET6, rdata, buf, buflen) == NULL)
|
||||
return -1;
|
||||
addlen(strlen(buf), &buf, &buflen);
|
||||
break;
|
||||
|
||||
case ns_t_loc: {
|
||||
char t[255];
|
||||
|
||||
/* XXX protocol format checking? */
|
||||
if (rdlen != 16)
|
||||
goto formerr;
|
||||
(void) loc_ntoa(rdata, t);
|
||||
T(addstr(t, strlen(t), &buf, &buflen));
|
||||
break;
|
||||
@@ -400,7 +430,8 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
goto formerr;
|
||||
|
||||
/* Address. */
|
||||
(void) inet_ntop(AF_INET, rdata, buf, buflen);
|
||||
if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL)
|
||||
return -1;
|
||||
addlen(strlen(buf), &buf, &buflen);
|
||||
rdata += NS_INADDRSZ;
|
||||
|
||||
@@ -434,96 +465,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
break;
|
||||
}
|
||||
|
||||
case ns_t_cert: {
|
||||
u_int c_type, key_tag, alg;
|
||||
int n;
|
||||
unsigned int siz;
|
||||
char base64_cert[8192], tmp[40];
|
||||
const char *leader;
|
||||
|
||||
c_type = ns_get16(rdata); rdata += NS_INT16SZ;
|
||||
key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
|
||||
alg = (u_int) *rdata++;
|
||||
|
||||
len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
|
||||
if (siz > sizeof(base64_cert) * 3/4) {
|
||||
const char *str = "record too long to print";
|
||||
T(addstr(str, strlen(str), &buf, &buflen));
|
||||
}
|
||||
else {
|
||||
len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
|
||||
|
||||
if (len < 0)
|
||||
goto formerr;
|
||||
else if (len > 15) {
|
||||
T(addstr(" (", 2, &buf, &buflen));
|
||||
leader = "\n\t\t";
|
||||
spaced = 0;
|
||||
}
|
||||
else
|
||||
leader = " ";
|
||||
|
||||
for (n = 0; n < len; n += 48) {
|
||||
T(addstr(leader, strlen(leader),
|
||||
&buf, &buflen));
|
||||
T(addstr(base64_cert + n, MIN(len - n, 48),
|
||||
&buf, &buflen));
|
||||
}
|
||||
if (len > 15)
|
||||
T(addstr(" )", 2, &buf, &buflen));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ns_t_tkey: {
|
||||
/* KJD - need to complete this */
|
||||
u_long t;
|
||||
int mode, err, keysize;
|
||||
|
||||
/* Algorithm name. */
|
||||
T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
|
||||
T(addstr(" ", 1, &buf, &buflen));
|
||||
|
||||
/* Inception. */
|
||||
t = ns_get32(rdata); rdata += NS_INT32SZ;
|
||||
len = SPRINTF((tmp, "%lu ", t));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
|
||||
/* Expiration. */
|
||||
t = ns_get32(rdata); rdata += NS_INT32SZ;
|
||||
len = SPRINTF((tmp, "%lu ", t));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
|
||||
/* Mode , Error, Key Size. */
|
||||
/* Priority, Weight, Port. */
|
||||
mode = ns_get16(rdata); rdata += NS_INT16SZ;
|
||||
err = ns_get16(rdata); rdata += NS_INT16SZ;
|
||||
keysize = ns_get16(rdata); rdata += NS_INT16SZ;
|
||||
len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
|
||||
/* XXX need to dump key, print otherdata length & other data */
|
||||
break;
|
||||
}
|
||||
|
||||
case ns_t_tsig: {
|
||||
/* BEW - need to complete this */
|
||||
int n;
|
||||
|
||||
T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
|
||||
T(addstr(" ", 1, &buf, &buflen));
|
||||
rdata += 8; /*%< time */
|
||||
n = ns_get16(rdata); rdata += INT16SZ;
|
||||
rdata += n; /*%< sig */
|
||||
n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */
|
||||
sprintf(buf, "%d", ns_get16(rdata));
|
||||
rdata += INT16SZ;
|
||||
addlen(strlen(buf), &buf, &buflen);
|
||||
break;
|
||||
}
|
||||
|
||||
case ns_t_a6: {
|
||||
struct in6_addr a;
|
||||
int pbyte, pbit;
|
||||
@@ -539,12 +480,14 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
|
||||
/* address suffix: provided only when prefix len != 128 */
|
||||
if (pbit < 128) {
|
||||
if (rdata + pbyte >= edata) goto formerr;
|
||||
unsigned int bytelen = sizeof(a) - pbyte;
|
||||
if (edata - rdata < bytelen) goto formerr;
|
||||
memset(&a, 0, sizeof(a));
|
||||
memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
|
||||
(void) inet_ntop(AF_INET6, &a, buf, buflen);
|
||||
memcpy(&a.s6_addr[pbyte], rdata, bytelen);
|
||||
if (inet_ntop (AF_INET6, &a, buf, buflen) == NULL)
|
||||
return -1;
|
||||
addlen(strlen(buf), &buf, &buflen);
|
||||
rdata += sizeof(a) - pbyte;
|
||||
rdata += bytelen;
|
||||
}
|
||||
|
||||
/* prefix name: provided only when prefix len > 0 */
|
||||
@@ -557,25 +500,18 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
|
||||
break;
|
||||
}
|
||||
|
||||
case ns_t_opt: {
|
||||
len = SPRINTF((tmp, "%u bytes", class));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
snprintf (errbuf, sizeof (errbuf), "unknown RR type %d", type);
|
||||
comment = errbuf;
|
||||
comment = "";
|
||||
goto hexify;
|
||||
}
|
||||
return (buf - obuf);
|
||||
formerr:
|
||||
comment = "RR format error";
|
||||
comment = " ; RR format error";
|
||||
hexify: {
|
||||
int n, m;
|
||||
char *p;
|
||||
|
||||
len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata),
|
||||
len = SPRINTF((tmp, "\\# %u%s%s", (unsigned)(edata - rdata),
|
||||
rdlen != 0U ? " (" : "", comment));
|
||||
T(addstr(tmp, len, &buf, &buflen));
|
||||
while (rdata < edata) {
|
||||
|
||||
@@ -820,7 +820,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
|
||||
/* expected_name may be updated to point into this buffer. */
|
||||
unsigned char name_buffer[NS_MAXCDNAME];
|
||||
|
||||
while (ancount > 0)
|
||||
for (; ancount > 0; --ancount)
|
||||
{
|
||||
struct ns_rr_wire rr;
|
||||
if (!__ns_rr_cursor_next (&c, &rr))
|
||||
@@ -866,7 +866,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
|
||||
char hname[MAXHOSTNAMELEN + 1];
|
||||
if (__ns_name_unpack (c.begin, c.end, rr.rdata,
|
||||
name_buffer, sizeof (name_buffer)) < 0
|
||||
|| !__res_binary_hnok (expected_name)
|
||||
|| !__res_binary_hnok (name_buffer)
|
||||
|| __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0)
|
||||
{
|
||||
*h_errnop = NO_RECOVERY;
|
||||
|
||||
@@ -207,6 +207,10 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
|
||||
sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
|
||||
net_bytes[1], net_bytes[0]);
|
||||
break;
|
||||
default:
|
||||
/* Default network (net is originally zero). */
|
||||
strcpy (qbuf, "0.0.0.0.in-addr.arpa");
|
||||
break;
|
||||
}
|
||||
|
||||
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
|
||||
|
||||
@@ -390,8 +390,6 @@ p_fqname(const u_char *cp, const u_char *msg, FILE *file) {
|
||||
* that C_ANY is a qclass but not a class. (You can ask for records of class
|
||||
* C_ANY, but you can't have any records of that class in the database.)
|
||||
*/
|
||||
extern const struct res_sym __p_class_syms[];
|
||||
libresolv_hidden_proto (__p_class_syms)
|
||||
const struct res_sym __p_class_syms[] = {
|
||||
{C_IN, (char *) "IN"},
|
||||
{C_CHAOS, (char *) "CHAOS"},
|
||||
@@ -426,8 +424,6 @@ const struct res_sym __p_update_section_syms[] attribute_hidden = {
|
||||
* Names of RR types and qtypes. The list is incomplete because its
|
||||
* size is part of the ABI.
|
||||
*/
|
||||
extern const struct res_sym __p_type_syms[];
|
||||
libresolv_hidden_proto (__p_type_syms)
|
||||
const struct res_sym __p_type_syms[] = {
|
||||
{ns_t_a, (char *) "A", (char *) "address"},
|
||||
{ns_t_ns, (char *) "NS", (char *) "name server"},
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
/* Tests for the ns_sprintrr function.
|
||||
Copyright (C) 2026 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 <arpa/nameser.h>
|
||||
|
||||
#include <alloc_buffer.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <libc-diag.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Regions that test_one_record uses for input and output. */
|
||||
static struct support_next_to_fault ntf_in;
|
||||
static struct support_next_to_fault ntf_out;
|
||||
|
||||
/* This is used by test_one_record to construct the packet. */
|
||||
static const char packet_prefix[] =
|
||||
/* DNS response with one question, one answer record. */
|
||||
"AA\x81\x80\0\1\0\1\0\0\0\0"
|
||||
/* Question: www.example.org/IN/ANY. */
|
||||
"\3www\7example\3org\0\0\xff\0\1"
|
||||
/* Response: compression reference. */
|
||||
"\xc0\x0c";
|
||||
|
||||
/* Use ns_sprintrr to format a DNS record (starting with
|
||||
packet_prefix) of type RTYPE, with a record payload of RDATALEN
|
||||
bytes starting at RDATA. Check successful formatting against
|
||||
EXPECTED. Try various truncated input and output buffers to catch
|
||||
overreads and buffer overflows, using ntf_in and ntf_out above. */
|
||||
static void
|
||||
test_one_record (uint16_t rtype, const char *rdata, size_t rdatalen,
|
||||
const char *expected)
|
||||
{
|
||||
struct rr_header
|
||||
{
|
||||
uint16_t typ;
|
||||
uint16_t cls;
|
||||
uint32_t ttl;
|
||||
uint16_t rdatalen;
|
||||
uint16_t pad;
|
||||
} hdr =
|
||||
{
|
||||
.typ = htons (rtype),
|
||||
.cls = htons (ns_c_in),
|
||||
.ttl = htonl (86400), /* One day. */
|
||||
.rdatalen = htons (rdatalen),
|
||||
};
|
||||
enum { hdrlen = offsetof (struct rr_header, pad) };
|
||||
TEST_COMPARE (hdrlen, 10);
|
||||
|
||||
/* Construct the packet from packet_prefix, hdr, and rdata. */
|
||||
unsigned char packet[512];
|
||||
size_t packetlen;
|
||||
{
|
||||
struct alloc_buffer buf = alloc_buffer_create (packet, sizeof (packet));
|
||||
alloc_buffer_copy_bytes (&buf, packet_prefix, sizeof (packet_prefix) - 1);
|
||||
alloc_buffer_copy_bytes (&buf, &hdr, hdrlen);
|
||||
alloc_buffer_copy_bytes (&buf, rdata, rdatalen);
|
||||
packetlen = sizeof (packet) - alloc_buffer_size (&buf);
|
||||
}
|
||||
|
||||
/* Parse the record. */
|
||||
ns_msg msg;
|
||||
TEST_COMPARE (ns_initparse (packet, packetlen, &msg), 0);
|
||||
ns_rr rr;
|
||||
TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0);
|
||||
|
||||
/* Try sizes up to this limit. Go a bit beyond the expected size to
|
||||
check for errors. */
|
||||
size_t max_result_size = strlen (expected) + 16;
|
||||
|
||||
bool success = false;
|
||||
for (size_t result_size = 1; result_size <= max_result_size; ++result_size)
|
||||
{
|
||||
char *result_start = ntf_out.buffer + ntf_out.length - result_size;
|
||||
memset (result_start, 'X', result_size);
|
||||
|
||||
/* ns_sprintrr was deprecated in 2.34. */
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
|
||||
int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size);
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
TEST_COMPARE_STRING (result_start, expected);
|
||||
TEST_COMPARE (ret, strlen (expected));
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEST_VERIFY (!success);
|
||||
TEST_COMPARE (ret, -1);
|
||||
}
|
||||
}
|
||||
TEST_VERIFY (success);
|
||||
|
||||
/* Test with truncated RDATA. */
|
||||
for (size_t rdata_size = 0; rdata_size <= rdatalen; ++rdata_size)
|
||||
{
|
||||
size_t truncated_packet_size = packetlen - rdatalen + rdata_size;
|
||||
unsigned char *packet_start
|
||||
= ((unsigned char *) ntf_in.buffer + ntf_in.length
|
||||
- truncated_packet_size);
|
||||
memcpy (packet_start, packet, truncated_packet_size);
|
||||
/* Patch in the updated RDATA length field. */
|
||||
uint16_t new_rdatalen = htons (rdata_size);
|
||||
memcpy (packet_start + truncated_packet_size - rdata_size - 2,
|
||||
&new_rdatalen, 2);
|
||||
|
||||
ns_msg msg;
|
||||
TEST_COMPARE (ns_initparse (packet_start, truncated_packet_size, &msg),
|
||||
0);
|
||||
ns_rr rr;
|
||||
TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0);
|
||||
|
||||
size_t result_size = strlen (expected) + 1;
|
||||
char *result_start = ntf_out.buffer + ntf_out.length - result_size;
|
||||
memset (result_start, 'X', result_size);
|
||||
|
||||
/* ns_sprintrr was deprecated in 2.34. */
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
|
||||
int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size);
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
|
||||
/* This flag indicates whether the output is syntactically
|
||||
correct. In some cases, truncation may still yield a valid
|
||||
payload. */
|
||||
bool broken = rdata_size < rdatalen;
|
||||
switch (rtype)
|
||||
{
|
||||
case ns_t_wks:
|
||||
/* WKS records use all trailing bytes for the port bitmap. */
|
||||
broken = rdata_size < 5;
|
||||
break;
|
||||
case ns_t_nsap:
|
||||
/* Uses all bytes that are available. */
|
||||
broken = false;
|
||||
break;
|
||||
case ns_t_txt:
|
||||
/* Truncation produces a valid payload if it occurs right
|
||||
after a complete string in the TXT payload. */
|
||||
broken = false;
|
||||
for (size_t pos = 0; pos < rdata_size; )
|
||||
{
|
||||
unsigned int slen = rdata[pos] & 0xff;
|
||||
if (pos + 1 + slen > rdata_size)
|
||||
{
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
pos += 1 + slen;
|
||||
}
|
||||
break;
|
||||
case ns_t_isdn:
|
||||
/* The second field is optional. If it is present, it must
|
||||
not be truncated. */
|
||||
broken = rdata_size < 6 || (rdata_size > 6 && rdata_size < rdatalen);
|
||||
break;
|
||||
case ns_t_a6:
|
||||
/* The first A6 subtest contains a trailing domain name,
|
||||
which is ignored and not formatted. */
|
||||
if (rdata_size > 0 && rdata[0] == 0)
|
||||
broken = rdata_size < 17;
|
||||
break;
|
||||
case ns_t_cert:
|
||||
case ns_t_tkey:
|
||||
case ns_t_tsig:
|
||||
/* Only generic printing, which does not validate anything. */
|
||||
broken = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (broken)
|
||||
{
|
||||
if (strstr (result_start, "RR format error") != NULL)
|
||||
/* No further checks if an error indicator has been added
|
||||
to the output. */
|
||||
;
|
||||
else
|
||||
TEST_COMPARE (ret, -1);
|
||||
}
|
||||
else
|
||||
TEST_VERIFY (ret > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
ntf_in = support_next_to_fault_allocate (512);
|
||||
ntf_out = support_next_to_fault_allocate (256);
|
||||
|
||||
#define T(rtype, rdata, expected) \
|
||||
test_one_record (rtype, rdata, sizeof (rdata) - 1, expected)
|
||||
T (ns_t_a, "\xc0\0\2\1", "www.example.org.\t1D IN A\t\t192.0.2.1");
|
||||
T (ns_t_cname, "\4www1\4prod\xc0\x10",
|
||||
"www.example.org.\t1D IN CNAME\twww1.prod.example.org.");
|
||||
T (ns_t_hinfo, "\5first\6second",
|
||||
"www.example.org.\t1D IN HINFO\t\"first\" \"second\"");
|
||||
T (ns_t_isdn, "\5first\6second",
|
||||
"www.example.org.\t1D IN ISDN\t\"first\" \"second\"");
|
||||
/* Bug: Extra space at the end in the text representation of ISDN RRs. */
|
||||
T (ns_t_isdn, "\5first", "www.example.org.\t1D IN ISDN\t\"first\" ");
|
||||
T (ns_t_soa,
|
||||
"\2ns\xc0\x10\12hostmaster\xc0\x10"
|
||||
"\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5",
|
||||
"www.example.org.\t1D IN SOA\tns.example.org. hostmaster.example.org. (\n"
|
||||
"\t\t\t\t\t1\t\t; serial\n"
|
||||
"\t\t\t\t\t2S\t\t; refresh\n"
|
||||
"\t\t\t\t\t3S\t\t; retry\n"
|
||||
"\t\t\t\t\t4S\t\t; expiry\n"
|
||||
"\t\t\t\t\t5S )\t\t; minimum\n");
|
||||
T (ns_t_mx, "\0\xa\2mx\xc0\x10",
|
||||
"www.example.org.\t1D IN MX\t10 mx.example.org.");
|
||||
T (ns_t_px, "\0\xa\3px1\xc0\x10\3px2\xc0\x10",
|
||||
"www.example.org.\t1D IN PX\t10 px1.example.org. px2.example.org.");
|
||||
T (ns_t_x25, "\4X.25",
|
||||
"www.example.org.\t1D IN X25\t\"X.25\"");
|
||||
T (ns_t_txt, "\1A\2BC\3DEF",
|
||||
"www.example.org.\t1D IN TXT\t\"A\" \"BC\" \"DEF\"");
|
||||
T (ns_t_nsap, "",
|
||||
"www.example.org.\t1D IN NSAP\t");
|
||||
T (ns_t_nsap, "\1",
|
||||
"www.example.org.\t1D IN NSAP\t01");
|
||||
T (ns_t_nsap, "\1\2",
|
||||
"www.example.org.\t1D IN NSAP\t01.02");
|
||||
T (ns_t_nsap, "\1\2\3",
|
||||
"www.example.org.\t1D IN NSAP\t01.0203");
|
||||
T (ns_t_nsap, "\1\2\3\4",
|
||||
"www.example.org.\t1D IN NSAP\t01.0203.04");
|
||||
T (ns_t_nsap,
|
||||
"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32"
|
||||
"\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61"
|
||||
"\62\63\64\65\66\67\70\71\72\73\74\75\76\77\100\101\102\103\104\105\106"
|
||||
"\107\110\111\112\113\114\115\116\117\120\121\122\123\124\125\126\127"
|
||||
"\130\131\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150"
|
||||
"\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171"
|
||||
"\172\173\174\175\176\177\200\201\202\203\204\205\206\207\210\211\212"
|
||||
"\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233"
|
||||
"\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254"
|
||||
"\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275"
|
||||
"\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316"
|
||||
"\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
|
||||
"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360"
|
||||
"\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
|
||||
"www.example.org.\t1D IN NSAP\t"
|
||||
"01.0203.0405.0607.0809.0A0B.0C0D.0E0F.1011.1213.1415.1617.1819.1A1B"
|
||||
".1C1D.1E1F.2021.2223.2425.2627.2829.2A2B.2C2D.2E2F.3031.3233.3435.3637"
|
||||
".3839.3A3B.3C3D.3E3F.4041.4243.4445.4647.4849.4A4B.4C4D.4E4F.5051.5253"
|
||||
".5455.5657.5859.5A5B.5C5D.5E5F.6061.6263.6465.6667.6869.6A6B.6C6D.6E6F"
|
||||
".7071.7273.7475.7677.7879.7A7B.7C7D.7E7F.8081.8283.8485.8687.8889.8A8B"
|
||||
".8C8D.8E8F.9091.9293.9495.9697.9899.9A9B.9C9D.9E9F.A0A1.A2A3.A4A5.A6A7"
|
||||
".A8A9.AAAB.ACAD.AEAF.B0B1.B2B3.B4B5.B6B7.B8B9.BABB.BCBD.BEBF.C0C1.C2C3"
|
||||
".C4C5.C6C7.C8C9.CACB.CCCD.CECF.D0D1.D2D3.D4D5.D6D7.D8D9.DADB.DCDD.DEDF"
|
||||
".E0E1.E2E3.E4E5.E6E7.E8E9.EAEB.ECED.EEEF.F0F1.F2F3.F4F5.F6F7.F8F9.FAFB"
|
||||
".FCFD.FEFF");
|
||||
T (ns_t_aaaa, "\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34",
|
||||
"www.example.org.\t1D IN AAAA\t2001:db8::1234");
|
||||
/* Example from RFC 1876. The loc_ntoa format is different from the
|
||||
official text representation. */
|
||||
T (ns_t_loc,
|
||||
"\000\063\026\023\211\027\055\320\160\276\025\360\000\230\215\040",
|
||||
"www.example.org.\t1D IN LOC"
|
||||
"\t42 21 54.000 N 71 06 18.000 W -24.00m 30.00m 10000.00m 10.00m");
|
||||
T (ns_t_naptr,
|
||||
"\0\1\0\2\5flags\7service\2.*\5naptr\xc0\x10",
|
||||
"www.example.org.\t1D IN NAPTR\t1 2 \"flags\" \"service\" \".*\""
|
||||
" naptr.example.org.");
|
||||
T (ns_t_srv,
|
||||
"\0\1\0\2\0\x50\4www1\xc0\x10",
|
||||
"www.example.org.\t1D IN SRV\t1 2 80 www1.example.org.");
|
||||
T (ns_t_rp, "\3rp1\xc0\x10\3rp2\xc0\x10",
|
||||
"www.example.org.\t1D IN RP\trp1.example.org. rp2.example.org.");
|
||||
T (ns_t_wks, "\xc0\0\2\1\6\0\0\0\0\0\0\0\0\0\0\200",
|
||||
"www.example.org.\t1D IN WKS\t192.0.2.1 6 ( \n\t\t\t\t80 )");
|
||||
T (ns_t_cert, "\0\1\x04\xd2\0blob",
|
||||
"www.example.org.\t1D IN CERT\t\\# 9 (\n"
|
||||
"\t00 01 04 d2 00 62 6c 6f 62 )\t\t\t; .....blob");
|
||||
T (ns_t_tkey, "\4algo\0\0\0\0\1\0\0\0\2\0\3\0\4"
|
||||
"\0\5\xa1\xa2\xa3\xa4\xa5\0\3\xb1\xb2\xb3",
|
||||
"www.example.org.\t1D IN TYPE249\t\\# 30 (\n"
|
||||
"\t04 61 6c 67 6f 00 00 00 00 01 00 00 00 02 00 03 ; .algo...........\n"
|
||||
"\t00 04 00 05 a1 a2 a3 a4 a5 00 03 b1 b2 b3 )\t; ..............");
|
||||
T (ns_t_tsig, "\4algo\0"
|
||||
"\0\20\xdd\xcd\x64\x10\xe9\x21\x34\x1a\x8e\xe0\xa1\x9a\x30\xfc\x3b\xd1"
|
||||
"\0\2\0\3\0\5other",
|
||||
"www.example.org.\t1D IN TSIG\t\\# 35 (\n"
|
||||
"\t04 61 6c 67 6f 00 00 10 dd cd 64 10 e9 21 34 1a ; .algo.....d..!4.\n"
|
||||
"\t8e e0 a1 9a 30 fc 3b d1 00 02 00 03 00 05 6f 74 ; ....0.;.......ot\n"
|
||||
"\t68 65 72 )\t\t\t\t\t; her");
|
||||
T (ns_t_a6,
|
||||
"\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34\6prefix\xc0\x10",
|
||||
"www.example.org.\t1D IN A6\t0 2001:db8::1234");
|
||||
T (ns_t_a6,
|
||||
"\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x35",
|
||||
"www.example.org.\t1D IN A6\t0 2001:db8::1235");
|
||||
T (ns_t_a6, "\200\6prefix\xc0\x10",
|
||||
"www.example.org.\t1D IN A6\t128 prefix.example.org.");
|
||||
T (ns_t_a6, "\x20\0\0\0\0\0\0\0\0\0\0\x12\x36\6prefix\xc0\x10",
|
||||
"www.example.org.\t1D IN A6\t32 ::1236 prefix.example.org.");
|
||||
#undef T
|
||||
|
||||
support_next_to_fault_free (&ntf_in);
|
||||
support_next_to_fault_free (&ntf_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,162 @@
|
||||
/* Test handling of invalid section transitions (bug 34014).
|
||||
Copyright (C) 2022-2026 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 <array_length.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/format_nss.h>
|
||||
#include <support/resolv_test.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* Name of test, and the second section type. */
|
||||
struct item {
|
||||
const char *test;
|
||||
int ns_section;
|
||||
};
|
||||
|
||||
static const struct item test_items[] =
|
||||
{
|
||||
{ "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar },
|
||||
{ "Test crossing from ns_s_an to ns_s_an.", ns_s_ns },
|
||||
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
/* The response is designed to contain the following:
|
||||
- An Answer section with one T_PTR record that is skipped.
|
||||
- A second section with a semantically invalid T_PTR record.
|
||||
The original defect is that the response parsing would cross
|
||||
section boundaries and handle the additional section T_PTR
|
||||
as if it were an answer. A conforming implementation would
|
||||
stop as soon as it reaches the end of the section. */
|
||||
static void
|
||||
response (const struct resolv_response_context *ctx,
|
||||
struct resolv_response_builder *b,
|
||||
const char *qname, uint16_t qclass, uint16_t qtype)
|
||||
{
|
||||
TEST_COMPARE (qclass, C_IN);
|
||||
|
||||
/* We only test PTR. */
|
||||
TEST_COMPARE (qtype, T_PTR);
|
||||
|
||||
unsigned int count;
|
||||
char *tail = NULL;
|
||||
|
||||
if (strstr (qname, "in-addr.arpa") != NULL
|
||||
&& sscanf (qname, "%u.%ms", &count, &tail) == 2)
|
||||
TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
|
||||
else if (sscanf (qname, "%x.%ms", &count, &tail) == 2)
|
||||
{
|
||||
TEST_COMPARE_STRING (tail, "\
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
|
||||
}
|
||||
else
|
||||
FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
|
||||
free (tail);
|
||||
|
||||
/* We have a bounded number of possible tests. */
|
||||
TEST_VERIFY (count >= 0);
|
||||
TEST_VERIFY (count <= 15);
|
||||
|
||||
struct resolv_response_flags flags = {};
|
||||
resolv_response_init (b, flags);
|
||||
resolv_response_add_question (b, qname, qclass, qtype);
|
||||
resolv_response_section (b, ns_s_an);
|
||||
|
||||
/* Actual answer record, but the wrong name (skipped). */
|
||||
resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60);
|
||||
|
||||
/* Record the answer. */
|
||||
resolv_response_add_name (b, "test.ptr.example.net");
|
||||
resolv_response_close_record (b);
|
||||
|
||||
/* Add a second section to test section boundary crossing. */
|
||||
resolv_response_section (b, test_items[count].ns_section);
|
||||
/* Semantically incorrect, but hide a T_PTR entry. */
|
||||
resolv_response_open_record (b, qname, qclass, qtype, 60);
|
||||
resolv_response_add_name (b, "wrong.ptr.example.net");
|
||||
resolv_response_close_record (b);
|
||||
}
|
||||
|
||||
|
||||
/* Perform one check using a reverse lookup. */
|
||||
static void
|
||||
check_reverse (int af, int count)
|
||||
{
|
||||
TEST_VERIFY (af == AF_INET || af == AF_INET6);
|
||||
TEST_VERIFY (count < array_length (test_items));
|
||||
|
||||
char addr[sizeof (struct in6_addr)] = { 0 };
|
||||
socklen_t addrlen;
|
||||
if (af == AF_INET)
|
||||
{
|
||||
addr[0] = (char) 192;
|
||||
addr[1] = (char) 168;
|
||||
addr[2] = (char) 0;
|
||||
addr[3] = (char) count;
|
||||
addrlen = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr[0] = 0x20;
|
||||
addr[1] = 0x01;
|
||||
addr[2] = 0x0d;
|
||||
addr[3] = 0xb8;
|
||||
addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
|
||||
addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
|
||||
addr[12] = 0x0;
|
||||
addr[13] = 0x0;
|
||||
addr[14] = 0x0;
|
||||
addr[15] = count;
|
||||
addrlen = 16;
|
||||
}
|
||||
|
||||
h_errno = 0;
|
||||
struct hostent *answer = gethostbyaddr (addr, addrlen, af);
|
||||
TEST_VERIFY (answer == NULL);
|
||||
TEST_VERIFY (h_errno == NO_RECOVERY);
|
||||
if (answer != NULL)
|
||||
printf ("error: unexpected success: %s\n",
|
||||
support_format_hostent (answer));
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct resolv_test *obj = resolv_test_start
|
||||
((struct resolv_redirect_config)
|
||||
{
|
||||
.response_callback = response
|
||||
});
|
||||
|
||||
for (int i = 0; test_items[i].test != NULL; i++)
|
||||
{
|
||||
check_reverse (AF_INET, i);
|
||||
check_reverse (AF_INET6, i);
|
||||
}
|
||||
|
||||
resolv_test_end (obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,255 @@
|
||||
/* Test handling of invalid T_PTR results (bug 34015).
|
||||
Copyright (C) 2022-2026 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 <array_length.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/format_nss.h>
|
||||
#include <support/resolv_test.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* Name of test, the answer, the expected error return, and if we
|
||||
expect the call to fail. */
|
||||
struct item {
|
||||
const char *test;
|
||||
const char *answer;
|
||||
int expected;
|
||||
bool fail;
|
||||
};
|
||||
|
||||
static const struct item test_items[] =
|
||||
{
|
||||
/* Test for invalid characters. */
|
||||
{ "Invalid use of \"|\"",
|
||||
"test.|.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"&\"",
|
||||
"test.&.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \";\"",
|
||||
"test.;.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"<\"",
|
||||
"test.<.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \">\"",
|
||||
"test.>.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"(\"",
|
||||
"test.(.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \")\"",
|
||||
"test.).ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"$\"",
|
||||
"test.$.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"`\"",
|
||||
"test.`.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\\\"",
|
||||
"test.\\.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\'\"",
|
||||
"test.'.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\"\"",
|
||||
"test.\".ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \" \"",
|
||||
"test. .ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\\t\"",
|
||||
"test.\t.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\\n\"",
|
||||
"test.\n.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"\\r\"",
|
||||
"test.\r.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"*\"",
|
||||
"test.*.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"?\"",
|
||||
"test.?.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"[\"",
|
||||
"test.[.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"]\"",
|
||||
"test.].ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \",\"",
|
||||
"test.,.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"~\"",
|
||||
"test.~.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \":\"",
|
||||
"test.:.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"!\"",
|
||||
"test.!.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"@\"",
|
||||
"test.@.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"#\"",
|
||||
"test.#.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"%\"",
|
||||
"test.%%.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of \"^\"",
|
||||
"test.^.ptr.example", NO_RECOVERY, true },
|
||||
|
||||
/* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */
|
||||
{ "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)",
|
||||
"ÁÂÃ.test.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of UTF-8 (4-byte, U+0750-U+0752)",
|
||||
"ݐݑݒ.test.ptr.example", NO_RECOVERY, true },
|
||||
{ "Invalid use of UTF-8 (6-byte, U+0904-U+0906)",
|
||||
"ऄअआ.test.ptr.example", NO_RECOVERY, true },
|
||||
|
||||
/* Test for "-" which may be valid depending on position. */
|
||||
{ "Invalid leading \"-\"",
|
||||
"-test.ptr.example", NO_RECOVERY, true },
|
||||
{ "Valid trailing \"-\"",
|
||||
"test-.ptr.example", 0, false },
|
||||
{ "Valid mid-label use of \"-\"",
|
||||
"te-st.ptr.example", 0, false },
|
||||
|
||||
/* Test for "_" which is always valid in any position. */
|
||||
{ "Valid leading use of \"_\"",
|
||||
"_test.ptr.example", 0, false },
|
||||
{ "Valid mid-label use of \"_\"",
|
||||
"te_st.ptr.example", 0, false },
|
||||
{ "Valid trailing use of \"_\"",
|
||||
"test_.ptr.example", 0, false },
|
||||
|
||||
/* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */
|
||||
{ "Valid \"[A-Z]\"",
|
||||
"test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false },
|
||||
{ "Valid \"[a-z]\"",
|
||||
"test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false },
|
||||
{ "Valid \"[0-9]\"",
|
||||
"test.0123456789.ptr.example", 0, false },
|
||||
{ "Valid mixed use of \"[A-Za-z0-9_-]\"",
|
||||
"test.012abcABZ_-.ptr.example", 0, false },
|
||||
};
|
||||
|
||||
static void
|
||||
response (const struct resolv_response_context *ctx,
|
||||
struct resolv_response_builder *b,
|
||||
const char *qname, uint16_t qclass, uint16_t qtype)
|
||||
{
|
||||
TEST_COMPARE (qclass, C_IN);
|
||||
|
||||
/* We only test PTR. */
|
||||
TEST_COMPARE (qtype, T_PTR);
|
||||
|
||||
unsigned int count, count1;
|
||||
char *tail = NULL;
|
||||
|
||||
/* The test implementation can handle up to 255 tests. */
|
||||
if (strstr (qname, "in-addr.arpa") != NULL
|
||||
&& sscanf (qname, "%u.%ms", &count, &tail) == 2)
|
||||
TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
|
||||
else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3)
|
||||
{
|
||||
TEST_COMPARE_STRING (tail, "\
|
||||
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
|
||||
count |= count1 << 4;
|
||||
}
|
||||
else
|
||||
FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
|
||||
free (tail);
|
||||
|
||||
/* Cross check. Count has a fixed bound (soft limit). */
|
||||
TEST_VERIFY (count >= 0 && count <= 255);
|
||||
|
||||
/* We have a fixed number of tests (hard limit). */
|
||||
TEST_VERIFY_EXIT (count < array_length (test_items));
|
||||
|
||||
struct resolv_response_flags flags = {};
|
||||
resolv_response_init (b, flags);
|
||||
resolv_response_add_question (b, qname, qclass, qtype);
|
||||
resolv_response_section (b, ns_s_an);
|
||||
|
||||
/* Actual answer record. */
|
||||
resolv_response_open_record (b, qname, qclass, qtype, 60);
|
||||
|
||||
/* Record the answer. */
|
||||
resolv_response_add_name (b, test_items[count].answer);
|
||||
resolv_response_close_record (b);
|
||||
}
|
||||
|
||||
/* Perform one check using a reverse lookup. */
|
||||
static void
|
||||
check_reverse (int af, int count)
|
||||
{
|
||||
TEST_VERIFY (af == AF_INET || af == AF_INET6);
|
||||
TEST_VERIFY_EXIT (count < array_length (test_items));
|
||||
|
||||
/* Generate an address to query for each test. */
|
||||
char addr[sizeof (struct in6_addr)] = { 0 };
|
||||
socklen_t addrlen;
|
||||
if (af == AF_INET)
|
||||
{
|
||||
addr[0] = (char) 192;
|
||||
addr[1] = (char) 168;
|
||||
addr[2] = (char) 0;
|
||||
addr[3] = (char) count;
|
||||
addrlen = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr[0] = 0x20;
|
||||
addr[1] = 0x01;
|
||||
addr[2] = 0x0d;
|
||||
addr[3] = 0xb8;
|
||||
addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
|
||||
addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
|
||||
addr[12] = 0x0;
|
||||
addr[13] = 0x0;
|
||||
addr[14] = 0x0;
|
||||
addr[15] = (char) count;
|
||||
addrlen = 16;
|
||||
}
|
||||
|
||||
h_errno = 0;
|
||||
struct hostent *answer = gethostbyaddr (addr, addrlen, af);
|
||||
|
||||
/* Verify h_errno is as expected. */
|
||||
TEST_COMPARE (h_errno, test_items[count].expected);
|
||||
if (h_errno != test_items[count].expected)
|
||||
/* And print more information if it's not. */
|
||||
printf ("INFO: %s\n", test_items[count].test);
|
||||
|
||||
if (test_items[count].fail)
|
||||
{
|
||||
/* We expected a failure so verify answer is NULL. */
|
||||
TEST_VERIFY (answer == NULL);
|
||||
/* If it's not NULL we should print out what we received. */
|
||||
if (answer != NULL)
|
||||
printf ("error: unexpected success: %s\n",
|
||||
support_format_hostent (answer));
|
||||
}
|
||||
else
|
||||
/* We don't expect a failure so answer must be valid. */
|
||||
TEST_COMPARE_STRING (answer->h_name, test_items[count].answer);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct resolv_test *obj = resolv_test_start
|
||||
((struct resolv_redirect_config)
|
||||
{
|
||||
.response_callback = response
|
||||
});
|
||||
|
||||
for (int i = 0; i < array_length (test_items); i++)
|
||||
{
|
||||
check_reverse (AF_INET, i);
|
||||
check_reverse (AF_INET6, i);
|
||||
}
|
||||
resolv_test_end (obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -46,6 +46,9 @@ handle_code (const struct resolv_response_context *ctx,
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case 0:
|
||||
send_ptr (b, qname, qclass, qtype, "0.in-addr.arpa");
|
||||
break;
|
||||
case 1:
|
||||
send_ptr (b, qname, qclass, qtype, "1.in-addr.arpa");
|
||||
break;
|
||||
@@ -265,6 +268,9 @@ do_test (void)
|
||||
"error: TRY_AGAIN\n");
|
||||
|
||||
/* Lookup by address, success cases. */
|
||||
check_reverse (0,
|
||||
"name: 0.in-addr.arpa\n"
|
||||
"net: 0x00000000\n");
|
||||
check_reverse (1,
|
||||
"name: 1.in-addr.arpa\n"
|
||||
"net: 0x00000001\n");
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ $4 == "*UND*" { next }
|
||||
$2 == "l" { next }
|
||||
|
||||
# If the target uses ST_OTHER, it will be output before the symbol name.
|
||||
$2 == "g" || $2 == "w" && (NF == 7 || NF == 8) {
|
||||
$2 == "g" || $2 == "w" && (NF == 6 || NF == 7 || NF == 8) {
|
||||
type = $3;
|
||||
size = $5;
|
||||
sub(/^0*/, "", size);
|
||||
|
||||
@@ -260,6 +260,8 @@ tests := \
|
||||
tllformat \
|
||||
tst-bz11319 \
|
||||
tst-bz11319-fortify2 \
|
||||
tst-bz12701-c \
|
||||
tst-bz12701-c2 \
|
||||
tst-cookie \
|
||||
tst-dprintf-length \
|
||||
tst-fclose-devzero \
|
||||
@@ -347,6 +349,7 @@ tests := \
|
||||
tst-vfprintf-user-type \
|
||||
tst-vfprintf-width-i18n \
|
||||
tst-vfprintf-width-prec-alloc \
|
||||
tst-vfscanf-bz34008 \
|
||||
tst-wc-printf \
|
||||
tstdiomisc \
|
||||
tstgetln \
|
||||
@@ -562,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
|
||||
tst-vfprintf-width-prec-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
|
||||
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
|
||||
tst-vfscanf-bz34008-ENV = \
|
||||
MALLOC_CHECK_=3 \
|
||||
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
|
||||
tst-printf-bz25691-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
|
||||
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/* Verify scanf field width handling with the 'c' conversion (BZ #12701).
|
||||
Copyright (C) 2025-2026 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 <string.h>
|
||||
|
||||
#include <libc-diag.h>
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
#include <support/xstdio.h>
|
||||
|
||||
/* Verify various aspects of field width handling, including the data
|
||||
obtained, the number of bytes consumed, and the stream position. */
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
static const char s[43] = "The quick brown fox jumps over the lazy dog";
|
||||
struct support_next_to_fault ntfo, ntfi;
|
||||
ntfo = support_next_to_fault_allocate (sizeof (s));
|
||||
ntfi = support_next_to_fault_allocate (sizeof (s));
|
||||
char *e = ntfo.buffer + sizeof (s);
|
||||
char *b = ntfi.buffer;
|
||||
|
||||
char *c;
|
||||
FILE *f;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
memcpy (ntfi.buffer, s, sizeof (s));
|
||||
|
||||
i = 0;
|
||||
f = xfmemopen (b, sizeof (s), "r");
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
/* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */
|
||||
DIAG_PUSH_NEEDS_COMMENT;
|
||||
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%0c%n", c, &n) == 1);
|
||||
DIAG_POP_NEEDS_COMMENT;
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%1c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 2;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 4;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%4c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 4);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 8;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%8c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 8);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 16;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%16c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 16);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - (sizeof (s) - i);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%32c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 10);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, sizeof (s) - i) == 0);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == sizeof (s));
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
i = 0;
|
||||
f = xfmemopen (b, 3, "r");
|
||||
|
||||
c = e - 1;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - 2;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - (3 - i);
|
||||
TEST_VERIFY_EXIT (feof (f) == 0);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == EOF);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
i = 0;
|
||||
f = xfmemopen (b, 3, "r");
|
||||
|
||||
c = e - 2;
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 2);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
|
||||
i += n;
|
||||
|
||||
c = e - (3 - i);
|
||||
TEST_VERIFY_EXIT (ftell (f) == i);
|
||||
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
|
||||
TEST_VERIFY_EXIT (n == 1);
|
||||
TEST_VERIFY_EXIT (memcmp (c, s + i, 3 - i) == 0);
|
||||
|
||||
TEST_VERIFY_EXIT (ftell (f) == 3);
|
||||
TEST_VERIFY_EXIT (feof (f) != 0);
|
||||
|
||||
xfclose (f);
|
||||
|
||||
support_next_to_fault_free (&ntfi);
|
||||
support_next_to_fault_free (&ntfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
|
||||
Copyright (C) 2026 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 <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libc-diag.h>
|
||||
#include <support/check.h>
|
||||
#include <support/next_to_fault.h>
|
||||
#include <support/xstdio.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *c = NULL;
|
||||
int i;
|
||||
|
||||
TEST_VERIFY (sscanf ("1234", "%30mc", &c) == 1);
|
||||
|
||||
TEST_VERIFY (c != NULL);
|
||||
TEST_COMPARE_BLOB (c, 5, "1234\0", 5);
|
||||
for (i = 5; i < 30; i ++)
|
||||
TEST_VERIFY (c[i] == '\0');
|
||||
|
||||
TEST_VERIFY (malloc_usable_size (c) >= 30);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user