mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 05:59:27 +08:00
Compare commits
167
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28eb5caf89 | ||
|
|
f18446d7b4 | ||
|
|
831299deb7 | ||
|
|
7b27c450c3 | ||
|
|
8e30f03744 | ||
|
|
2d0e16a01b | ||
|
|
8216216b7b | ||
|
|
ddb6d93f5e | ||
|
|
12d4ddf3c1 | ||
|
|
bbf5a58ccb | ||
|
|
8d79491837 | ||
|
|
304ce5fe46 | ||
|
|
69c58d5ef9 | ||
|
|
3703c32a8d | ||
|
|
531717afbc | ||
|
|
61f2272bc6 | ||
|
|
9f27ef8090 | ||
|
|
01a8874eba | ||
|
|
047968e81d | ||
|
|
9e5693b446 | ||
|
|
1c08c17156 | ||
|
|
2d4f26e5cf | ||
|
|
d4b7559457 | ||
|
|
b8bb48a18d | ||
|
|
c8f2a3e803 | ||
|
|
e6ae5b25cd | ||
|
|
1dbe841a67 | ||
|
|
5a802723db | ||
|
|
ae7748e67f | ||
|
|
174d0b61c7 | ||
|
|
0a167374fd | ||
|
|
4ad1659d8c | ||
|
|
3319cea99e | ||
|
|
f733e291bb | ||
|
|
43757c70ee | ||
|
|
29d3aeb0e8 | ||
|
|
2d7720f316 | ||
|
|
183709983d | ||
|
|
d385079bd5 | ||
|
|
7df3ad6560 | ||
|
|
fc133fcf49 | ||
|
|
775c05b28c | ||
|
|
c6b346ec55 | ||
|
|
0675185923 | ||
|
|
5db3239baf | ||
|
|
5b99f172b8 | ||
|
|
70d293a158 | ||
|
|
a2be2c0f5d | ||
|
|
489006c3c5 | ||
|
|
937f2c783a | ||
|
|
0058c73d11 | ||
|
|
665d0252f1 | ||
|
|
82ff13e2cc | ||
|
|
539b593a1d | ||
|
|
7b37ae60c6 | ||
|
|
0381c1c10d | ||
|
|
10368cb76b | ||
|
|
66ca40582e | ||
|
|
927bcaf892 | ||
|
|
0d4159c36c | ||
|
|
c0cbb9345e | ||
|
|
e81b975fcc | ||
|
|
aa4e48e73c | ||
|
|
ac911d3b57 | ||
|
|
20d37de533 | ||
|
|
fbaa99ed41 | ||
|
|
096e14f632 | ||
|
|
f00fad4e4c | ||
|
|
cf239ddd2e | ||
|
|
7257ba7bf2 | ||
|
|
db9071c0f6 | ||
|
|
2d612b2c5f | ||
|
|
5b13651085 | ||
|
|
5ee8a436ab | ||
|
|
3ef8be9b89 | ||
|
|
e5c8da9826 | ||
|
|
412aaf1522 | ||
|
|
c4c833d3dd | ||
|
|
547b63bf6d | ||
|
|
b061e95277 | ||
|
|
95e206b67f | ||
|
|
e26a2db141 | ||
|
|
094618d401 | ||
|
|
4f0a61f753 | ||
|
|
8e88c0d888 | ||
|
|
74a4425fae | ||
|
|
bb5bb87959 | ||
|
|
700264179c | ||
|
|
7fa6d30eea | ||
|
|
0de2b69c31 | ||
|
|
0c92f409a7 | ||
|
|
f84949f1c4 | ||
|
|
5e43566f0f | ||
|
|
d0c84d22b6 | ||
|
|
af316e4627 | ||
|
|
36eb01dd85 | ||
|
|
8b7be87aa2 | ||
|
|
c4f5e32aae | ||
|
|
0858f46440 | ||
|
|
1e40391de2 | ||
|
|
568c86274a | ||
|
|
df31c7ca92 | ||
|
|
7df507808c | ||
|
|
8dc7605665 | ||
|
|
48cf525f4b | ||
|
|
8d730cb25a | ||
|
|
4bc9918c99 | ||
|
|
4722d1fb9d | ||
|
|
bea507a3f5 | ||
|
|
d0a5b76902 | ||
|
|
24a30c5959 | ||
|
|
88db98fa6e | ||
|
|
32965a46ce | ||
|
|
6fdf971c9d | ||
|
|
70d585151c | ||
|
|
1c8efe848b | ||
|
|
7611339a9b | ||
|
|
21b760cc2f | ||
|
|
6f3459f985 | ||
|
|
64246fccaf | ||
|
|
9bbd2b6172 | ||
|
|
4e8a33a959 | ||
|
|
fd15ba932d | ||
|
|
3a44844c97 | ||
|
|
c839175267 | ||
|
|
1094741224 | ||
|
|
a98b8b221c | ||
|
|
4c833bbebe | ||
|
|
3b9ceb3320 | ||
|
|
bb44fe7711 | ||
|
|
f2ac792047 | ||
|
|
18fdba553d | ||
|
|
7c9e054afd | ||
|
|
9c5ae39a64 | ||
|
|
63c3696a4a | ||
|
|
5b9d49293b | ||
|
|
496b5963a7 | ||
|
|
04330f8526 | ||
|
|
de371d1581 | ||
|
|
ece4e11d55 | ||
|
|
91b909315c | ||
|
|
b999c0098a | ||
|
|
54ba2541b3 | ||
|
|
83d3eec672 | ||
|
|
ad9b0037cc | ||
|
|
2448ba1d72 | ||
|
|
5810e6d75f | ||
|
|
d39fb022c2 | ||
|
|
46bbbd4622 | ||
|
|
3937f6806d | ||
|
|
ab029a2801 | ||
|
|
a3189fb15b | ||
|
|
05c08d5aea | ||
|
|
123d48b33a | ||
|
|
03f44ce093 | ||
|
|
e85a88e00c | ||
|
|
a9ae2062d5 | ||
|
|
da6ce60e3c | ||
|
|
9db2970506 | ||
|
|
44f2c26ee4 | ||
|
|
f2d95cf030 | ||
|
|
9f997ceca2 | ||
|
|
71f2b249a2 | ||
|
|
5d31b083e4 | ||
|
|
f4f8843515 | ||
|
|
aa638b86b2 | ||
|
|
ae1b6b13d4 |
@@ -184,14 +184,9 @@ if 'CFLAGS' is specified it must enable optimization. For example:
|
||||
'--enable-pt_chown'
|
||||
The file 'pt_chown' is a helper binary for 'grantpt' (*note
|
||||
Pseudo-Terminals: Allocation.) that is installed setuid root to fix
|
||||
up pseudo-terminal ownership. It is not built by default because
|
||||
systems using the Linux kernel are commonly built with the 'devpts'
|
||||
filesystem enabled and mounted at '/dev/pts', which manages
|
||||
pseudo-terminal ownership automatically. By using
|
||||
'--enable-pt_chown', you may build 'pt_chown' and install it setuid
|
||||
and owned by 'root'. The use of 'pt_chown' introduces additional
|
||||
security risks to the system and you should enable it only if you
|
||||
understand and accept those risks.
|
||||
up pseudo-terminal ownership on GNU/Hurd. It is not required on
|
||||
GNU/Linux, and the GNU C Library will not use the installed
|
||||
'pt_chown' program when configured with '--enable-pt_chown'.
|
||||
|
||||
'--disable-werror'
|
||||
By default, the GNU C Library is built with '-Werror'. If you wish
|
||||
|
||||
+22
-3
@@ -42,6 +42,22 @@ else
|
||||
objdir must be defined by the build-directory Makefile.
|
||||
endif
|
||||
|
||||
# Did we request 'make -s' run? "yes" or "no".
|
||||
# Starting from make-4.4 MAKEFLAGS now contains long
|
||||
# options like '--shuffle'. To detect presence of 's'
|
||||
# we pick first word with short options. Long options
|
||||
# are guaranteed to come after whitespace. We use '-'
|
||||
# prefix to always have a word before long options
|
||||
# even if no short options were passed.
|
||||
# Typical MAKEFLAGS values to watch for:
|
||||
# "rs --shuffle=42" (silent)
|
||||
# " --shuffle" (not silent)
|
||||
ifeq ($(findstring s, $(firstword -$(MAKEFLAGS))),)
|
||||
silent-make := no
|
||||
else
|
||||
silent-make := yes
|
||||
endif
|
||||
|
||||
# Root of the sysdeps tree.
|
||||
sysdep_dir := $(..)sysdeps
|
||||
export sysdep_dir := $(sysdep_dir)
|
||||
@@ -557,9 +573,12 @@ link-libc-rpath-link = -Wl,-rpath-link=$(rpath-link)
|
||||
# before the expansion of LDLIBS-* variables).
|
||||
|
||||
# Tests use -Wl,-rpath instead of -Wl,-rpath-link for
|
||||
# build-hardcoded-path-in-tests.
|
||||
# build-hardcoded-path-in-tests. Add -Wl,--disable-new-dtags to force
|
||||
# DT_RPATH instead of DT_RUNPATH which only applies to DT_NEEDED entries
|
||||
# in the executable and doesn't applies to DT_NEEDED entries in shared
|
||||
# libraries which are loaded via DT_NEEDED entries in the executable.
|
||||
ifeq (yes,$(build-hardcoded-path-in-tests))
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath)
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath) -Wl,--disable-new-dtags
|
||||
else
|
||||
link-libc-tests-rpath-link = $(link-libc-rpath-link)
|
||||
endif # build-hardcoded-path-in-tests
|
||||
@@ -892,7 +911,7 @@ endif
|
||||
# umpteen zillion filenames along with it (we use `...' instead)
|
||||
# but we don't want this echoing done when the user has said
|
||||
# he doesn't want to see commands echoed by using -s.
|
||||
ifneq "$(findstring s,$(MAKEFLAGS))" "" # if -s
|
||||
ifeq ($(silent-make),yes) # if -s
|
||||
+cmdecho := echo >/dev/null
|
||||
else # not -s
|
||||
+cmdecho := echo
|
||||
|
||||
@@ -805,7 +805,7 @@ endif
|
||||
# Maximize efficiency by minimizing the number of rules.
|
||||
.SUFFIXES: # Clear the suffix list. We don't use suffix rules.
|
||||
# Don't define any builtin rules.
|
||||
MAKEFLAGS := $(MAKEFLAGS)r
|
||||
MAKEFLAGS := $(MAKEFLAGS) -r
|
||||
|
||||
# Generic rule for making directories.
|
||||
%/:
|
||||
@@ -822,7 +822,7 @@ MAKEFLAGS := $(MAKEFLAGS)r
|
||||
.PRECIOUS: $(foreach l,$(libtypes),$(patsubst %,$(common-objpfx)$l,c))
|
||||
|
||||
# Use the verbose option of ar and tar when not running silently.
|
||||
ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s
|
||||
ifeq ($(silent-make),no) # if not -s
|
||||
verbose := v
|
||||
else # -s
|
||||
verbose :=
|
||||
|
||||
@@ -5,6 +5,99 @@ 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.31.1
|
||||
|
||||
The following bugs are resolved with this release:
|
||||
[14231] stdio-common tests memory requirements
|
||||
[19519] iconv(1) with -c option hangs on illegal multi-byte sequences
|
||||
(CVE-2016-10228)
|
||||
[20019] NULL pointer dereference in libc.so.6 IFUNC due to uninitialized GOT
|
||||
[20543] Please move from .gnu.linkonce to comdat
|
||||
[22542] CVE-2022-23219: Buffer overflow in sunrpc clnt_create for "unix"
|
||||
[23296] Data race in setting function descriptor during lazy binding
|
||||
[24973] iconv encounters segmentation fault when converting 0x00 0xfe in
|
||||
EUC-KR to UTF-8 (CVE-2019-25013)
|
||||
[25487] sinl() stack corruption from crafted input (CVE-2020-10029)
|
||||
[25523] MIPS/Linux inline syscall template is miscompiled
|
||||
[25623] test-sysvmsg, test-sysvsem, test-sysvshm fail with 2.31 on 32 bit and
|
||||
old kernel
|
||||
[25635] arm: Wrong sysdep order selection for soft-fp
|
||||
[25639] localedata: Some names of days and months wrongly spelt in
|
||||
Occitan
|
||||
[25691] stdio: Remove memory leak from multibyte convertion
|
||||
[25715] system() returns wrong errors when posix_spawn fails
|
||||
[25810] x32: Incorrect syscall entries with pointer, off_t and size_t
|
||||
[25896] Incorrect prctl
|
||||
[25902] Bad LOADARGS_N
|
||||
[25933] Off by one error in __strncmp_avx2
|
||||
[25966] Incorrect access of __x86_shared_non_temporal_threshold for x32
|
||||
[25976] nss_compat: internal_end*ent may clobber errno, hiding ERANGE
|
||||
[26211] printf integer overflow calculating allocation size
|
||||
[26224] iconv hangs when converting some invalid inputs from several IBM
|
||||
character sets (CVE-2020-27618)
|
||||
[26248] Incorrect argument types for INLINE_SETXID_SYSCALL
|
||||
[26332] Incorrect cache line size load causes memory corruption in memset
|
||||
[26383] bind_textdomain_codeset doesn't accept //TRANSLIT anymore
|
||||
[26923] Assertion failure in iconv when converting invalid UCS4 (CVE-2020-29562)
|
||||
[26932] libc: sh: Multiple floating point functions defined as stubs only
|
||||
[27130] "rep movsb" performance issue
|
||||
[27177] GLIBC_TUNABLES=glibc.cpu.x86_ibt=on:glibc.cpu.x86_shstk=on doesn't work
|
||||
[27457] vzeroupper use in AVX2 multiarch string functions cause HTM aborts
|
||||
[27974] Overflow bug in some implementation of wcsnlen, wmemchr, and wcsncat
|
||||
[28524] Conversion from ISO-2022-JP-3 with iconv may emit spurious NULs
|
||||
[28755] overflow bug in wcsncmp_avx2 and wcsncmp_evex
|
||||
[28768] CVE-2022-23218: Buffer overflow in sunrpc svcunix_create
|
||||
[28769] CVE-2021-3999: Off-by-one buffer overflow/underflow in getcwd()
|
||||
[28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm
|
||||
variants when avoiding overflow
|
||||
[29530] segfault in printf handling thousands separator
|
||||
[33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return
|
||||
uninitialized memory
|
||||
|
||||
Security related changes:
|
||||
|
||||
CVE-2016-10228: An infinite loop has been fixed in the iconv program when
|
||||
invoked with the -c option and when processing invalid multi-byte input
|
||||
sequences. Reported by Jan Engelhardt.
|
||||
|
||||
CVE-2020-10029: Trigonometric functions on x86 targets suffered from stack
|
||||
corruption when they were passed a pseudo-zero argument. Reported by Guido
|
||||
Vranken / ForAllSecure Mayhem.
|
||||
|
||||
CVE-2020-1751: A defect in the PowerPC backtrace function could cause an
|
||||
out-of-bounds write when executed in a signal frame context.
|
||||
|
||||
CVE-2020-1752: A use-after-free vulnerability in the glob function when
|
||||
expanding ~user has been fixed.
|
||||
|
||||
CVE-2020-6096: A signed comparison vulnerability in the ARMv7 memcpy and
|
||||
memmove functions has been fixed. Discovered by Jason Royes and Samual
|
||||
Dytrych of the Cisco Security Assessment and Penetration Team (See
|
||||
TALOS-2020-1019).
|
||||
|
||||
CVE-2020-27618: An infinite loop has been fixed in the iconv program when
|
||||
invoked with input containing redundant shift sequences in the IBM1364,
|
||||
IBM1371, IBM1388, IBM1390, or IBM1399 character sets.
|
||||
|
||||
CVE-2020-29562: An assertion failure has been fixed in the iconv function
|
||||
when invoked with UCS4 input containing an invalid character.
|
||||
|
||||
CVE-2021-3999: Passing a buffer of size exactly 1 byte to the getcwd
|
||||
function may result in an off-by-one buffer underflow and overflow
|
||||
when the current working directory is longer than PATH_MAX and also
|
||||
corresponds to the / directory through an unprivileged mount
|
||||
namespace. Reported by Qualys.
|
||||
|
||||
CVE-2022-23219: Passing an overlong file name to the clnt_create
|
||||
legacy function could result in a stack-based buffer overflow when
|
||||
using the "unix" protocol. Reported by Martin Sebor.
|
||||
|
||||
CVE-2022-23218: Passing an overlong file name to the svcunix_create
|
||||
legacy function could result in a stack-based buffer overflow.
|
||||
|
||||
CVE-2025-15281: wordexp with WRDE_REUSE and WRDE_APPEND may return
|
||||
uninitialized memory.
|
||||
|
||||
Version 2.31
|
||||
|
||||
Major new features:
|
||||
@@ -141,6 +234,18 @@ Changes to build and runtime requirements:
|
||||
source tree. ChangeLog files are located in the ChangeLog.old directory as
|
||||
ChangeLog.N where the highest N has the latest entries.
|
||||
|
||||
* On Linux, the system administrator needs to configure /dev/pts with
|
||||
the intended access modes for pseudo-terminals. glibc no longer
|
||||
attemps to adjust permissions of terminal devices. The previous glibc
|
||||
defaults ("tty" group, user read/write and group write) already
|
||||
corresponded to what most systems used, so that grantpt did not
|
||||
perform any adjustments.
|
||||
|
||||
* On Linux, the posix_openpt and getpt functions no longer attempt to
|
||||
use legacy (BSD) pseudo-terminals and assume that if /dev/ptmx exists
|
||||
(and pseudo-terminals are supported), a devpts file system is mounted
|
||||
on /dev/pts. Current systems already meet these requirements.
|
||||
|
||||
Security related changes:
|
||||
|
||||
CVE-2019-19126: ld.so failed to ignore the LD_PREFER_MAP_32BIT_EXEC
|
||||
|
||||
@@ -155,6 +155,7 @@ xtests: tests $(xtests-special)
|
||||
else
|
||||
tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \
|
||||
$(tests-container:%=$(objpfx)%.out) \
|
||||
$(tests-mcheck:%=$(objpfx)%-mcheck.out) \
|
||||
$(tests-special) $(tests-printers-out)
|
||||
xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
|
||||
endif
|
||||
@@ -165,7 +166,7 @@ ifeq ($(run-built-tests),no)
|
||||
tests-expected =
|
||||
else
|
||||
tests-expected = $(tests) $(tests-internal) $(tests-printers) \
|
||||
$(tests-container)
|
||||
$(tests-container) $(tests-mcheck:%=%-mcheck)
|
||||
endif
|
||||
tests:
|
||||
$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
|
||||
@@ -191,6 +192,7 @@ else
|
||||
binaries-pie-tests =
|
||||
binaries-pie-notests =
|
||||
endif
|
||||
binaries-mcheck-tests = $(tests-mcheck:%=%-mcheck)
|
||||
else
|
||||
binaries-all-notests =
|
||||
binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs)
|
||||
@@ -200,6 +202,7 @@ binaries-static-tests =
|
||||
binaries-static =
|
||||
binaries-pie-tests =
|
||||
binaries-pie-notests =
|
||||
binaries-mcheck-tests =
|
||||
endif
|
||||
|
||||
binaries-pie = $(binaries-pie-tests) $(binaries-pie-notests)
|
||||
@@ -223,6 +226,14 @@ $(addprefix $(objpfx),$(binaries-shared-tests)): %: %.o \
|
||||
$(+link-tests)
|
||||
endif
|
||||
|
||||
ifneq "$(strip $(binaries-mcheck-tests))" ""
|
||||
$(addprefix $(objpfx),$(binaries-mcheck-tests)): %-mcheck: %.o \
|
||||
$(link-extra-libs-tests) \
|
||||
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
|
||||
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
|
||||
$(+link-tests)
|
||||
endif
|
||||
|
||||
ifneq "$(strip $(binaries-pie-tests))" ""
|
||||
$(addprefix $(objpfx),$(binaries-pie-tests)): %: %.o \
|
||||
$(link-extra-libs-tests) \
|
||||
@@ -253,6 +264,12 @@ $(addprefix $(objpfx),$(binaries-static-tests)): %: %.o \
|
||||
$(+link-static-tests)
|
||||
endif
|
||||
|
||||
# All mcheck tests will be run with MALLOC_CHECK_=3
|
||||
define mcheck-ENVS
|
||||
$(1)-mcheck-ENV = MALLOC_CHECK_=3
|
||||
endef
|
||||
$(foreach t,$(tests-mcheck),$(eval $(call mcheck-ENVS,$(t))))
|
||||
|
||||
ifneq "$(strip $(tests) $(tests-internal) $(xtests) $(test-srcs))" ""
|
||||
# These are the implicit rules for making test outputs
|
||||
# from the test programs and whatever input files are present.
|
||||
|
||||
@@ -4035,7 +4035,7 @@ if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS \
|
||||
-o conftest conftest.S 1>&5 2>&5; then
|
||||
# Do a link to see if the backend supports IFUNC relocs.
|
||||
$READELF -r conftest 1>&5
|
||||
LC_ALL=C $READELF -r conftest | grep 'no relocations' >/dev/null || {
|
||||
LC_ALL=C $READELF -Wr conftest | grep -q 'IRELATIVE\|R_SPARC_JMP_IREL' && {
|
||||
libc_cv_ld_gnu_indirect_function=yes
|
||||
}
|
||||
fi
|
||||
|
||||
+1
-1
@@ -649,7 +649,7 @@ if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS \
|
||||
-o conftest conftest.S 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then
|
||||
# Do a link to see if the backend supports IFUNC relocs.
|
||||
$READELF -r conftest 1>&AS_MESSAGE_LOG_FD
|
||||
LC_ALL=C $READELF -r conftest | grep 'no relocations' >/dev/null || {
|
||||
LC_ALL=C $READELF -Wr conftest | grep -q 'IRELATIVE\|R_SPARC_JMP_IREL' && {
|
||||
libc_cv_ld_gnu_indirect_function=yes
|
||||
}
|
||||
fi
|
||||
|
||||
@@ -168,6 +168,8 @@ extra-libs-others = $(extra-libs)
|
||||
|
||||
libSegFault-routines = segfault
|
||||
libSegFault-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
# libSegFault.so installs a signal handler in its ELF constructor.
|
||||
LDFLAGS-SegFault.so = -Wl,--enable-new-dtags,-z,nodelete
|
||||
|
||||
libpcprofile-routines = pcprofile
|
||||
libpcprofile-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <gnu/lib-names.h>
|
||||
#include <stdlib.h>
|
||||
#include <unwind.h>
|
||||
#include <unwind-arch.h>
|
||||
|
||||
struct trace_arg
|
||||
{
|
||||
@@ -78,6 +79,10 @@ backtrace_helper (struct _Unwind_Context *ctx, void *a)
|
||||
if (arg->cnt != -1)
|
||||
{
|
||||
arg->array[arg->cnt] = (void *) unwind_getip (ctx);
|
||||
if (arg->cnt > 0)
|
||||
arg->array[arg->cnt]
|
||||
= unwind_arch_adjustment (arg->array[arg->cnt - 1],
|
||||
arg->array[arg->cnt]);
|
||||
|
||||
/* Check whether we make any progress. */
|
||||
_Unwind_Word cfa = unwind_getcfa (ctx);
|
||||
|
||||
+2
-2
@@ -1348,6 +1348,8 @@ CFLAGS-ifuncmain7pie.c += $(pie-ccflag)
|
||||
CFLAGS-ifuncmain9pie.c += $(pie-ccflag)
|
||||
CFLAGS-tst-ifunc-textrel.c += $(pic-ccflag)
|
||||
|
||||
LDFLAGS-ifuncmain6pie = -Wl,-z,lazy
|
||||
|
||||
$(objpfx)ifuncmain1pie: $(objpfx)ifuncmod1.so
|
||||
$(objpfx)ifuncmain1staticpie: $(objpfx)ifuncdep1pic.o
|
||||
$(objpfx)ifuncmain1vispie: $(objpfx)ifuncmod1.so
|
||||
@@ -1581,8 +1583,6 @@ $(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \
|
||||
|
||||
tst-env-setuid-ENV = MALLOC_CHECK_=2 MALLOC_MMAP_THRESHOLD_=4096 \
|
||||
LD_HWCAP_MASK=0x1
|
||||
tst-env-setuid-tunables-ENV = \
|
||||
GLIBC_TUNABLES=glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096
|
||||
|
||||
$(objpfx)tst-debug1: $(libdl)
|
||||
$(objpfx)tst-debug1.out: $(objpfx)tst-debug1mod1.so
|
||||
|
||||
+26
-30
@@ -177,6 +177,7 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
return;
|
||||
|
||||
char *p = tunestr;
|
||||
size_t off = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -190,7 +191,11 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
/* If we reach the end of the string before getting a valid name-value
|
||||
pair, bail out. */
|
||||
if (p[len] == '\0')
|
||||
return;
|
||||
{
|
||||
if (__libc_enable_secure)
|
||||
tunestr[off] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
/* We did not find a valid name-value pair before encountering the
|
||||
colon. */
|
||||
@@ -216,35 +221,28 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
|
||||
if (tunable_is_name (cur->name, name))
|
||||
{
|
||||
/* If we are in a secure context (AT_SECURE) then ignore the tunable
|
||||
unless it is explicitly marked as secure. Tunable values take
|
||||
precendence over their envvar aliases. */
|
||||
/* If we are in a secure context (AT_SECURE) then ignore the
|
||||
tunable unless it is explicitly marked as secure. Tunable
|
||||
values take precedence over their envvar aliases. We write
|
||||
the tunables that are not SXID_ERASE back to TUNESTR, thus
|
||||
dropping all SXID_ERASE tunables and any invalid or
|
||||
unrecognized tunables. */
|
||||
if (__libc_enable_secure)
|
||||
{
|
||||
if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
|
||||
if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
|
||||
{
|
||||
if (p[len] == '\0')
|
||||
{
|
||||
/* Last tunable in the valstring. Null-terminate and
|
||||
return. */
|
||||
*name = '\0';
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove the current tunable from the string. We do
|
||||
this by overwriting the string starting from NAME
|
||||
(which is where the current tunable begins) with
|
||||
the remainder of the string. We then have P point
|
||||
to NAME so that we continue in the correct
|
||||
position in the valstring. */
|
||||
char *q = &p[len + 1];
|
||||
p = name;
|
||||
while (*q != '\0')
|
||||
*name++ = *q++;
|
||||
name[0] = '\0';
|
||||
len = 0;
|
||||
}
|
||||
if (off > 0)
|
||||
tunestr[off++] = ':';
|
||||
|
||||
const char *n = cur->name;
|
||||
|
||||
while (*n != '\0')
|
||||
tunestr[off++] = *n++;
|
||||
|
||||
tunestr[off++] = '=';
|
||||
|
||||
for (size_t j = 0; j < len; j++)
|
||||
tunestr[off++] = value[j];
|
||||
}
|
||||
|
||||
if (cur->security_level != TUNABLE_SECLEVEL_NONE)
|
||||
@@ -257,9 +255,7 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
}
|
||||
}
|
||||
|
||||
if (p[len] == '\0')
|
||||
return;
|
||||
else
|
||||
if (p[len] != '\0')
|
||||
p += len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,14 @@ typedef int (*foo_p) (void);
|
||||
#endif
|
||||
|
||||
foo_p foo_ptr = foo;
|
||||
|
||||
/* Address-significant access to protected symbols is not supported in
|
||||
position-dependent mode on several architectures because GCC
|
||||
generates relocations that assume that the address is local to the
|
||||
main program. */
|
||||
#ifdef __PIE__
|
||||
foo_p foo_procted_ptr = foo_protected;
|
||||
#endif
|
||||
|
||||
extern foo_p get_foo_p (void);
|
||||
extern foo_p get_foo_hidden_p (void);
|
||||
@@ -37,12 +44,16 @@ main (void)
|
||||
if ((*foo_ptr) () != -1)
|
||||
abort ();
|
||||
|
||||
#ifdef __PIE__
|
||||
if (foo_procted_ptr != foo_protected)
|
||||
abort ();
|
||||
#endif
|
||||
if (foo_protected () != 0)
|
||||
abort ();
|
||||
#ifdef __PIE__
|
||||
if ((*foo_procted_ptr) () != 0)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = get_foo_p ();
|
||||
if (p != foo)
|
||||
@@ -55,8 +66,10 @@ main (void)
|
||||
abort ();
|
||||
|
||||
p = get_foo_protected_p ();
|
||||
#ifdef __PIE__
|
||||
if (p != foo_protected)
|
||||
abort ();
|
||||
#endif
|
||||
if (ret_foo_protected != 0 || (*p) () != ret_foo_protected)
|
||||
abort ();
|
||||
|
||||
|
||||
@@ -14,12 +14,19 @@ get_foo (void)
|
||||
return foo;
|
||||
}
|
||||
|
||||
|
||||
/* Address-significant access to protected symbols is not supported in
|
||||
position-dependent mode on several architectures because GCC
|
||||
generates relocations that assume that the address is local to the
|
||||
main program. */
|
||||
#ifdef __PIE__
|
||||
foo_p
|
||||
__attribute__ ((noinline))
|
||||
get_foo_protected (void)
|
||||
{
|
||||
return foo_protected;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main (void)
|
||||
@@ -30,9 +37,11 @@ main (void)
|
||||
if ((*p) () != -1)
|
||||
abort ();
|
||||
|
||||
#ifdef __PIE__
|
||||
p = get_foo_protected ();
|
||||
if ((*p) () != 0)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-11
@@ -9,7 +9,6 @@
|
||||
#include "ifunc-sel.h"
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
static int
|
||||
one (void)
|
||||
@@ -28,20 +27,17 @@ foo_ifunc (void)
|
||||
}
|
||||
|
||||
extern int foo (void);
|
||||
extern foo_p get_foo (void);
|
||||
extern int call_foo (void);
|
||||
extern foo_p get_foo_p (void);
|
||||
|
||||
foo_p my_foo_ptr = foo;
|
||||
foo_p foo_ptr = foo;
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
foo_p p;
|
||||
|
||||
p = get_foo ();
|
||||
if (p != foo)
|
||||
abort ();
|
||||
if ((*p) () != -30)
|
||||
if (call_foo () != -30)
|
||||
abort ();
|
||||
|
||||
p = get_foo_p ();
|
||||
@@ -52,12 +48,8 @@ main (void)
|
||||
|
||||
if (foo_ptr != foo)
|
||||
abort ();
|
||||
if (my_foo_ptr != foo)
|
||||
abort ();
|
||||
if ((*foo_ptr) () != -30)
|
||||
abort ();
|
||||
if ((*my_foo_ptr) () != -30)
|
||||
abort ();
|
||||
if (foo () != -30)
|
||||
abort ();
|
||||
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ extern int foo (void);
|
||||
|
||||
typedef int (*foo_p) (void);
|
||||
|
||||
foo_p foo_ptr = foo;
|
||||
extern foo_p foo_ptr;
|
||||
|
||||
foo_p
|
||||
get_foo_p (void)
|
||||
@@ -12,8 +12,8 @@ get_foo_p (void)
|
||||
return foo_ptr;
|
||||
}
|
||||
|
||||
foo_p
|
||||
get_foo (void)
|
||||
int
|
||||
call_foo (void)
|
||||
{
|
||||
return foo;
|
||||
return foo ();
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ $(objpfx)rtld-libc.a: $(foreach dir,$(rtld-subdirs),\
|
||||
mv -f $@T $@
|
||||
|
||||
# Use the verbose option of ar and tar when not running silently.
|
||||
ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s
|
||||
ifeq ($(silent-make),no) # if not -s
|
||||
verbose := v
|
||||
else # -s
|
||||
verbose :=
|
||||
|
||||
@@ -25,35 +25,76 @@
|
||||
#include "config.h"
|
||||
#undef _LIBC
|
||||
|
||||
#define test_parent test_parent_tunables
|
||||
#define test_child test_child_tunables
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <intprops.h>
|
||||
#include <array_length.h>
|
||||
|
||||
static int test_child_tunables (void);
|
||||
static int test_parent_tunables (void);
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
#include "tst-env-setuid.c"
|
||||
const char *teststrings[] =
|
||||
{
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.check=2:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
|
||||
"glibc.malloc.perturb=0x800",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
|
||||
"glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
|
||||
":glibc.malloc.garbage=2:glibc.malloc.check=1",
|
||||
"glibc.malloc.check=1:glibc.malloc.check=2",
|
||||
"not_valid.malloc.check=2",
|
||||
"glibc.not_valid.check=2",
|
||||
};
|
||||
|
||||
#define CHILD_VALSTRING_VALUE "glibc.malloc.mmap_threshold=4096"
|
||||
#define PARENT_VALSTRING_VALUE \
|
||||
"glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096"
|
||||
const char *resultstrings[] =
|
||||
{
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"glibc.malloc.mmap_threshold=4096",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
};
|
||||
|
||||
static int
|
||||
test_child_tunables (void)
|
||||
test_child (int off)
|
||||
{
|
||||
const char *val = getenv ("GLIBC_TUNABLES");
|
||||
|
||||
#if HAVE_TUNABLES
|
||||
if (val != NULL && strcmp (val, CHILD_VALSTRING_VALUE) == 0)
|
||||
if (val != NULL && strcmp (val, resultstrings[off]) == 0)
|
||||
return 0;
|
||||
|
||||
if (val != NULL)
|
||||
printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
|
||||
printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val);
|
||||
|
||||
return 1;
|
||||
#else
|
||||
if (val != NULL)
|
||||
{
|
||||
printf ("GLIBC_TUNABLES not cleared\n");
|
||||
printf ("[%d] GLIBC_TUNABLES not cleared\n", off);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -61,15 +102,48 @@ test_child_tunables (void)
|
||||
}
|
||||
|
||||
static int
|
||||
test_parent_tunables (void)
|
||||
do_test (int argc, char **argv)
|
||||
{
|
||||
const char *val = getenv ("GLIBC_TUNABLES");
|
||||
/* Setgid child process. */
|
||||
if (argc == 2)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
|
||||
if (val != NULL && strcmp (val, PARENT_VALSTRING_VALUE) == 0)
|
||||
return 0;
|
||||
int ret = test_child (atoi (argv[1]));
|
||||
|
||||
if (val != NULL)
|
||||
printf ("Unexpected GLIBC_TUNABLES VALUE %s\n", val);
|
||||
if (ret != 0)
|
||||
exit (1);
|
||||
|
||||
return 1;
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Spawn tests. */
|
||||
for (int i = 0; i < array_length (teststrings); i++)
|
||||
{
|
||||
char buf[INT_BUFSIZE_BOUND (int)];
|
||||
|
||||
printf ("Spawned test for %s (%d)\n", teststrings[i], i);
|
||||
snprintf (buf, sizeof (buf), "%d\n", i);
|
||||
if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0)
|
||||
exit (1);
|
||||
|
||||
int status = support_capture_subprogram_self_sgid (buf);
|
||||
|
||||
/* Bail out early if unsupported. */
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
ret |= status;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION_ARGV do_test
|
||||
#include <support/test-driver.c>
|
||||
|
||||
+14
-183
@@ -29,173 +29,12 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
static char SETGID_CHILD[] = "setgid-child";
|
||||
#define CHILD_STATUS 42
|
||||
|
||||
/* Return a GID which is not our current GID, but is present in the
|
||||
supplementary group list. */
|
||||
static gid_t
|
||||
choose_gid (void)
|
||||
{
|
||||
const int count = 64;
|
||||
gid_t groups[count];
|
||||
int ret = getgroups (count, groups);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf ("getgroups: %m\n");
|
||||
exit (1);
|
||||
}
|
||||
gid_t current = getgid ();
|
||||
for (int i = 0; i < ret; ++i)
|
||||
{
|
||||
if (groups[i] != current)
|
||||
return groups[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Spawn and execute a program and verify that it returns the CHILD_STATUS. */
|
||||
static pid_t
|
||||
do_execve (char **args)
|
||||
{
|
||||
pid_t kid = vfork ();
|
||||
|
||||
if (kid < 0)
|
||||
{
|
||||
printf ("vfork: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (kid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
execve (args[0], args, environ);
|
||||
_exit (-errno);
|
||||
}
|
||||
|
||||
if (kid < 0)
|
||||
return 1;
|
||||
|
||||
int status;
|
||||
|
||||
if (waitpid (kid, &status, 0) < 0)
|
||||
{
|
||||
printf ("waitpid: %m\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != CHILD_STATUS)
|
||||
{
|
||||
printf ("Unexpected exit status %d from child process\n",
|
||||
WEXITSTATUS (status));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copies the executable into a restricted directory, so that we can
|
||||
safely make it SGID with the TARGET group ID. Then runs the
|
||||
executable. */
|
||||
static int
|
||||
run_executable_sgid (gid_t target)
|
||||
{
|
||||
char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
|
||||
test_dir, (intmax_t) getpid ());
|
||||
char *execname = xasprintf ("%s/bin", dirname);
|
||||
int infd = -1;
|
||||
int outfd = -1;
|
||||
int ret = 0;
|
||||
if (mkdir (dirname, 0700) < 0)
|
||||
{
|
||||
printf ("mkdir: %m\n");
|
||||
goto err;
|
||||
}
|
||||
infd = open ("/proc/self/exe", O_RDONLY);
|
||||
if (infd < 0)
|
||||
{
|
||||
printf ("open (/proc/self/exe): %m\n");
|
||||
goto err;
|
||||
}
|
||||
outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
|
||||
if (outfd < 0)
|
||||
{
|
||||
printf ("open (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
char buf[4096];
|
||||
for (;;)
|
||||
{
|
||||
ssize_t rdcount = read (infd, buf, sizeof (buf));
|
||||
if (rdcount < 0)
|
||||
{
|
||||
printf ("read: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (rdcount == 0)
|
||||
break;
|
||||
char *p = buf;
|
||||
char *end = buf + rdcount;
|
||||
while (p != end)
|
||||
{
|
||||
ssize_t wrcount = write (outfd, buf, end - p);
|
||||
if (wrcount == 0)
|
||||
errno = ENOSPC;
|
||||
if (wrcount <= 0)
|
||||
{
|
||||
printf ("write: %m\n");
|
||||
goto err;
|
||||
}
|
||||
p += wrcount;
|
||||
}
|
||||
}
|
||||
if (fchown (outfd, getuid (), target) < 0)
|
||||
{
|
||||
printf ("fchown (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (fchmod (outfd, 02750) < 0)
|
||||
{
|
||||
printf ("fchmod (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (close (outfd) < 0)
|
||||
{
|
||||
printf ("close (outfd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (close (infd) < 0)
|
||||
{
|
||||
printf ("close (infd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
char *args[] = {execname, SETGID_CHILD, NULL};
|
||||
|
||||
ret = do_execve (args);
|
||||
|
||||
err:
|
||||
if (outfd >= 0)
|
||||
close (outfd);
|
||||
if (infd >= 0)
|
||||
close (infd);
|
||||
if (execname)
|
||||
{
|
||||
unlink (execname);
|
||||
free (execname);
|
||||
}
|
||||
if (dirname)
|
||||
{
|
||||
rmdir (dirname);
|
||||
free (dirname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef test_child
|
||||
static int
|
||||
@@ -256,40 +95,32 @@ do_test (int argc, char **argv)
|
||||
if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
{
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
exit (EXIT_UNSUPPORTED);
|
||||
}
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
|
||||
int ret = test_child ();
|
||||
|
||||
if (ret != 0)
|
||||
exit (1);
|
||||
|
||||
exit (CHILD_STATUS);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test_parent () != 0)
|
||||
exit (1);
|
||||
|
||||
/* Try running a setgid program. */
|
||||
gid_t target = choose_gid ();
|
||||
if (target == 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Could not find a suitable GID for user %jd, skipping test\n",
|
||||
(intmax_t) getuid ());
|
||||
exit (0);
|
||||
}
|
||||
int status = support_capture_subprogram_self_sgid (SETGID_CHILD);
|
||||
|
||||
return run_executable_sgid (target);
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status))
|
||||
FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Something went wrong and our argv was corrupted. */
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION_ARGV do_test
|
||||
|
||||
+15
-2
@@ -26,7 +26,7 @@ headers = iconv.h gconv.h
|
||||
routines = iconv_open iconv iconv_close \
|
||||
gconv_open gconv gconv_close gconv_db gconv_conf \
|
||||
gconv_builtin gconv_simple gconv_trans gconv_cache
|
||||
routines += gconv_dl
|
||||
routines += gconv_dl gconv_charset
|
||||
|
||||
vpath %.c ../locale/programs ../intl
|
||||
|
||||
@@ -44,7 +44,7 @@ CFLAGS-linereader.c += -DNO_TRANSLITERATION
|
||||
CFLAGS-simple-hash.c += -I../locale
|
||||
|
||||
tests = tst-iconv1 tst-iconv2 tst-iconv3 tst-iconv4 tst-iconv5 tst-iconv6 \
|
||||
tst-iconv7 tst-iconv-mt
|
||||
tst-iconv7 tst-iconv8 tst-iconv-mt tst-iconv-opt
|
||||
|
||||
others = iconv_prog iconvconfig
|
||||
install-others-programs = $(inst_bindir)/iconv
|
||||
@@ -61,6 +61,7 @@ include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
xtests-special += $(objpfx)test-iconvconfig.out
|
||||
tests-special += $(objpfx)tst-iconv_prog.out
|
||||
endif
|
||||
|
||||
# Make a copy of the file because gconv module names are constructed
|
||||
@@ -81,6 +82,13 @@ endif
|
||||
|
||||
include ../Rules
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
LOCALES := en_US.UTF-8
|
||||
include ../gen-locales.mk
|
||||
|
||||
$(objpfx)tst-iconv-opt.out: $(gen-locales)
|
||||
endif
|
||||
|
||||
$(inst_bindir)/iconv: $(objpfx)iconv_prog $(+force)
|
||||
$(do-install-program)
|
||||
|
||||
@@ -95,3 +103,8 @@ $(objpfx)test-iconvconfig.out: /dev/null $(objpfx)iconvconfig
|
||||
cmp $$tmp $(inst_gconvdir)/gconv-modules.cache; \
|
||||
rm -f $$tmp) > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog
|
||||
$(BASH) $< $(common-objdir) '$(test-wrapper-env)' \
|
||||
'$(run-program-env)' > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
@@ -7,6 +7,9 @@ libc {
|
||||
# functions shared with iconv program
|
||||
__gconv_get_alias_db; __gconv_get_cache; __gconv_get_modules_db;
|
||||
|
||||
# functions used elsewhere in glibc
|
||||
__gconv_open; __gconv_create_spec; __gconv_destroy_spec;
|
||||
|
||||
# function used by the gconv modules
|
||||
__gconv_transliterate;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/* Charset name normalization.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "gconv_int.h"
|
||||
#include "gconv_charset.h"
|
||||
|
||||
|
||||
/* This function returns a pointer to the last suffix in a conversion code
|
||||
string. Valid suffixes matched by this function are of the form: '/' or ','
|
||||
followed by arbitrary text that doesn't contain '/' or ','. It does not
|
||||
edit the string in any way. The caller is expected to parse the suffix and
|
||||
remove it (by e.g. truncating the string) before the next call. */
|
||||
static char *
|
||||
find_suffix (char *s)
|
||||
{
|
||||
/* The conversion code is in the form of a triplet, separated by '/' chars.
|
||||
The third component of the triplet contains suffixes. If we don't have two
|
||||
slashes, we don't have a suffix. */
|
||||
|
||||
int slash_count = 0;
|
||||
char *suffix_term = NULL;
|
||||
|
||||
for (int i = 0; s[i] != '\0'; i++)
|
||||
switch (s[i])
|
||||
{
|
||||
case '/':
|
||||
slash_count++;
|
||||
/* Fallthrough */
|
||||
case ',':
|
||||
suffix_term = &s[i];
|
||||
}
|
||||
|
||||
if (slash_count >= 2)
|
||||
return suffix_term;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct gconv_parsed_code
|
||||
{
|
||||
char *code;
|
||||
bool translit;
|
||||
bool ignore;
|
||||
};
|
||||
|
||||
|
||||
/* This function parses an iconv_open encoding PC.CODE, strips any suffixes
|
||||
(such as TRANSLIT or IGNORE) from it and sets corresponding flags in it. */
|
||||
static void
|
||||
gconv_parse_code (struct gconv_parsed_code *pc)
|
||||
{
|
||||
pc->translit = false;
|
||||
pc->ignore = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* First drop any trailing whitespaces and separators. */
|
||||
size_t len = strlen (pc->code);
|
||||
while ((len > 0)
|
||||
&& (isspace (pc->code[len - 1])
|
||||
|| pc->code[len - 1] == ','
|
||||
|| pc->code[len - 1] == '/'))
|
||||
len--;
|
||||
|
||||
pc->code[len] = '\0';
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
char * suffix = find_suffix (pc->code);
|
||||
if (suffix == NULL)
|
||||
{
|
||||
/* At this point, we have processed and removed all suffixes from the
|
||||
code and what remains of the code is suffix free. */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* A suffix is processed from the end of the code array going
|
||||
backwards, one suffix at a time. The suffix is an index into the
|
||||
code character array and points to: one past the end of the code
|
||||
and any unprocessed suffixes, and to the beginning of the suffix
|
||||
currently being processed during this iteration. We must process
|
||||
this suffix and then drop it from the code by terminating the
|
||||
preceding text with NULL.
|
||||
|
||||
We want to allow and recognize suffixes such as:
|
||||
|
||||
"/TRANSLIT" i.e. single suffix
|
||||
"//TRANSLIT" i.e. single suffix and multiple separators
|
||||
"//TRANSLIT/IGNORE" i.e. suffixes separated by "/"
|
||||
"/TRANSLIT//IGNORE" i.e. suffixes separated by "//"
|
||||
"//IGNORE,TRANSLIT" i.e. suffixes separated by ","
|
||||
"//IGNORE," i.e. trailing ","
|
||||
"//TRANSLIT/" i.e. trailing "/"
|
||||
"//TRANSLIT//" i.e. trailing "//"
|
||||
"/" i.e. empty suffix.
|
||||
|
||||
Unknown suffixes are silently discarded and ignored. */
|
||||
|
||||
if ((__strcasecmp_l (suffix,
|
||||
GCONV_TRIPLE_SEPARATOR
|
||||
GCONV_TRANSLIT_SUFFIX,
|
||||
_nl_C_locobj_ptr) == 0)
|
||||
|| (__strcasecmp_l (suffix,
|
||||
GCONV_SUFFIX_SEPARATOR
|
||||
GCONV_TRANSLIT_SUFFIX,
|
||||
_nl_C_locobj_ptr) == 0))
|
||||
pc->translit = true;
|
||||
|
||||
if ((__strcasecmp_l (suffix,
|
||||
GCONV_TRIPLE_SEPARATOR
|
||||
GCONV_IGNORE_ERRORS_SUFFIX,
|
||||
_nl_C_locobj_ptr) == 0)
|
||||
|| (__strcasecmp_l (suffix,
|
||||
GCONV_SUFFIX_SEPARATOR
|
||||
GCONV_IGNORE_ERRORS_SUFFIX,
|
||||
_nl_C_locobj_ptr) == 0))
|
||||
pc->ignore = true;
|
||||
|
||||
/* We just processed this suffix. We can now drop it from the
|
||||
code string by truncating it at the suffix's position. */
|
||||
suffix[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This function accepts the charset names of the source and destination of the
|
||||
conversion and populates *conv_spec with an equivalent conversion
|
||||
specification that may later be used by __gconv_open. The charset names
|
||||
might contain options in the form of suffixes that alter the conversion,
|
||||
e.g. "ISO-10646/UTF-8/TRANSLIT". It processes the charset names, ignoring
|
||||
and truncating any suffix options in fromcode, and processing and truncating
|
||||
any suffix options in tocode. Supported suffix options ("TRANSLIT" or
|
||||
"IGNORE") when found in tocode lead to the corresponding flag in *conv_spec
|
||||
to be set to true. Unrecognized suffix options are silently discarded. If
|
||||
the function succeeds, it returns conv_spec back to the caller. It returns
|
||||
NULL upon failure. conv_spec must be allocated and freed by the caller. */
|
||||
struct gconv_spec *
|
||||
__gconv_create_spec (struct gconv_spec *conv_spec, const char *fromcode,
|
||||
const char *tocode)
|
||||
{
|
||||
struct gconv_parsed_code pfc, ptc;
|
||||
struct gconv_spec *ret = NULL;
|
||||
|
||||
pfc.code = __strdup (fromcode);
|
||||
ptc.code = __strdup (tocode);
|
||||
|
||||
if ((pfc.code == NULL)
|
||||
|| (ptc.code == NULL))
|
||||
goto out;
|
||||
|
||||
gconv_parse_code (&pfc);
|
||||
gconv_parse_code (&ptc);
|
||||
|
||||
/* We ignore suffixes in the fromcode because that is how the current
|
||||
implementation has always handled them. Only suffixes in the tocode are
|
||||
processed and handled. The reality is that invalid input in the input
|
||||
character set should only be ignored if the fromcode specifies IGNORE.
|
||||
The current implementation ignores invalid intput in the input character
|
||||
set if the tocode contains IGNORE. We preserve this behavior for
|
||||
backwards compatibility. In the future we may split the handling of
|
||||
IGNORE to allow a finer grained specification of ignorning invalid input
|
||||
and/or ignoring invalid output. */
|
||||
conv_spec->translit = ptc.translit;
|
||||
conv_spec->ignore = ptc.ignore;
|
||||
|
||||
/* 3 extra bytes because 1 extra for '\0', and 2 extra so strip might
|
||||
be able to add one or two trailing '/' characters if necessary. */
|
||||
conv_spec->fromcode = malloc (strlen (fromcode) + 3);
|
||||
if (conv_spec->fromcode == NULL)
|
||||
goto out;
|
||||
|
||||
conv_spec->tocode = malloc (strlen (tocode) + 3);
|
||||
if (conv_spec->tocode == NULL)
|
||||
{
|
||||
free (conv_spec->fromcode);
|
||||
conv_spec->fromcode = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Strip unrecognized characters and ensure that the code has two '/'
|
||||
characters as per conversion code triplet specification. */
|
||||
strip (conv_spec->fromcode, pfc.code);
|
||||
strip (conv_spec->tocode, ptc.code);
|
||||
ret = conv_spec;
|
||||
|
||||
out:
|
||||
free (pfc.code);
|
||||
free (ptc.code);
|
||||
|
||||
return ret;
|
||||
}
|
||||
libc_hidden_def (__gconv_create_spec)
|
||||
|
||||
|
||||
void
|
||||
__gconv_destroy_spec (struct gconv_spec *conv_spec)
|
||||
{
|
||||
free (conv_spec->fromcode);
|
||||
free (conv_spec->tocode);
|
||||
return;
|
||||
}
|
||||
libc_hidden_def (__gconv_destroy_spec)
|
||||
+33
-1
@@ -19,9 +19,41 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include "gconv_int.h"
|
||||
|
||||
|
||||
static void
|
||||
/* An iconv encoding is in the form of a triplet, with parts separated by
|
||||
a '/' character. The first part is the standard name, the second part is
|
||||
the character set, and the third part is the error handler. If the first
|
||||
part is sufficient to identify both the standard and the character set
|
||||
then the second part can be empty e.g. UTF-8//. If the first part is not
|
||||
sufficient to identify both the standard and the character set then the
|
||||
second part is required e.g. ISO-10646/UTF8/. If neither the first or
|
||||
second parts are provided e.g. //, then the current locale is used.
|
||||
The actual values used in the first and second parts are not entirely
|
||||
relevant to the implementation. The values themselves are used in a hash
|
||||
table to lookup modules and so the naming convention of the first two parts
|
||||
is somewhat arbitrary and only helps locate the entries in the cache.
|
||||
The third part is the error handler and is comprised of a ',' or '/'
|
||||
separated list of suffixes. Currently, we support "TRANSLIT" for
|
||||
transliteration and "IGNORE" for ignoring conversion errors due to
|
||||
unrecognized input characters. */
|
||||
#define GCONV_TRIPLE_SEPARATOR "/"
|
||||
#define GCONV_SUFFIX_SEPARATOR ","
|
||||
#define GCONV_TRANSLIT_SUFFIX "TRANSLIT"
|
||||
#define GCONV_IGNORE_ERRORS_SUFFIX "IGNORE"
|
||||
|
||||
|
||||
/* This function copies in-order, characters from the source 's' that are
|
||||
either alpha-numeric or one in one of these: "_-.,:/" - into the destination
|
||||
'wp' while dropping all other characters. In the process, it converts all
|
||||
alphabetical characters to upper case. It then appends up to two '/'
|
||||
characters so that the total number of '/'es in the destination is 2. */
|
||||
static inline void __attribute__ ((unused, always_inline))
|
||||
strip (char *wp, const char *s)
|
||||
{
|
||||
int slash_count = 0;
|
||||
|
||||
+36
-4
@@ -75,6 +75,15 @@ struct gconv_module
|
||||
};
|
||||
|
||||
|
||||
/* The specification of the conversion that needs to be performed. */
|
||||
struct gconv_spec
|
||||
{
|
||||
char *fromcode;
|
||||
char *tocode;
|
||||
bool translit;
|
||||
bool ignore;
|
||||
};
|
||||
|
||||
/* Flags for `gconv_open'. */
|
||||
enum
|
||||
{
|
||||
@@ -136,10 +145,33 @@ __libc_lock_define (extern, __gconv_lock attribute_hidden)
|
||||
})
|
||||
|
||||
|
||||
/* Return in *HANDLE decriptor for transformation from FROMSET to TOSET. */
|
||||
extern int __gconv_open (const char *toset, const char *fromset,
|
||||
__gconv_t *handle, int flags)
|
||||
attribute_hidden;
|
||||
/* Return in *HANDLE, a decriptor for the transformation. The function expects
|
||||
the specification of the transformation in the structure pointed to by
|
||||
CONV_SPEC. It only reads *CONV_SPEC and does not take ownership of it. */
|
||||
extern int __gconv_open (struct gconv_spec *conv_spec,
|
||||
__gconv_t *handle, int flags);
|
||||
libc_hidden_proto (__gconv_open)
|
||||
|
||||
/* This function accepts the charset names of the source and destination of the
|
||||
conversion and populates *conv_spec with an equivalent conversion
|
||||
specification that may later be used by __gconv_open. The charset names
|
||||
might contain options in the form of suffixes that alter the conversion,
|
||||
e.g. "ISO-10646/UTF-8/TRANSLIT". It processes the charset names, ignoring
|
||||
and truncating any suffix options in fromcode, and processing and truncating
|
||||
any suffix options in tocode. Supported suffix options ("TRANSLIT" or
|
||||
"IGNORE") when found in tocode lead to the corresponding flag in *conv_spec
|
||||
to be set to true. Unrecognized suffix options are silently discarded. If
|
||||
the function succeeds, it returns conv_spec back to the caller. It returns
|
||||
NULL upon failure. */
|
||||
extern struct gconv_spec *
|
||||
__gconv_create_spec (struct gconv_spec *conv_spec, const char *fromcode,
|
||||
const char *tocode);
|
||||
libc_hidden_proto (__gconv_create_spec)
|
||||
|
||||
/* This function frees all heap memory allocated by __gconv_create_spec. */
|
||||
extern void
|
||||
__gconv_destroy_spec (struct gconv_spec *conv_spec);
|
||||
libc_hidden_proto (__gconv_destroy_spec)
|
||||
|
||||
/* Free resources associated with transformation descriptor CD. */
|
||||
extern int __gconv_close (__gconv_t cd)
|
||||
|
||||
+13
-51
@@ -31,7 +31,7 @@
|
||||
|
||||
|
||||
int
|
||||
__gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
|
||||
__gconv_open (struct gconv_spec *conv_spec, __gconv_t *handle,
|
||||
int flags)
|
||||
{
|
||||
struct __gconv_step *steps;
|
||||
@@ -40,77 +40,38 @@ __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
|
||||
size_t cnt = 0;
|
||||
int res;
|
||||
int conv_flags = 0;
|
||||
const char *errhand;
|
||||
const char *ignore;
|
||||
bool translit = false;
|
||||
char *tocode, *fromcode;
|
||||
|
||||
/* Find out whether any error handling method is specified. */
|
||||
errhand = strchr (toset, '/');
|
||||
if (errhand != NULL)
|
||||
errhand = strchr (errhand + 1, '/');
|
||||
if (__glibc_likely (errhand != NULL))
|
||||
{
|
||||
if (*++errhand == '\0')
|
||||
errhand = NULL;
|
||||
else
|
||||
{
|
||||
/* Make copy without the error handling description. */
|
||||
char *newtoset = (char *) alloca (errhand - toset + 1);
|
||||
char *tok;
|
||||
char *ptr = NULL /* Work around a bogus warning */;
|
||||
translit = conv_spec->translit;
|
||||
|
||||
newtoset[errhand - toset] = '\0';
|
||||
toset = memcpy (newtoset, toset, errhand - toset);
|
||||
if (conv_spec->ignore)
|
||||
conv_flags |= __GCONV_IGNORE_ERRORS;
|
||||
|
||||
/* Find the appropriate transliteration handlers. */
|
||||
tok = strdupa (errhand);
|
||||
|
||||
tok = __strtok_r (tok, ",", &ptr);
|
||||
while (tok != NULL)
|
||||
{
|
||||
if (__strcasecmp_l (tok, "TRANSLIT", _nl_C_locobj_ptr) == 0)
|
||||
translit = true;
|
||||
else if (__strcasecmp_l (tok, "IGNORE", _nl_C_locobj_ptr) == 0)
|
||||
/* Set the flag to ignore all errors. */
|
||||
conv_flags |= __GCONV_IGNORE_ERRORS;
|
||||
|
||||
tok = __strtok_r (NULL, ",", &ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For the source character set we ignore the error handler specification.
|
||||
XXX Is this really always the best? */
|
||||
ignore = strchr (fromset, '/');
|
||||
if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
|
||||
&& *++ignore != '\0')
|
||||
{
|
||||
char *newfromset = (char *) alloca (ignore - fromset + 1);
|
||||
|
||||
newfromset[ignore - fromset] = '\0';
|
||||
fromset = memcpy (newfromset, fromset, ignore - fromset);
|
||||
}
|
||||
tocode = conv_spec->tocode;
|
||||
fromcode = conv_spec->fromcode;
|
||||
|
||||
/* If the string is empty define this to mean the charset of the
|
||||
currently selected locale. */
|
||||
if (strcmp (toset, "//") == 0)
|
||||
if (strcmp (tocode, "//") == 0)
|
||||
{
|
||||
const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
|
||||
size_t len = strlen (codeset);
|
||||
char *dest;
|
||||
toset = dest = (char *) alloca (len + 3);
|
||||
tocode = dest = (char *) alloca (len + 3);
|
||||
memcpy (__mempcpy (dest, codeset, len), "//", 3);
|
||||
}
|
||||
if (strcmp (fromset, "//") == 0)
|
||||
if (strcmp (fromcode, "//") == 0)
|
||||
{
|
||||
const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
|
||||
size_t len = strlen (codeset);
|
||||
char *dest;
|
||||
fromset = dest = (char *) alloca (len + 3);
|
||||
fromcode = dest = (char *) alloca (len + 3);
|
||||
memcpy (__mempcpy (dest, codeset, len), "//", 3);
|
||||
}
|
||||
|
||||
res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
|
||||
res = __gconv_find_transform (tocode, fromcode, &steps, &nsteps, flags);
|
||||
if (res == __GCONV_OK)
|
||||
{
|
||||
/* Allocate room for handle. */
|
||||
@@ -209,3 +170,4 @@ __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
|
||||
*handle = result;
|
||||
return res;
|
||||
}
|
||||
libc_hidden_def (__gconv_open)
|
||||
|
||||
+4
-12
@@ -239,11 +239,9 @@ ucs4_internal_loop (struct __gconv_step *step,
|
||||
int flags = step_data->__flags;
|
||||
const unsigned char *inptr = *inptrp;
|
||||
unsigned char *outptr = *outptrp;
|
||||
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
|
||||
int result;
|
||||
size_t cnt;
|
||||
|
||||
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
|
||||
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
|
||||
{
|
||||
uint32_t inval;
|
||||
|
||||
@@ -307,11 +305,9 @@ ucs4_internal_loop_unaligned (struct __gconv_step *step,
|
||||
int flags = step_data->__flags;
|
||||
const unsigned char *inptr = *inptrp;
|
||||
unsigned char *outptr = *outptrp;
|
||||
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
|
||||
int result;
|
||||
size_t cnt;
|
||||
|
||||
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
|
||||
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
|
||||
{
|
||||
if (__glibc_unlikely (inptr[0] > 0x80))
|
||||
{
|
||||
@@ -613,11 +609,9 @@ ucs4le_internal_loop (struct __gconv_step *step,
|
||||
int flags = step_data->__flags;
|
||||
const unsigned char *inptr = *inptrp;
|
||||
unsigned char *outptr = *outptrp;
|
||||
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
|
||||
int result;
|
||||
size_t cnt;
|
||||
|
||||
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
|
||||
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
|
||||
{
|
||||
uint32_t inval;
|
||||
|
||||
@@ -684,11 +678,9 @@ ucs4le_internal_loop_unaligned (struct __gconv_step *step,
|
||||
int flags = step_data->__flags;
|
||||
const unsigned char *inptr = *inptrp;
|
||||
unsigned char *outptr = *outptrp;
|
||||
size_t n_convert = MIN (inend - inptr, outend - outptr) / 4;
|
||||
int result;
|
||||
size_t cnt;
|
||||
|
||||
for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4)
|
||||
for (; inptr + 4 <= inend && outptr + 4 <= outend; inptr += 4)
|
||||
{
|
||||
if (__glibc_unlikely (inptr[3] > 0x80))
|
||||
{
|
||||
|
||||
+7
-41
@@ -31,49 +31,15 @@
|
||||
iconv_t
|
||||
iconv_open (const char *tocode, const char *fromcode)
|
||||
{
|
||||
/* Normalize the name. We remove all characters beside alpha-numeric,
|
||||
'_', '-', '/', '.', and ':'. */
|
||||
size_t tocode_len = strlen (tocode) + 3;
|
||||
char *tocode_conv;
|
||||
bool tocode_usealloca = __libc_use_alloca (tocode_len);
|
||||
if (tocode_usealloca)
|
||||
tocode_conv = (char *) alloca (tocode_len);
|
||||
else
|
||||
{
|
||||
tocode_conv = (char *) malloc (tocode_len);
|
||||
if (tocode_conv == NULL)
|
||||
return (iconv_t) -1;
|
||||
}
|
||||
strip (tocode_conv, tocode);
|
||||
tocode = (tocode_conv[2] == '\0' && tocode[0] != '\0'
|
||||
? upstr (tocode_conv, tocode) : tocode_conv);
|
||||
|
||||
size_t fromcode_len = strlen (fromcode) + 3;
|
||||
char *fromcode_conv;
|
||||
bool fromcode_usealloca = __libc_use_alloca (fromcode_len);
|
||||
if (fromcode_usealloca)
|
||||
fromcode_conv = (char *) alloca (fromcode_len);
|
||||
else
|
||||
{
|
||||
fromcode_conv = (char *) malloc (fromcode_len);
|
||||
if (fromcode_conv == NULL)
|
||||
{
|
||||
if (! tocode_usealloca)
|
||||
free (tocode_conv);
|
||||
return (iconv_t) -1;
|
||||
}
|
||||
}
|
||||
strip (fromcode_conv, fromcode);
|
||||
fromcode = (fromcode_conv[2] == '\0' && fromcode[0] != '\0'
|
||||
? upstr (fromcode_conv, fromcode) : fromcode_conv);
|
||||
|
||||
__gconv_t cd;
|
||||
int res = __gconv_open (tocode, fromcode, &cd, 0);
|
||||
struct gconv_spec conv_spec;
|
||||
|
||||
if (! fromcode_usealloca)
|
||||
free (fromcode_conv);
|
||||
if (! tocode_usealloca)
|
||||
free (tocode_conv);
|
||||
if (__gconv_create_spec (&conv_spec, fromcode, tocode) == NULL)
|
||||
return (iconv_t) -1;
|
||||
|
||||
int res = __gconv_open (&conv_spec, &cd, 0);
|
||||
|
||||
__gconv_destroy_spec (&conv_spec);
|
||||
|
||||
if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
|
||||
{
|
||||
|
||||
+23
-40
@@ -39,6 +39,7 @@
|
||||
#include <gconv_int.h>
|
||||
#include "iconv_prog.h"
|
||||
#include "iconvconfig.h"
|
||||
#include "gconv_charset.h"
|
||||
|
||||
/* Get libc version number. */
|
||||
#include "../version.h"
|
||||
@@ -118,8 +119,7 @@ main (int argc, char *argv[])
|
||||
{
|
||||
int status = EXIT_SUCCESS;
|
||||
int remaining;
|
||||
iconv_t cd;
|
||||
const char *orig_to_code;
|
||||
__gconv_t cd;
|
||||
struct charmap_t *from_charmap = NULL;
|
||||
struct charmap_t *to_charmap = NULL;
|
||||
|
||||
@@ -139,39 +139,6 @@ main (int argc, char *argv[])
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* If we have to ignore errors make sure we use the appropriate name for
|
||||
the to-character-set. */
|
||||
orig_to_code = to_code;
|
||||
if (omit_invalid)
|
||||
{
|
||||
const char *errhand = strchrnul (to_code, '/');
|
||||
int nslash = 2;
|
||||
char *newp;
|
||||
char *cp;
|
||||
|
||||
if (*errhand == '/')
|
||||
{
|
||||
--nslash;
|
||||
errhand = strchrnul (errhand + 1, '/');
|
||||
|
||||
if (*errhand == '/')
|
||||
{
|
||||
--nslash;
|
||||
errhand = strchr (errhand, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
newp = (char *) alloca (errhand - to_code + nslash + 7 + 1);
|
||||
cp = mempcpy (newp, to_code, errhand - to_code);
|
||||
while (nslash-- > 0)
|
||||
*cp++ = '/';
|
||||
if (cp[-1] != '/')
|
||||
*cp++ = ',';
|
||||
memcpy (cp, "IGNORE", sizeof ("IGNORE"));
|
||||
|
||||
to_code = newp;
|
||||
}
|
||||
|
||||
/* POSIX 1003.2b introduces a silly thing: the arguments to -t anf -f
|
||||
can be file names of charmaps. In this case iconv will have to read
|
||||
those charmaps and use them to do the conversion. But there are
|
||||
@@ -184,10 +151,10 @@ main (int argc, char *argv[])
|
||||
file. */
|
||||
from_charmap = charmap_read (from_code, /*0, 1*/1, 0, 0, 0);
|
||||
|
||||
if (strchr (orig_to_code, '/') != NULL)
|
||||
if (strchr (to_code, '/') != NULL)
|
||||
/* The to-name might be a charmap file name. Try reading the
|
||||
file. */
|
||||
to_charmap = charmap_read (orig_to_code, /*0, 1,*/1, 0, 0, 0);
|
||||
to_charmap = charmap_read (to_code, /*0, 1,*/1, 0, 0, 0);
|
||||
|
||||
|
||||
/* At this point we have to handle two cases. The first one is
|
||||
@@ -201,9 +168,25 @@ main (int argc, char *argv[])
|
||||
argc, remaining, argv, output_file);
|
||||
else
|
||||
{
|
||||
struct gconv_spec conv_spec;
|
||||
int res;
|
||||
|
||||
if (__gconv_create_spec (&conv_spec, from_code, to_code) == NULL)
|
||||
{
|
||||
error (EXIT_FAILURE, errno,
|
||||
_("failed to start conversion processing"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (omit_invalid)
|
||||
conv_spec.ignore = true;
|
||||
|
||||
/* Let's see whether we have these coded character sets. */
|
||||
cd = iconv_open (to_code, from_code);
|
||||
if (cd == (iconv_t) -1)
|
||||
res = __gconv_open (&conv_spec, &cd, 0);
|
||||
|
||||
__gconv_destroy_spec (&conv_spec);
|
||||
|
||||
if (res != __GCONV_OK)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
@@ -221,7 +204,7 @@ main (int argc, char *argv[])
|
||||
const char *from_pretty =
|
||||
(from_code[0] ? from_code : nl_langinfo (CODESET));
|
||||
const char *to_pretty =
|
||||
(orig_to_code[0] ? orig_to_code : nl_langinfo (CODESET));
|
||||
(to_code[0] ? to_code : nl_langinfo (CODESET));
|
||||
|
||||
if (from_wrong)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/* Test iconv's TRANSLIT and IGNORE option handling
|
||||
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#include <iconv.h>
|
||||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <support/support.h>
|
||||
#include <support/check.h>
|
||||
|
||||
|
||||
/* Run one iconv test. Arguments:
|
||||
to: destination character set and options
|
||||
from: source character set
|
||||
input: input string to be converted
|
||||
exp_in: expected number of bytes consumed
|
||||
exp_ret: expected return value (error or number of irreversible conversions)
|
||||
exp_out: expected output string
|
||||
exp_err: expected value of `errno' after iconv returns. */
|
||||
static void
|
||||
test_iconv (const char *to, const char *from, char *input, size_t exp_in,
|
||||
size_t exp_ret, const char *exp_out, int exp_err)
|
||||
{
|
||||
iconv_t cd;
|
||||
char outbuf[500];
|
||||
size_t inlen, outlen;
|
||||
char *inptr, *outptr;
|
||||
size_t n;
|
||||
|
||||
cd = iconv_open (to, from);
|
||||
TEST_VERIFY (cd != (iconv_t) -1);
|
||||
|
||||
inlen = strlen (input);
|
||||
outlen = sizeof (outbuf);
|
||||
inptr = input;
|
||||
outptr = outbuf;
|
||||
|
||||
errno = 0;
|
||||
n = iconv (cd, &inptr, &inlen, &outptr, &outlen);
|
||||
|
||||
TEST_COMPARE (n, exp_ret);
|
||||
TEST_VERIFY (inptr == input + exp_in);
|
||||
TEST_COMPARE (errno, exp_err);
|
||||
TEST_COMPARE_BLOB (outbuf, outptr - outbuf, exp_out, strlen (exp_out));
|
||||
TEST_VERIFY (iconv_close (cd) == 0);
|
||||
}
|
||||
|
||||
|
||||
/* We test option parsing by converting UTF-8 inputs to ASCII under various
|
||||
option combinations. The UTF-8 inputs fall into three categories:
|
||||
- ASCII-only,
|
||||
- non-ASCII,
|
||||
- non-ASCII with invalid UTF-8 characters. */
|
||||
|
||||
/* 1. */
|
||||
char ascii[] = "Just some ASCII text";
|
||||
|
||||
/* 2. Valid UTF-8 input and some corresponding expected outputs with various
|
||||
options. The two non-ASCII characters below are accented alphabets:
|
||||
an `a' then an `o'. */
|
||||
char utf8[] = "UTF-8 text with \u00E1 couple \u00F3f non-ASCII characters";
|
||||
char u2a[] = "UTF-8 text with ";
|
||||
char u2a_translit[] = "UTF-8 text with a couple of non-ASCII characters";
|
||||
char u2a_ignore[] = "UTF-8 text with couple f non-ASCII characters";
|
||||
|
||||
/* 3. Invalid UTF-8 input and some corresponding expected outputs. \xff is
|
||||
invalid UTF-8. It's followed by some valid but non-ASCII UTF-8. */
|
||||
char iutf8[] = "Invalid UTF-8 \xff\u27E6text\u27E7";
|
||||
char iu2a[] = "Invalid UTF-8 ";
|
||||
char iu2a_ignore[] = "Invalid UTF-8 text";
|
||||
char iu2a_both[] = "Invalid UTF-8 [|text|]";
|
||||
|
||||
/* 4. Another invalid UTF-8 input and corresponding expected outputs. This time
|
||||
the valid non-ASCII UTF-8 characters appear before the invalid \xff. */
|
||||
char jutf8[] = "Invalid \u27E6UTF-8\u27E7 \xfftext";
|
||||
char ju2a[] = "Invalid ";
|
||||
char ju2a_translit[] = "Invalid [|UTF-8|] ";
|
||||
char ju2a_ignore[] = "Invalid UTF-8 text";
|
||||
char ju2a_both[] = "Invalid [|UTF-8|] text";
|
||||
|
||||
/* We also test option handling for character set names that have the form
|
||||
"A/B". In this test, we test conversions "ISO-10646/UTF-8", and either
|
||||
ISO-8859-1 or ASCII. */
|
||||
|
||||
/* 5. Accented 'A' and 'a' characters in ISO-8859-1 and UTF-8, and an
|
||||
equivalent ASCII transliteration. */
|
||||
char iso8859_1_a[] = {0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, /* Accented A's. */
|
||||
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, /* Accented a's. */
|
||||
0x00};
|
||||
char utf8_a[] = "\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5"
|
||||
"\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5";
|
||||
char ascii_a[] = "AAAAAAaaaaaa";
|
||||
|
||||
/* 6. An invalid ASCII string where [0] is invalid and [1] is '~'. */
|
||||
char iascii [] = {0x80, '~', '\0'};
|
||||
char empty[] = "";
|
||||
char ia2u_ignore[] = "~";
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
xsetlocale (LC_ALL, "en_US.UTF-8");
|
||||
|
||||
|
||||
/* 0. iconv_open should gracefully fail for invalid character sets. */
|
||||
|
||||
TEST_VERIFY (iconv_open ("INVALID", "UTF-8") == (iconv_t) -1);
|
||||
TEST_VERIFY (iconv_open ("UTF-8", "INVALID") == (iconv_t) -1);
|
||||
TEST_VERIFY (iconv_open ("INVALID", "INVALID") == (iconv_t) -1);
|
||||
|
||||
|
||||
/* 1. ASCII-only UTF-8 input should convert to ASCII with no changes: */
|
||||
|
||||
test_iconv ("ASCII", "UTF-8", ascii, strlen (ascii), 0, ascii, 0);
|
||||
test_iconv ("ASCII//", "UTF-8", ascii, strlen (ascii), 0, ascii, 0);
|
||||
test_iconv ("ASCII//TRANSLIT", "UTF-8", ascii, strlen (ascii), 0, ascii, 0);
|
||||
test_iconv ("ASCII//TRANSLIT//", "UTF-8", ascii, strlen (ascii), 0, ascii,
|
||||
0);
|
||||
test_iconv ("ASCII//IGNORE", "UTF-8", ascii, strlen (ascii), 0, ascii, 0);
|
||||
test_iconv ("ASCII//IGNORE//", "UTF-8", ascii, strlen (ascii), 0, ascii, 0);
|
||||
|
||||
|
||||
/* 2. Valid UTF-8 input with non-ASCII characters: */
|
||||
|
||||
/* EILSEQ when converted to ASCII. */
|
||||
test_iconv ("ASCII", "UTF-8", utf8, strlen (u2a), (size_t) -1, u2a, EILSEQ);
|
||||
|
||||
/* Converted without error with TRANSLIT enabled. */
|
||||
test_iconv ("ASCII//TRANSLIT", "UTF-8", utf8, strlen (utf8), 2, u2a_translit,
|
||||
0);
|
||||
|
||||
/* EILSEQ with IGNORE enabled. Non-ASCII chars dropped from output. */
|
||||
test_iconv ("ASCII//IGNORE", "UTF-8", utf8, strlen (utf8), (size_t) -1,
|
||||
u2a_ignore, EILSEQ);
|
||||
|
||||
/* With TRANSLIT and IGNORE enabled, transliterated without error. We test
|
||||
four combinations. */
|
||||
|
||||
test_iconv ("ASCII//TRANSLIT,IGNORE", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
test_iconv ("ASCII//TRANSLIT//IGNORE", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
/* Due to bug 19519, iconv was ignoring TRANSLIT for the following input. */
|
||||
test_iconv ("ASCII//IGNORE//TRANSLIT", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
|
||||
/* Misspellings of TRANSLIT and IGNORE are ignored, but conversion still
|
||||
works while respecting any other correctly spelled options. */
|
||||
|
||||
test_iconv ("ASCII//T", "UTF-8", utf8, strlen (u2a), (size_t) -1, u2a,
|
||||
EILSEQ);
|
||||
test_iconv ("ASCII//TRANSLITERATE", "UTF-8", utf8, strlen (u2a), (size_t) -1,
|
||||
u2a, EILSEQ);
|
||||
test_iconv ("ASCII//I", "UTF-8", utf8, strlen (u2a), (size_t) -1, u2a,
|
||||
EILSEQ);
|
||||
test_iconv ("ASCII//IGNORED", "UTF-8", utf8, strlen (u2a), (size_t) -1, u2a,
|
||||
EILSEQ);
|
||||
test_iconv ("ASCII//TRANSLITERATE//IGNORED", "UTF-8", utf8, strlen (u2a),
|
||||
(size_t) -1, u2a, EILSEQ);
|
||||
test_iconv ("ASCII//IGNORED,TRANSLITERATE", "UTF-8", utf8, strlen (u2a),
|
||||
(size_t) -1, u2a, EILSEQ);
|
||||
test_iconv ("ASCII//T//I", "UTF-8", utf8, strlen (u2a), (size_t) -1, u2a,
|
||||
EILSEQ);
|
||||
|
||||
test_iconv ("ASCII//TRANSLIT//I", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
/* Due to bug 19519, iconv was ignoring TRANSLIT for the following input. */
|
||||
test_iconv ("ASCII//I//TRANSLIT", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
test_iconv ("ASCII//IGNORED,TRANSLIT", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
test_iconv ("ASCII//TRANSLIT,IGNORED", "UTF-8", utf8, strlen (utf8), 2,
|
||||
u2a_translit, 0);
|
||||
|
||||
test_iconv ("ASCII//IGNORE,T", "UTF-8", utf8, strlen (utf8), (size_t) -1,
|
||||
u2a_ignore, EILSEQ);
|
||||
test_iconv ("ASCII//T,IGNORE", "UTF-8", utf8, strlen (utf8), (size_t) -1,
|
||||
u2a_ignore, EILSEQ);
|
||||
/* Due to bug 19519, iconv was ignoring IGNORE for the following input. */
|
||||
test_iconv ("ASCII//TRANSLITERATE//IGNORE", "UTF-8", utf8, strlen (utf8),
|
||||
(size_t) -1, u2a_ignore, EILSEQ);
|
||||
test_iconv ("ASCII//IGNORE//TRANSLITERATE", "UTF-8", utf8, strlen (utf8),
|
||||
(size_t) -1, u2a_ignore, EILSEQ);
|
||||
|
||||
|
||||
/* 3. Invalid UTF-8 followed by some valid non-ASCII UTF-8 characters: */
|
||||
|
||||
/* EILSEQ; output is truncated at the first invalid UTF-8 character. */
|
||||
test_iconv ("ASCII", "UTF-8", iutf8, strlen (iu2a), (size_t) -1, iu2a,
|
||||
EILSEQ);
|
||||
|
||||
/* With TRANSLIT enabled: EILSEQ; output still truncated at the first invalid
|
||||
UTF-8 character. */
|
||||
test_iconv ("ASCII//TRANSLIT", "UTF-8", iutf8, strlen (iu2a), (size_t) -1,
|
||||
iu2a, EILSEQ);
|
||||
|
||||
/* With IGNORE enabled: EILSEQ; output omits invalid UTF-8 characters and
|
||||
valid UTF-8 non-ASCII characters. */
|
||||
test_iconv ("ASCII//IGNORE", "UTF-8", iutf8, strlen (iutf8), (size_t) -1,
|
||||
iu2a_ignore, EILSEQ);
|
||||
|
||||
/* With TRANSLIT and IGNORE enabled, output omits only invalid UTF-8
|
||||
characters and transliterates valid non-ASCII UTF-8 characters. We test
|
||||
four combinations. */
|
||||
|
||||
test_iconv ("ASCII//TRANSLIT,IGNORE", "UTF-8", iutf8, strlen (iutf8), 2,
|
||||
iu2a_both, 0);
|
||||
/* Due to bug 19519, iconv was ignoring IGNORE for the following input. */
|
||||
test_iconv ("ASCII//TRANSLIT//IGNORE", "UTF-8", iutf8, strlen (iutf8), 2,
|
||||
iu2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT", "UTF-8", iutf8, strlen (iutf8), 2,
|
||||
iu2a_both, 0);
|
||||
/* Due to bug 19519, iconv was ignoring TRANSLIT for the following input. */
|
||||
test_iconv ("ASCII//IGNORE//TRANSLIT", "UTF-8", iutf8, strlen (iutf8), 2,
|
||||
iu2a_both, 0);
|
||||
|
||||
|
||||
/* 4. Invalid UTF-8 with valid non-ASCII UTF-8 chars appearing first: */
|
||||
|
||||
/* EILSEQ; output is truncated at the first non-ASCII character. */
|
||||
test_iconv ("ASCII", "UTF-8", jutf8, strlen (ju2a), (size_t) -1, ju2a,
|
||||
EILSEQ);
|
||||
|
||||
/* With TRANSLIT enabled: EILSEQ; output now truncated at the first invalid
|
||||
UTF-8 character. */
|
||||
test_iconv ("ASCII//TRANSLIT", "UTF-8", jutf8, strlen (jutf8) - 5,
|
||||
(size_t) -1, ju2a_translit, EILSEQ);
|
||||
test_iconv ("ASCII//translit", "UTF-8", jutf8, strlen (jutf8) - 5,
|
||||
(size_t) -1, ju2a_translit, EILSEQ);
|
||||
|
||||
/* With IGNORE enabled: EILSEQ; output omits invalid UTF-8 characters and
|
||||
valid UTF-8 non-ASCII characters. */
|
||||
test_iconv ("ASCII//IGNORE", "UTF-8", jutf8, strlen (jutf8), (size_t) -1,
|
||||
ju2a_ignore, EILSEQ);
|
||||
test_iconv ("ASCII//ignore", "UTF-8", jutf8, strlen (jutf8), (size_t) -1,
|
||||
ju2a_ignore, EILSEQ);
|
||||
|
||||
/* With TRANSLIT and IGNORE enabled, output omits only invalid UTF-8
|
||||
characters and transliterates valid non-ASCII UTF-8 characters. We test
|
||||
several combinations. */
|
||||
|
||||
test_iconv ("ASCII//TRANSLIT,IGNORE", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
/* Due to bug 19519, iconv was ignoring IGNORE for the following input. */
|
||||
test_iconv ("ASCII//TRANSLIT//IGNORE", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
/* Due to bug 19519, iconv was ignoring TRANSLIT for the following input. */
|
||||
test_iconv ("ASCII//IGNORE//TRANSLIT", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//translit,ignore", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
/* Trailing whitespace and separators should be ignored. */
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT ", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT/", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT//", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT,", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT,,", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
test_iconv ("ASCII//IGNORE,TRANSLIT /,", "UTF-8", jutf8, strlen (jutf8), 2,
|
||||
ju2a_both, 0);
|
||||
|
||||
/* TRANSLIT or IGNORE suffixes in fromcode should be ignored. */
|
||||
test_iconv ("ASCII", "UTF-8//TRANSLIT", jutf8, strlen (ju2a), (size_t) -1,
|
||||
ju2a, EILSEQ);
|
||||
test_iconv ("ASCII", "UTF-8//IGNORE", jutf8, strlen (ju2a), (size_t) -1,
|
||||
ju2a, EILSEQ);
|
||||
test_iconv ("ASCII", "UTF-8//TRANSLIT,IGNORE", jutf8, strlen (ju2a),
|
||||
(size_t) -1, ju2a, EILSEQ);
|
||||
|
||||
|
||||
/* 5. Charset names of the form "A/B/": */
|
||||
|
||||
/* ISO-8859-1 is converted to UTF-8 without needing transliteration. */
|
||||
test_iconv ("ISO-10646/UTF-8", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8/", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8/IGNORE", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8//IGNORE", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8/TRANSLIT", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8//TRANSLIT", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8//TRANSLIT/IGNORE", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8//TRANSLIT//IGNORE", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
test_iconv ("ISO-10646/UTF-8/TRANSLIT,IGNORE", "ISO-8859-1", iso8859_1_a,
|
||||
strlen (iso8859_1_a), 0, utf8_a, 0);
|
||||
|
||||
/* UTF-8 with accented A's is converted to ASCII with transliteration. */
|
||||
test_iconv ("ASCII", "ISO-10646/UTF-8", utf8_a,
|
||||
0, (size_t) -1, empty, EILSEQ);
|
||||
test_iconv ("ASCII//IGNORE", "ISO-10646/UTF-8", utf8_a,
|
||||
strlen (utf8_a), (size_t) -1, empty, EILSEQ);
|
||||
test_iconv ("ASCII//TRANSLIT", "ISO-10646/UTF-8", utf8_a,
|
||||
strlen (utf8_a), 12, ascii_a, 0);
|
||||
|
||||
/* Invalid ASCII is converted to UTF-8 only with IGNORE. */
|
||||
test_iconv ("ISO-10646/UTF-8", "ASCII", iascii, strlen (empty), (size_t) -1,
|
||||
empty, EILSEQ);
|
||||
test_iconv ("ISO-10646/UTF-8/TRANSLIT", "ASCII", iascii, strlen (empty),
|
||||
(size_t) -1, empty, EILSEQ);
|
||||
test_iconv ("ISO-10646/UTF-8/IGNORE", "ASCII", iascii, strlen (iascii),
|
||||
(size_t) -1, ia2u_ignore, EILSEQ);
|
||||
test_iconv ("ISO-10646/UTF-8/TRANSLIT,IGNORE", "ASCII", iascii,
|
||||
strlen (iascii), (size_t) -1, ia2u_ignore, EILSEQ);
|
||||
/* Due to bug 19519, iconv was ignoring IGNORE for the following three
|
||||
inputs: */
|
||||
test_iconv ("ISO-10646/UTF-8/TRANSLIT/IGNORE", "ASCII", iascii,
|
||||
strlen (iascii), (size_t) -1, ia2u_ignore, EILSEQ);
|
||||
test_iconv ("ISO-10646/UTF-8//TRANSLIT,IGNORE", "ASCII", iascii,
|
||||
strlen (iascii), (size_t) -1, ia2u_ignore, EILSEQ);
|
||||
test_iconv ("ISO-10646/UTF-8//TRANSLIT//IGNORE", "ASCII", iascii,
|
||||
strlen (iascii), (size_t) -1, ia2u_ignore, EILSEQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Test iconv behavior on UCS4 conversions with //IGNORE.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Derived from BZ #26923 */
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
iconv_t cd = iconv_open ("UTF-8//IGNORE", "ISO-10646/UCS4/");
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
/*
|
||||
* Convert sequence beginning with an irreversible character into buffer that
|
||||
* is too small.
|
||||
*/
|
||||
char input[12] = "\xe1\x80\xa1" "AAAAAAAAA";
|
||||
char *inptr = input;
|
||||
size_t insize = sizeof (input);
|
||||
char output[6];
|
||||
char *outptr = output;
|
||||
size_t outsize = sizeof (output);
|
||||
|
||||
TEST_VERIFY (iconv (cd, &inptr, &insize, &outptr, &outsize) == -1);
|
||||
TEST_VERIFY (errno == E2BIG);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) != -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,284 @@
|
||||
#!/bin/bash
|
||||
# Test for some known iconv(1) hangs from bug 19519, and miscellaneous
|
||||
# iconv(1) program error conditions.
|
||||
# Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
|
||||
codir=$1
|
||||
test_wrapper_env="$2"
|
||||
run_program_env="$3"
|
||||
|
||||
# We have to have some directories in the library path.
|
||||
LIBPATH=$codir:$codir/iconvdata
|
||||
|
||||
# How the start the iconv(1) program. $from is not defined/expanded yet.
|
||||
ICONV='
|
||||
$codir/elf/ld.so --library-path $LIBPATH --inhibit-rpath ${from}.so
|
||||
$codir/iconv/iconv_prog
|
||||
'
|
||||
ICONV="$test_wrapper_env $run_program_env $ICONV"
|
||||
|
||||
# List of known hangs;
|
||||
# Gathered by running an exhaustive 2 byte input search against glibc-2.28
|
||||
hangarray=(
|
||||
"\x00\x23;-c;ANSI_X3.110;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa1;-c;ARMSCII-8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa1;-c;ASMO_449;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;BIG5;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xff;-c;BIG5HKSCS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xff;-c;BRF;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xff;-c;BS_4730;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1250;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x98;-c;CP1251;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1252;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1253;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1254;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1255;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1257;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;CP1258;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;CP932;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;CSA_Z243.4-1985-1;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;CSA_Z243.4-1985-2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;DEC-MCS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;DIN_66003;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;DS_2089;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-AT-DE;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-AT-DE-A;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-CA-FR;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-DK-NO;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-DK-NO-A;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-ES;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-ES-A;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-ES-S;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-FI-SE;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-FI-SE-A;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-FR;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-IS-FRISS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-IT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-PT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-UK;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;EBCDIC-US;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ES;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ES2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-CN;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-JISX0213;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-JP;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-JP-MS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-KR;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;EUC-TW;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GB18030;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GB_1988-80;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GBK;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GOST_19768-74;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GREEK7;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GREEK7-OLD;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;GREEK-CCITT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;HP-GREEK8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;HP-ROMAN8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;HP-ROMAN9;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;HP-THAI8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;HP-TURKISH8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM038;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM1004;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xff;-c;IBM1008;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;IBM1046;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x51;-c;IBM1132;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa0;-c;IBM1133;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xce;-c;IBM1137;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM1161;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xdb;-c;IBM1162;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x70;-c;IBM12712;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x0f;-c;IBM1364;UTF-8"
|
||||
"\x0e\x0e;-c;IBM1364;UTF-8"
|
||||
"\x00\x0f;-c;IBM1371;UTF-8"
|
||||
"\x0e\x0e;-c;IBM1371;UTF-8"
|
||||
"\x00\x0f;-c;IBM1388;UTF-8"
|
||||
"\x0e\x0e;-c;IBM1388;UTF-8"
|
||||
"\x00\x0f;-c;IBM1390;UTF-8"
|
||||
"\x0e\x0e;-c;IBM1390;UTF-8"
|
||||
"\x00\x0f;-c;IBM1399;UTF-8"
|
||||
"\x0e\x0e;-c;IBM1399;UTF-8"
|
||||
"\x00\x53;-c;IBM16804;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM274;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM275;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM281;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x57;-c;IBM290;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x45;-c;IBM420;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x68;-c;IBM423;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x70;-c;IBM424;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x53;-c;IBM4517;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x53;-c;IBM4899;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa5;-c;IBM4909;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xdc;-c;IBM4971;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM803;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x91;-c;IBM851;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x9b;-c;IBM856;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xd5;-c;IBM857;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;IBM864;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x94;-c;IBM868;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x94;-c;IBM869;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;IBM874;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x6a;-c;IBM875;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM880;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM891;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;IBM903;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;IBM904;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM905;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM9066;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x48;-c;IBM918;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x57;-c;IBM930;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM932;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM933;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM935;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM937;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x41;-c;IBM939;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IBM943;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;INIS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;INIS-8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;INIS-CYRILLIC;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xec;-c;ISIRI-3342;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xec;-c;ISO_10367-BOX;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-CN;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-CN-EXT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-JP;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-JP-2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-JP-3;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-2022-KR;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO_2033;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO_5427;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO_5427-EXT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO_5428;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa4;-c;ISO_6937;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa0;-c;ISO_6937-2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-8859-11;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xa5;-c;ISO-8859-3;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-8859-6;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-8859-7;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;ISO-8859-8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;ISO-IR-197;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;ISO-IR-209;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;IT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;JIS_C6220-1969-RO;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;JIS_C6229-1984-B;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;JOHAB;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;JUS_I.B1.002;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;KOI-8;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x88;-c;KOI8-T;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;KSC5636;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;LATIN-GREEK;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;LATIN-GREEK-1;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xf6;-c;MAC-IS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;MSZ_7795.3;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NATS-DANO;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NATS-SEFI;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NC_NC00-10;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NF_Z_62-010;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NF_Z_62-010_1973;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NS_4551-1;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;NS_4551-2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;PT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;PT2;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x98;-c;RK1048;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x98;-c;SEN_850200_B;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x98;-c;SEN_850200_C;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;Shift_JISX0213;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x80;-c;SJIS;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x23;-c;T.61-8BIT;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;TIS-620;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;TSCII;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;UHC;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xd8;-c;UNICODE;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xdc;-c;UTF-16;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xdc\x00;-c;UTF-16BE;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\xdc;-c;UTF-16LE;UTF-8//TRANSLIT//IGNORE"
|
||||
"\xff\xff;-c;UTF-7;UTF-8//TRANSLIT//IGNORE"
|
||||
"\x00\x81;-c;WIN-SAMI-2;UTF-8//TRANSLIT//IGNORE"
|
||||
)
|
||||
|
||||
# List of option combinations that *should* lead to an error
|
||||
errorarray=(
|
||||
# Converting from/to invalid character sets should cause error
|
||||
"\x00\x00;;INVALID;INVALID"
|
||||
"\x00\x00;;INVALID;UTF-8"
|
||||
"\x00\x00;;UTF-8;INVALID"
|
||||
)
|
||||
|
||||
# Requires $twobyte input, $c flag, $from, and $to to be set; sets $ret
|
||||
execute_test ()
|
||||
{
|
||||
eval PROG=\"$ICONV\"
|
||||
echo -en "$twobyte" \
|
||||
| timeout -k 4 3 $PROG $c -f $from -t "$to" &>/dev/null
|
||||
ret=$?
|
||||
}
|
||||
|
||||
check_hangtest_result ()
|
||||
{
|
||||
if [ "$ret" -eq "124" ] || [ "$ret" -eq "137" ]; then # timeout/hang
|
||||
result="HANG"
|
||||
else
|
||||
if [ "$ret" -eq "139" ]; then # segfault
|
||||
result="SEGFAULT"
|
||||
else
|
||||
if [ "$ret" -gt "127" ]; then # unexpected error
|
||||
result="UNEXPECTED"
|
||||
else
|
||||
result="OK"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -n "$result: from: \"$from\", to: \"$to\","
|
||||
echo " input \"$twobyte\", flags \"$c\""
|
||||
|
||||
if [ "$result" != "OK" ]; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for hangcommand in "${hangarray[@]}"; do
|
||||
twobyte="$(echo "$hangcommand" | cut -d";" -f 1)"
|
||||
c="$(echo "$hangcommand" | cut -d";" -f 2)"
|
||||
from="$(echo "$hangcommand" | cut -d";" -f 3)"
|
||||
to="$(echo "$hangcommand" | cut -d";" -f 4)"
|
||||
execute_test
|
||||
check_hangtest_result
|
||||
done
|
||||
|
||||
check_errtest_result ()
|
||||
{
|
||||
if [ "$ret" -eq "1" ]; then # we errored out as expected
|
||||
result="PASS"
|
||||
else
|
||||
result="FAIL"
|
||||
fi
|
||||
echo -n "$result: from: \"$from\", to: \"$to\","
|
||||
echo " input \"$twobyte\", flags \"$c\", return code $ret"
|
||||
|
||||
if [ "$result" != "PASS" ]; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for errorcommand in "${errorarray[@]}"; do
|
||||
twobyte="$(echo "$errorcommand" | cut -d";" -f 1)"
|
||||
c="$(echo "$errorcommand" | cut -d";" -f 2)"
|
||||
from="$(echo "$errorcommand" | cut -d";" -f 3)"
|
||||
to="$(echo "$errorcommand" | cut -d";" -f 4)"
|
||||
execute_test
|
||||
check_errtest_result
|
||||
done
|
||||
+10
-1
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 1997-2020 Free Software Foundation, Inc.
|
||||
# Copyright (C) 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
|
||||
@@ -73,7 +74,9 @@ modules.so := $(addsuffix .so, $(modules))
|
||||
ifeq (yes,$(build-shared))
|
||||
tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
|
||||
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
|
||||
bug-iconv10 bug-iconv11 bug-iconv12
|
||||
bug-iconv10 bug-iconv11 bug-iconv12 bug-iconv13 bug-iconv14 \
|
||||
bug-iconv15 \
|
||||
tst-iconv-iso-2022-cn-ext
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -316,6 +319,12 @@ $(objpfx)bug-iconv10.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv12.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv14.out: $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)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)iconv-test.out: run-iconv-test.sh $(objpfx)gconv-modules \
|
||||
$(addprefix $(objpfx),$(modules.so)) \
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* bug 24973: Test EUC-KR module
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
iconv_t cd = iconv_open ("UTF-8//IGNORE", "EUC-KR");
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
/* 0xfe (->0x7e : row 94) and 0xc9 (->0x49 : row 41) are user-defined
|
||||
areas, which are not allowed and should be skipped over due to
|
||||
//IGNORE. The trailing 0xfe also is an incomplete sequence, which
|
||||
should be checked first. */
|
||||
char input[4] = { '\xc9', '\xa1', '\0', '\xfe' };
|
||||
char *inptr = input;
|
||||
size_t insize = sizeof (input);
|
||||
char output[4];
|
||||
char *outptr = output;
|
||||
size_t outsize = sizeof (output);
|
||||
|
||||
/* This used to crash due to buffer overrun. */
|
||||
TEST_VERIFY (iconv (cd, &inptr, &insize, &outptr, &outsize) == (size_t) -1);
|
||||
TEST_VERIFY (errno == EINVAL);
|
||||
/* The conversion should produce one character, the converted null
|
||||
character. */
|
||||
TEST_VERIFY (sizeof (output) - outsize == 1);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) != -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Assertion in ISO-2022-JP-3 due to two-character sequence (bug 27256).
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <iconv.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* Use an escape sequence to return to the initial state. */
|
||||
static void
|
||||
with_escape_sequence (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D\e(B";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 3);
|
||||
TEST_COMPARE (inbuf - in, strlen (in) - 3);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Return to the initial shift state, producing the pending
|
||||
character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
/* Use an explicit flush to return to the initial state. */
|
||||
static void
|
||||
with_flush (void)
|
||||
{
|
||||
iconv_t c = iconv_open ("UTF-8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (c != (iconv_t) -1);
|
||||
|
||||
char in[] = "\e$(O+D";
|
||||
char *inbuf = in;
|
||||
size_t inleft = strlen (in);
|
||||
char out[3]; /* Space for one output character. */
|
||||
char *outbuf;
|
||||
size_t outleft;
|
||||
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, &inbuf, &inleft, &outbuf, &outleft), (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
TEST_COMPARE (inleft, 0);
|
||||
TEST_COMPARE (inbuf - in, strlen (in));
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xc3);
|
||||
TEST_COMPARE (out[1] & 0xff, 0xa6);
|
||||
|
||||
/* Flush the pending character. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out) - 2);
|
||||
TEST_COMPARE (outbuf - out, 2);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
/* Nothing should be flushed the second time. */
|
||||
outbuf = out;
|
||||
outleft = sizeof (out);
|
||||
TEST_COMPARE (iconv (c, NULL, 0, &outbuf, &outleft), 0);
|
||||
TEST_COMPARE (outleft, sizeof (out));
|
||||
TEST_COMPARE (outbuf - out, 0);
|
||||
TEST_COMPARE (out[0] & 0xff, 0xcc);
|
||||
TEST_COMPARE (out[1] & 0xff, 0x80);
|
||||
|
||||
TEST_COMPARE (iconv_close (c), 0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
with_escape_sequence ();
|
||||
with_flush ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Bug 28524: Conversion from ISO-2022-JP-3 with iconv
|
||||
may emit spurious NUL character on state reset.
|
||||
Copyright (C) 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 <stddef.h>
|
||||
#include <iconv.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char in[] = "\x1b(I";
|
||||
char *inbuf = in;
|
||||
size_t inleft = sizeof (in) - 1;
|
||||
char out[1];
|
||||
char *outbuf = out;
|
||||
size_t outleft = sizeof (out);
|
||||
iconv_t cd;
|
||||
|
||||
cd = iconv_open ("UTF8", "ISO-2022-JP-3");
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
/* First call to iconv should alter internal state.
|
||||
Now, JISX0201_Kana_set is selected and
|
||||
state value != ASCII_set. */
|
||||
TEST_VERIFY (iconv (cd, &inbuf, &inleft, &outbuf, &outleft) != (size_t) -1);
|
||||
|
||||
/* No bytes should have been added to
|
||||
the output buffer at this point. */
|
||||
TEST_VERIFY (outbuf == out);
|
||||
TEST_VERIFY (outleft == sizeof (out));
|
||||
|
||||
/* Second call shall emit spurious NUL character in unpatched glibc. */
|
||||
TEST_VERIFY (iconv (cd, NULL, NULL, &outbuf, &outleft) != (size_t) -1);
|
||||
|
||||
/* No characters are expected to be produced. */
|
||||
TEST_VERIFY (outbuf == out);
|
||||
TEST_VERIFY (outleft == sizeof (out));
|
||||
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) != -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+1
-5
@@ -80,11 +80,7 @@ euckr_from_ucs4 (uint32_t ch, unsigned char *cp)
|
||||
\
|
||||
if (ch <= 0x9f) \
|
||||
++inptr; \
|
||||
/* 0xfe(->0x7e : row 94) and 0xc9(->0x59 : row 41) are \
|
||||
user-defined areas. */ \
|
||||
else if (__builtin_expect (ch == 0xa0, 0) \
|
||||
|| __builtin_expect (ch > 0xfe, 0) \
|
||||
|| __builtin_expect (ch == 0xc9, 0)) \
|
||||
else if (__glibc_unlikely (ch == 0xa0)) \
|
||||
{ \
|
||||
/* This is illegal. */ \
|
||||
STANDARD_FROM_LOOP_ERR_HANDLER (1); \
|
||||
|
||||
+2
-12
@@ -158,24 +158,14 @@ enum
|
||||
\
|
||||
if (__builtin_expect (ch, 0) == SO) \
|
||||
{ \
|
||||
/* Shift OUT, change to DBCS converter. */ \
|
||||
if (curcs == db) \
|
||||
{ \
|
||||
result = __GCONV_ILLEGAL_INPUT; \
|
||||
break; \
|
||||
} \
|
||||
/* Shift OUT, change to DBCS converter (redundant escape okay). */ \
|
||||
curcs = db; \
|
||||
++inptr; \
|
||||
continue; \
|
||||
} \
|
||||
if (__builtin_expect (ch, 0) == SI) \
|
||||
{ \
|
||||
/* Shift IN, change to SBCS converter. */ \
|
||||
if (curcs == sb) \
|
||||
{ \
|
||||
result = __GCONV_ILLEGAL_INPUT; \
|
||||
break; \
|
||||
} \
|
||||
/* Shift IN, change to SBCS converter (redundant escape okay). */ \
|
||||
curcs = sb; \
|
||||
++inptr; \
|
||||
continue; \
|
||||
|
||||
@@ -575,6 +575,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
|
||||
{ \
|
||||
const char *escseq; \
|
||||
\
|
||||
if (outptr + 4 > outend) \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
assert (used == CNS11643_2_set); /* XXX */ \
|
||||
escseq = "*H"; \
|
||||
*outptr++ = ESC; \
|
||||
@@ -588,6 +594,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
|
||||
{ \
|
||||
const char *escseq; \
|
||||
\
|
||||
if (outptr + 4 > outend) \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
assert ((used >> 5) >= 3 && (used >> 5) <= 7); \
|
||||
escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; \
|
||||
*outptr++ = ESC; \
|
||||
|
||||
+59
-18
@@ -1,5 +1,6 @@
|
||||
/* Conversion module for ISO-2022-JP-3.
|
||||
Copyright (C) 1998-2020 Free Software Foundation, Inc.
|
||||
Copyright (C) The GNU Toolchain Authors.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998,
|
||||
and Bruno Haible <bruno@clisp.org>, 2002.
|
||||
@@ -67,10 +68,15 @@ enum
|
||||
CURRENT_SEL_MASK = 7 << 3
|
||||
};
|
||||
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the state
|
||||
also contains the last two bytes to be output, shifted by 6 bits, and a
|
||||
one-bit indicator whether they must be preceded by the shift sequence,
|
||||
in bit 22. */
|
||||
/* During UCS-4 to ISO-2022-JP-3 conversion, the COUNT element of the
|
||||
state also contains the last two bytes to be output, shifted by 6
|
||||
bits, and a one-bit indicator whether they must be preceded by the
|
||||
shift sequence, in bit 22. During ISO-2022-JP-3 to UCS-4
|
||||
conversion, COUNT may also contain a non-zero pending wide
|
||||
character, shifted by six bits. This happens for certain inputs in
|
||||
JISX0213_1_2004_set and JISX0213_2_set if the second wide character
|
||||
in a combining sequence cannot be written because the buffer is
|
||||
full. */
|
||||
|
||||
/* Since this is a stateful encoding we have to provide code which resets
|
||||
the output state to the initial state. This has to be done during the
|
||||
@@ -80,10 +86,27 @@ enum
|
||||
{ \
|
||||
if (FROM_DIRECTION) \
|
||||
{ \
|
||||
/* It's easy, we don't have to emit anything, we just reset the \
|
||||
state for the input. */ \
|
||||
data->__statep->__count &= 7; \
|
||||
data->__statep->__count |= ASCII_set; \
|
||||
uint32_t ch = data->__statep->__count >> 6; \
|
||||
\
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
if (__glibc_likely (outbuf + 4 <= outend)) \
|
||||
{ \
|
||||
/* Write out the last character. */ \
|
||||
put32u (outbuf, ch); \
|
||||
outbuf += 4; \
|
||||
data->__statep->__count &= 7; \
|
||||
data->__statep->__count |= ASCII_set; \
|
||||
} \
|
||||
else \
|
||||
/* We don't have enough room in the output buffer. */ \
|
||||
status = __GCONV_FULL_OUTPUT; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
data->__statep->__count &= 7; \
|
||||
data->__statep->__count |= ASCII_set; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -151,7 +174,21 @@ enum
|
||||
#define LOOPFCT FROM_LOOP
|
||||
#define BODY \
|
||||
{ \
|
||||
uint32_t ch = *inptr; \
|
||||
uint32_t ch; \
|
||||
\
|
||||
/* Output any pending character. */ \
|
||||
ch = set >> 6; \
|
||||
if (__glibc_unlikely (ch != 0)) \
|
||||
{ \
|
||||
put32 (outptr, ch); \
|
||||
outptr += 4; \
|
||||
/* Remove the pending character, but preserve state bits. */ \
|
||||
set &= (1 << 6) - 1; \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise read the next input byte. */ \
|
||||
ch = *inptr; \
|
||||
\
|
||||
/* Recognize escape sequences. */ \
|
||||
if (__glibc_unlikely (ch == ESC)) \
|
||||
@@ -297,21 +334,25 @@ enum
|
||||
uint32_t u1 = __jisx0213_to_ucs_combining[ch - 1][0]; \
|
||||
uint32_t u2 = __jisx0213_to_ucs_combining[ch - 1][1]; \
|
||||
\
|
||||
inptr += 2; \
|
||||
\
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
\
|
||||
/* See whether we have room for two characters. */ \
|
||||
if (outptr + 8 <= outend) \
|
||||
if (outptr + 4 <= outend) \
|
||||
{ \
|
||||
inptr += 2; \
|
||||
put32 (outptr, u1); \
|
||||
outptr += 4; \
|
||||
put32 (outptr, u2); \
|
||||
outptr += 4; \
|
||||
continue; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise store only the first character now, and \
|
||||
put the second one into the queue. */ \
|
||||
set |= u2 << 6; \
|
||||
/* Tell the caller why we terminate the loop. */ \
|
||||
result = __GCONV_FULL_OUTPUT; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
inptr += 2; \
|
||||
|
||||
+3
-3
@@ -50,15 +50,15 @@ ksc5601_to_ucs4 (const unsigned char **s, size_t avail, unsigned char offset)
|
||||
unsigned char ch2;
|
||||
int idx;
|
||||
|
||||
if (avail < 2)
|
||||
return 0;
|
||||
|
||||
/* row 94(0x7e) and row 41(0x49) are user-defined area in KS C 5601 */
|
||||
|
||||
if (ch < offset || (ch - offset) <= 0x20 || (ch - offset) >= 0x7e
|
||||
|| (ch - offset) == 0x49)
|
||||
return __UNKNOWN_10646_CHAR;
|
||||
|
||||
if (avail < 2)
|
||||
return 0;
|
||||
|
||||
ch2 = (*s)[1];
|
||||
if (ch2 < offset || (ch2 - offset) <= 0x20 || (ch2 - offset) >= 0x7f)
|
||||
return __UNKNOWN_10646_CHAR;
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* Verify ISO-2022-CN-EXT does not write out of the bounds.
|
||||
Copyright (C) 2024 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 <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <support/xunistd.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
/* The test sets up a two memory page buffer with the second page marked
|
||||
PROT_NONE to trigger a fault if the conversion writes beyond the exact
|
||||
expected amount. Then we carry out various conversions and precisely
|
||||
place the start of the output buffer in order to trigger a SIGSEGV if the
|
||||
process writes anywhere between 1 and page sized bytes more (only one
|
||||
PROT_NONE page is setup as a canary) than expected. These tests exercise
|
||||
all three of the cases in ISO-2022-CN-EXT where the converter must switch
|
||||
character sets and may run out of buffer space while doing the
|
||||
operation. */
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
iconv_t cd = iconv_open ("ISO-2022-CN-EXT", "UTF-8");
|
||||
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
|
||||
char *ntf;
|
||||
size_t ntfsize;
|
||||
char *outbufbase;
|
||||
{
|
||||
int pgz = getpagesize ();
|
||||
TEST_VERIFY_EXIT (pgz > 0);
|
||||
ntfsize = 2 * pgz;
|
||||
|
||||
ntf = xmmap (NULL, ntfsize, PROT_READ | PROT_WRITE, MAP_PRIVATE
|
||||
| MAP_ANONYMOUS, -1);
|
||||
xmprotect (ntf + pgz, pgz, PROT_NONE);
|
||||
|
||||
outbufbase = ntf + pgz;
|
||||
}
|
||||
|
||||
/* Check if SOdesignation escape sequence does not trigger an OOB write. */
|
||||
{
|
||||
char inbuf[] = "\xe4\xba\xa4\xe6\x8d\xa2";
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
char *inp = inbuf;
|
||||
size_t inleft = sizeof (inbuf) - 1;
|
||||
|
||||
char *outp = outbufbase - i;
|
||||
size_t outleft = i;
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
== (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as before for SS2designation. */
|
||||
{
|
||||
char inbuf[] = "㴽 \xe3\xb4\xbd";
|
||||
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
char *inp = inbuf;
|
||||
size_t inleft = sizeof (inbuf) - 1;
|
||||
|
||||
char *outp = outbufbase - i;
|
||||
size_t outleft = i;
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
== (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as before for SS3designation. */
|
||||
{
|
||||
char inbuf[] = "劄 \xe5\x8a\x84";
|
||||
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
char *inp = inbuf;
|
||||
size_t inleft = sizeof (inbuf) - 1;
|
||||
|
||||
char *outp = outbufbase - i;
|
||||
size_t outleft = i;
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
== (size_t) -1);
|
||||
TEST_COMPARE (errno, E2BIG);
|
||||
|
||||
TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_VERIFY_EXIT (iconv_close (cd) != -1);
|
||||
|
||||
xmunmap (ntf, ntfsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+13
-13
@@ -59,6 +59,19 @@
|
||||
# define IN_MODULE (-1)
|
||||
#endif
|
||||
|
||||
/* Use symbol_version_reference to specify the version a symbol
|
||||
reference should link to. Use symbol_version or
|
||||
default_symbol_version for the definition of a versioned symbol.
|
||||
The difference is that the latter is a no-op in non-shared
|
||||
builds. */
|
||||
#ifdef __ASSEMBLER__
|
||||
# define symbol_version_reference(real, name, version) \
|
||||
.symver real, name##@##version
|
||||
#else /* !__ASSEMBLER__ */
|
||||
# define symbol_version_reference(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@" #version)
|
||||
#endif
|
||||
|
||||
#ifndef _ISOMAC
|
||||
|
||||
/* This is defined for the compilation of all C library code. features.h
|
||||
@@ -396,19 +409,6 @@ for linking")
|
||||
past the last element in SET. */
|
||||
#define symbol_set_end_p(set, ptr) ((ptr) >= (void *const *) &__stop_##set)
|
||||
|
||||
/* Use symbol_version_reference to specify the version a symbol
|
||||
reference should link to. Use symbol_version or
|
||||
default_symbol_version for the definition of a versioned symbol.
|
||||
The difference is that the latter is a no-op in non-shared
|
||||
builds. */
|
||||
#ifdef __ASSEMBLER__
|
||||
# define symbol_version_reference(real, name, version) \
|
||||
.symver real, name##@##version
|
||||
#else /* !__ASSEMBLER__ */
|
||||
# define symbol_version_reference(real, name, version) \
|
||||
__asm__ (".symver " #real "," #name "@" #version)
|
||||
#endif
|
||||
|
||||
#ifdef SHARED
|
||||
# define symbol_version(real, name, version) \
|
||||
symbol_version_reference(real, name, version)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# ifndef _ISOMAC
|
||||
|
||||
extern int __prctl (int __option, ...);
|
||||
libc_hidden_proto (__prctl)
|
||||
|
||||
# endif /* !_ISOMAC */
|
||||
#endif
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
#include <socket/sys/un.h>
|
||||
|
||||
#ifndef _ISOMAC
|
||||
|
||||
/* Set ADDR->sun_family to AF_UNIX and ADDR->sun_path to PATHNAME.
|
||||
Return 0 on success or -1 on failure (due to overlong PATHNAME).
|
||||
The caller should always use sizeof (struct sockaddr_un) as the
|
||||
socket address length, disregaring the length of PATHNAME.
|
||||
Only concrete (non-abstract) pathnames are supported. */
|
||||
int __sockaddr_un_set (struct sockaddr_un *addr, const char *pathname)
|
||||
attribute_hidden;
|
||||
|
||||
#endif /* _ISOMAC */
|
||||
|
||||
+12
-4
@@ -1119,11 +1119,19 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
|
||||
outcharset = encoding;
|
||||
|
||||
# ifdef _LIBC
|
||||
|
||||
struct gconv_spec conv_spec;
|
||||
|
||||
__gconv_create_spec (&conv_spec, charset, outcharset);
|
||||
|
||||
/* We always want to use transliteration. */
|
||||
outcharset = norm_add_slashes (outcharset, "TRANSLIT");
|
||||
charset = norm_add_slashes (charset, "");
|
||||
int r = __gconv_open (outcharset, charset, &convd->conv,
|
||||
GCONV_AVOID_NOCONV);
|
||||
conv_spec.translit = true;
|
||||
|
||||
int r = __gconv_open (&conv_spec, &convd->conv,
|
||||
GCONV_AVOID_NOCONV);
|
||||
|
||||
__gconv_destroy_spec (&conv_spec);
|
||||
|
||||
if (__builtin_expect (r != __GCONV_OK, 0))
|
||||
{
|
||||
/* If the output encoding is the same there is
|
||||
|
||||
+14
-20
@@ -22,13 +22,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char *s;
|
||||
int result = 0;
|
||||
|
||||
unsetenv ("LANGUAGE");
|
||||
unsetenv ("OUTPUT_CHARSET");
|
||||
setlocale (LC_ALL, "de_DE.ISO-8859-1");
|
||||
@@ -36,25 +34,21 @@ do_test (void)
|
||||
bindtextdomain ("codeset", OBJPFX "domaindir");
|
||||
|
||||
/* Here we expect output in ISO-8859-1. */
|
||||
s = gettext ("cheese");
|
||||
if (strcmp (s, "K\344se"))
|
||||
{
|
||||
printf ("call 1 returned: %s\n", s);
|
||||
result = 1;
|
||||
}
|
||||
|
||||
bind_textdomain_codeset ("codeset", "UTF-8");
|
||||
TEST_COMPARE_STRING (gettext ("cheese"), "K\344se");
|
||||
|
||||
/* Here we expect output in UTF-8. */
|
||||
s = gettext ("cheese");
|
||||
if (strcmp (s, "K\303\244se"))
|
||||
{
|
||||
printf ("call 2 returned: %s\n", s);
|
||||
result = 1;
|
||||
}
|
||||
bind_textdomain_codeset ("codeset", "UTF-8");
|
||||
TEST_COMPARE_STRING (gettext ("cheese"), "K\303\244se");
|
||||
|
||||
return result;
|
||||
/* `a with umlaut' is transliterated to `ae'. */
|
||||
bind_textdomain_codeset ("codeset", "ASCII//TRANSLIT");
|
||||
TEST_COMPARE_STRING (gettext ("cheese"), "Kaese");
|
||||
|
||||
/* Transliteration also works by default even if not set. */
|
||||
bind_textdomain_codeset ("codeset", "ASCII");
|
||||
TEST_COMPARE_STRING (gettext ("cheese"), "Kaese");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
||||
#include <support/test-driver.c>
|
||||
|
||||
@@ -92,7 +92,7 @@ day "dimenge";/
|
||||
"diluns";/
|
||||
"dimars";/
|
||||
"dim<U00E8>cres";/
|
||||
"dij<U00F3>us";/
|
||||
"dij<U00F2>us";/
|
||||
"divendres";/
|
||||
"dissabte"
|
||||
abmon "gen.";/
|
||||
@@ -110,7 +110,7 @@ abmon "gen.";/
|
||||
alt_mon "geni<U00E8>r";/
|
||||
"febri<U00E8>r";/
|
||||
"mar<U00E7>";/
|
||||
"abrial";/
|
||||
"abril";/
|
||||
"mai";/
|
||||
"junh";/
|
||||
"julhet";/
|
||||
@@ -122,7 +122,7 @@ alt_mon "geni<U00E8>r";/
|
||||
mon "de geni<U00E8>r";/
|
||||
"de febri<U00E8>r";/
|
||||
"de mar<U00E7>";/
|
||||
"d<U2019>abrial";/
|
||||
"d<U2019>abril";/
|
||||
"de mai";/
|
||||
"de junh";/
|
||||
"de julhet";/
|
||||
|
||||
@@ -61,6 +61,16 @@ endif
|
||||
tests += $(tests-static)
|
||||
test-srcs = tst-mtrace
|
||||
|
||||
# These tests either are run with MALLOC_CHECK_=3 by default or do not work
|
||||
# with MALLOC_CHECK_=3 because they expect a specific failure.
|
||||
tests-exclude-mcheck = tst-mcheck tst-malloc-usable \
|
||||
tst-interpose-nothread tst-interpose-static-nothread \
|
||||
tst-interpose-static-thread tst-malloc-too-large \
|
||||
tst-mxfast tst-safe-linking
|
||||
|
||||
# Run all tests with MALLOC_CHECK_=3
|
||||
tests-mcheck = $(filter-out $(tests-exclude-mcheck),$(tests))
|
||||
|
||||
routines = malloc morecore mcheck mtrace obstack reallocarray \
|
||||
scratch_buffer_grow scratch_buffer_grow_preserve \
|
||||
scratch_buffer_set_array_size \
|
||||
@@ -99,6 +109,11 @@ $(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-stats-cancellation: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-backtrace-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-thread-exit-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-thread-fail-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-fork-deadlock-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-stats-cancellation-mcheck: $(shared-thread-library)
|
||||
|
||||
# Export the __malloc_initialize_hook variable to libc.so.
|
||||
LDFLAGS-tst-mallocstate = -rdynamic
|
||||
@@ -238,6 +253,8 @@ $(tests:%=$(objpfx)%.o): CPPFLAGS += -DTEST_NO_MALLOPT
|
||||
$(objpfx)tst-interpose-nothread: $(objpfx)tst-interpose-aux-nothread.o
|
||||
$(objpfx)tst-interpose-thread: \
|
||||
$(objpfx)tst-interpose-aux-thread.o $(shared-thread-library)
|
||||
$(objpfx)tst-interpose-thread-mcheck: \
|
||||
$(objpfx)tst-interpose-aux-thread.o $(shared-thread-library)
|
||||
$(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o
|
||||
$(objpfx)tst-interpose-static-thread: \
|
||||
$(objpfx)tst-interpose-aux-thread.o $(static-thread-library)
|
||||
@@ -255,3 +272,6 @@ $(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
|
||||
$(objpfx)tst-malloc-tcache-leak: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc_info: $(shared-thread-library)
|
||||
$(objpfx)tst-mallocfork2: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-tcache-leak-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc_info-mcheck: $(shared-thread-library)
|
||||
$(objpfx)tst-mallocfork2-mcheck: $(shared-thread-library)
|
||||
|
||||
+5
-3
@@ -4668,7 +4668,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
|
||||
|
||||
|
||||
|
||||
if (!checked_request2size (bytes, &nb))
|
||||
if (!checked_request2size (bytes, &nb) || alignment > PTRDIFF_MAX)
|
||||
{
|
||||
__set_errno (ENOMEM);
|
||||
return NULL;
|
||||
@@ -4679,8 +4679,10 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
|
||||
request, and then possibly free the leading and trailing space.
|
||||
*/
|
||||
|
||||
/* 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 == 0)
|
||||
|
||||
@@ -127,7 +127,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
|
||||
@@ -146,8 +145,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)
|
||||
@@ -240,11 +239,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.
|
||||
|
||||
|
||||
+28
-11
@@ -62,6 +62,9 @@ static volatile sig_atomic_t sigusr1_received;
|
||||
progress. Checked by liveness_signal_handler. */
|
||||
static volatile sig_atomic_t progress_indicator = 1;
|
||||
|
||||
/* Set to 1 if an error occurs in the signal handler. */
|
||||
static volatile sig_atomic_t error_indicator = 0;
|
||||
|
||||
static void
|
||||
sigusr1_handler (int signo)
|
||||
{
|
||||
@@ -72,7 +75,8 @@ sigusr1_handler (int signo)
|
||||
if (pid == -1)
|
||||
{
|
||||
write_message ("error: fork\n");
|
||||
abort ();
|
||||
error_indicator = 1;
|
||||
return;
|
||||
}
|
||||
if (pid == 0)
|
||||
_exit (0);
|
||||
@@ -81,12 +85,14 @@ sigusr1_handler (int signo)
|
||||
if (ret < 0)
|
||||
{
|
||||
write_message ("error: waitpid\n");
|
||||
abort ();
|
||||
error_indicator = 1;
|
||||
return;
|
||||
}
|
||||
if (status != 0)
|
||||
{
|
||||
write_message ("error: unexpected exit status from subprocess\n");
|
||||
abort ();
|
||||
error_indicator = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +128,25 @@ signal_sender (int signo, bool sleep)
|
||||
}
|
||||
}
|
||||
|
||||
/* Children processes. */
|
||||
static pid_t sigusr1_sender_pids[5] = { 0 };
|
||||
static pid_t sigusr2_sender_pid = 0;
|
||||
|
||||
static void
|
||||
kill_children (void)
|
||||
{
|
||||
for (size_t i = 0; i < array_length (sigusr1_sender_pids); ++i)
|
||||
if (sigusr1_sender_pids[i] > 0)
|
||||
kill (sigusr1_sender_pids[i], SIGKILL);
|
||||
if (sigusr2_sender_pid > 0)
|
||||
kill (sigusr2_sender_pid, SIGKILL);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
atexit (kill_children);
|
||||
|
||||
/* shared->barrier is intialized along with sigusr1_sender_pids
|
||||
below. */
|
||||
shared = support_shared_allocate (sizeof (*shared));
|
||||
@@ -148,14 +170,13 @@ do_test (void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t sigusr2_sender_pid = xfork ();
|
||||
sigusr2_sender_pid = xfork ();
|
||||
if (sigusr2_sender_pid == 0)
|
||||
signal_sender (SIGUSR2, true);
|
||||
|
||||
/* Send SIGUSR1 signals from several processes. Hopefully, one
|
||||
signal will hit one of the ciritical functions. Use a barrier to
|
||||
avoid sending signals while not running fork/free/malloc. */
|
||||
pid_t sigusr1_sender_pids[5];
|
||||
{
|
||||
pthread_barrierattr_t attr;
|
||||
xpthread_barrierattr_init (&attr);
|
||||
@@ -166,7 +187,7 @@ do_test (void)
|
||||
}
|
||||
for (size_t i = 0; i < array_length (sigusr1_sender_pids); ++i)
|
||||
{
|
||||
sigusr1_sender_pids[i] = fork ();
|
||||
sigusr1_sender_pids[i] = xfork ();
|
||||
if (sigusr1_sender_pids[i] == 0)
|
||||
signal_sender (SIGUSR1, false);
|
||||
}
|
||||
@@ -211,7 +232,7 @@ do_test (void)
|
||||
++malloc_signals;
|
||||
xpthread_barrier_wait (&shared->barrier);
|
||||
|
||||
if (objects[slot] == NULL)
|
||||
if (objects[slot] == NULL || error_indicator != 0)
|
||||
{
|
||||
printf ("error: malloc: %m\n");
|
||||
for (size_t i = 0; i < array_length (sigusr1_sender_pids); ++i)
|
||||
@@ -225,10 +246,6 @@ do_test (void)
|
||||
for (int slot = 0; slot < malloc_objects; ++slot)
|
||||
free (objects[slot]);
|
||||
|
||||
for (size_t i = 0; i < array_length (sigusr1_sender_pids); ++i)
|
||||
kill (sigusr1_sender_pids[i], SIGKILL);
|
||||
kill (sigusr2_sender_pid, SIGKILL);
|
||||
|
||||
printf ("info: signals received during fork: %u\n", fork_signals);
|
||||
printf ("info: signals received during free: %u\n", free_signals);
|
||||
printf ("info: signals received during malloc: %u\n", malloc_signals);
|
||||
|
||||
+3
-8
@@ -214,14 +214,9 @@ increased program load times.
|
||||
@item --enable-pt_chown
|
||||
The file @file{pt_chown} is a helper binary for @code{grantpt}
|
||||
(@pxref{Allocation, Pseudo-Terminals}) that is installed setuid root to
|
||||
fix up pseudo-terminal ownership. It is not built by default because
|
||||
systems using the Linux kernel are commonly built with the @code{devpts}
|
||||
filesystem enabled and mounted at @file{/dev/pts}, which manages
|
||||
pseudo-terminal ownership automatically. By using
|
||||
@samp{--enable-pt_chown}, you may build @file{pt_chown} and install it
|
||||
setuid and owned by @code{root}. The use of @file{pt_chown} introduces
|
||||
additional security risks to the system and you should enable it only if
|
||||
you understand and accept those risks.
|
||||
fix up pseudo-terminal ownership on GNU/Hurd. It is not required on
|
||||
GNU/Linux, and @theglibc{} will not use the installed @file{pt_chown}
|
||||
program when configured with @option{--enable-pt_chown}.
|
||||
|
||||
@item --disable-werror
|
||||
By default, @theglibc{} is built with @option{-Werror}. If you wish
|
||||
|
||||
@@ -391,7 +391,11 @@ set shared cache size in bytes for use in memory and string routines.
|
||||
|
||||
@deftp Tunable glibc.cpu.x86_non_temporal_threshold
|
||||
The @code{glibc.cpu.x86_non_temporal_threshold} tunable allows the user
|
||||
to set threshold in bytes for non temporal store.
|
||||
to set threshold in bytes for non temporal store. Non temporal stores
|
||||
give a hint to the hardware to move data directly to memory without
|
||||
displacing other data from the cache. This tunable is used by some
|
||||
platforms to determine when to use non temporal stores in operations
|
||||
like memmove and memcpy.
|
||||
|
||||
This tunable is specific to i386 and x86-64.
|
||||
@end deftp
|
||||
|
||||
+122
@@ -644,6 +644,128 @@ ifneq ($(long-double-fcts),yes)
|
||||
# We won't compile the `long double' code at all. Tell the `double' code
|
||||
# to define aliases for the `FUNCl' names.
|
||||
math-CPPFLAGS += -DNO_LONG_DOUBLE
|
||||
# GCC 10 diagnoses aliases with types conflicting with built-in
|
||||
# functions.
|
||||
CFLAGS-w_acos.c += -fno-builtin-acosl
|
||||
CFLAGS-w_acosh.c += -fno-builtin-acoshl
|
||||
CFLAGS-w_asin.c += -fno-builtin-asinl
|
||||
CFLAGS-s_asinh.c += -fno-builtin-asinhl
|
||||
CFLAGS-s_atan.c += -fno-builtin-atanl
|
||||
CFLAGS-w_atan2.c += -fno-builtin-atan2l
|
||||
CFLAGS-w_atanh.c += -fno-builtin-atanhl
|
||||
CFLAGS-s_cabs.c += -fno-builtin-cabsl
|
||||
CFLAGS-s_cacos.c += -fno-builtin-cacosl
|
||||
CFLAGS-s_cacosh.c += -fno-builtin-cacoshl
|
||||
CFLAGS-s_canonicalize.c += -fno-builtin-canonicalizel
|
||||
CFLAGS-s_carg.c += -fno-builtin-cargl
|
||||
CFLAGS-s_casin.c += -fno-builtin-casinl
|
||||
CFLAGS-s_casinh.c += -fno-builtin-casinhl
|
||||
CFLAGS-s_catan.c += -fno-builtin-catanl
|
||||
CFLAGS-s_catanh.c += -fno-builtin-catanhl
|
||||
CFLAGS-s_cbrt.c += -fno-builtin-cbrtl
|
||||
CFLAGS-s_ccos.c += -fno-builtin-ccosl
|
||||
CFLAGS-s_ccosh.c += -fno-builtin-ccoshl
|
||||
CFLAGS-s_ceil.c += -fno-builtin-ceill
|
||||
CFLAGS-s_cexp.c += -fno-builtin-cexpl
|
||||
CFLAGS-s_cimag.c += -fno-builtin-cimagl
|
||||
CFLAGS-s_clog.c += -fno-builtin-clogl
|
||||
CFLAGS-s_clog10.c += -fno-builtin-clog10l
|
||||
CFLAGS-s_conj.c += -fno-builtin-conjl
|
||||
CFLAGS-s_copysign.c += -fno-builtin-copysignl
|
||||
CFLAGS-s_cos.c += -fno-builtin-cosl
|
||||
CFLAGS-w_cosh.c += -fno-builtin-coshl
|
||||
CFLAGS-s_cpow.c += -fno-builtin-cpowl
|
||||
CFLAGS-s_cproj.c += -fno-builtin-cprojl
|
||||
CFLAGS-s_creal.c += -fno-builtin-creall
|
||||
CFLAGS-s_csin.c += -fno-builtin-csinl
|
||||
CFLAGS-s_csinh.c += -fno-builtin-csinhl
|
||||
CFLAGS-s_csqrt.c += -fno-builtin-csqrtl
|
||||
CFLAGS-s_ctan.c += -fno-builtin-ctanl
|
||||
CFLAGS-s_ctanh.c += -fno-builtin-ctanhl
|
||||
CFLAGS-s_dadd.c += -fno-builtin-daddl
|
||||
CFLAGS-s_ddiv.c += -fno-builtin-ddivl
|
||||
CFLAGS-s_dmul.c += -fno-builtin-dmull
|
||||
CFLAGS-s_dsub.c += -fno-builtin-dsubl
|
||||
CFLAGS-s_erf.c += -fno-builtin-erfl
|
||||
CFLAGS-s_erfc.c += -fno-builtin-erfcl
|
||||
CFLAGS-e_exp.c += -fno-builtin-expl
|
||||
CFLAGS-w_exp10.c += -fno-builtin-exp10l
|
||||
CFLAGS-e_exp2.c += -fno-builtin-exp2l
|
||||
CFLAGS-s_expm1.c += -fno-builtin-expm1l
|
||||
CFLAGS-s_fabs.c += -fno-builtin-fabsl
|
||||
CFLAGS-s_fadd.c += -fno-builtin-faddl
|
||||
CFLAGS-s_fdim.c += -fno-builtin-fdiml
|
||||
CFLAGS-s_fdiv.c += -fno-builtin-fdivl
|
||||
CFLAGS-s_finite.c += -fno-builtin-finitel
|
||||
CFLAGS-s_floor.c += -fno-builtin-floorl
|
||||
CFLAGS-s_fma.c += -fno-builtin-fmal
|
||||
CFLAGS-s_fmax.c += -fno-builtin-fmaxl
|
||||
CFLAGS-s_fmaxmag.c += -fno-builtin-fmaxmagl
|
||||
CFLAGS-s_fmin.c += -fno-builtin-fminl
|
||||
CFLAGS-s_fminmag.c += -fno-builtin-fminmagl
|
||||
CFLAGS-w_fmod.c += -fno-builtin-fmodl
|
||||
CFLAGS-s_fmul.c += -fno-builtin-fmull
|
||||
CFLAGS-s_frexp.c += -fno-builtin-frexpl
|
||||
CFLAGS-s_fromfp.c += -fno-builtin-fromfpl
|
||||
CFLAGS-s_fromfpx.c += -fno-builtin-fromfpxl
|
||||
CFLAGS-s_fsub.c += -fno-builtin-fsubl
|
||||
CFLAGS-s_gamma.c += -fno-builtin-gammal
|
||||
CFLAGS-s_getpayload.c += -fno-builtin-getpayloadl
|
||||
CFLAGS-w_hypot.c += -fno-builtin-hypotl
|
||||
CFLAGS-w_ilogb.c += -fno-builtin-ilogbl
|
||||
CFLAGS-s_isinf.c += -fno-builtin-isinfl
|
||||
CFLAGS-s_isnan.c += -fno-builtin-isnanl
|
||||
CFLAGS-w_j0.c += -fno-builtin-j0l
|
||||
CFLAGS-w_j1.c += -fno-builtin-j1l
|
||||
CFLAGS-w_jn.c += -fno-builtin-jnl
|
||||
CFLAGS-s_ldexp.c += -fno-builtin-ldexpl
|
||||
CFLAGS-w_lgamma.c += -fno-builtin-lgammal
|
||||
CFLAGS-w_lgamma_r.c += -fno-builtin-lgammal_r
|
||||
CFLAGS-w_llogb.c += -fno-builtin-llogbl
|
||||
CFLAGS-s_llrint.c += -fno-builtin-llrintl
|
||||
CFLAGS-s_llround.c += -fno-builtin-llroundl
|
||||
CFLAGS-e_log.c += -fno-builtin-logl
|
||||
CFLAGS-w_log10.c += -fno-builtin-log10l
|
||||
CFLAGS-w_log1p.c += -fno-builtin-log1pl
|
||||
CFLAGS-e_log2.c += -fno-builtin-log2l
|
||||
CFLAGS-s_logb.c += -fno-builtin-logbl
|
||||
CFLAGS-s_lrint.c += -fno-builtin-lrintl
|
||||
CFLAGS-s_lround.c += -fno-builtin-lroundl
|
||||
CFLAGS-s_modf.c += -fno-builtin-modfl
|
||||
CFLAGS-s_nan.c += -fno-builtin-nanl
|
||||
CFLAGS-s_nearbyint.c += -fno-builtin-nearbyintl
|
||||
CFLAGS-s_nextafter.c += -fno-builtin-nextafterl
|
||||
CFLAGS-s_nextdown.c += -fno-builtin-nextdownl
|
||||
CFLAGS-s_nexttoward.c += -fno-builtin-nexttoward -fno-builtin-nexttowardl
|
||||
CFLAGS-s_nexttowardf.c += -fno-builtin-nexttowardf
|
||||
CFLAGS-s_nextup.c += -fno-builtin-nextupl
|
||||
CFLAGS-e_pow.c += -fno-builtin-powl
|
||||
CFLAGS-w_remainder.c += -fno-builtin-remainderl -fno-builtin-dreml
|
||||
CFLAGS-s_remquo.c += -fno-builtin-remquol
|
||||
CFLAGS-s_rint.c += -fno-builtin-rintl
|
||||
CFLAGS-s_round.c += -fno-builtin-roundl
|
||||
CFLAGS-s_roundeven.c += -fno-builtin-roundevenl
|
||||
CFLAGS-w_scalb.c += -fno-builtin-scalbl
|
||||
CFLAGS-w_scalbln.c += -fno-builtin-scalblnl
|
||||
CFLAGS-s_scalbn.c += -fno-builtin-scalbnl
|
||||
CFLAGS-s_setpayload.c += -fno-builtin-setpayloadl
|
||||
CFLAGS-s_setpayloadsig.c += -fno-builtin-setpayloadsigl
|
||||
CFLAGS-s_significand.c += -fno-builtin-significandl
|
||||
CFLAGS-s_sin.c += -fno-builtin-sinl
|
||||
CFLAGS-s_sincos.c += -fno-builtin-sincosl
|
||||
CFLAGS-w_sinh.c += -fno-builtin-sinhl
|
||||
CFLAGS-w_sqrt.c += -fno-builtin-sqrtl
|
||||
CFLAGS-s_tan.c += -fno-builtin-tanl
|
||||
CFLAGS-s_tanh.c += -fno-builtin-tanhl
|
||||
CFLAGS-w_tgamma.c += -fno-builtin-tgammal
|
||||
CFLAGS-s_totalorder.c += -fno-builtin-totalorderl
|
||||
CFLAGS-s_totalordermag.c += -fno-builtin-totalordermagl
|
||||
CFLAGS-s_trunc.c += -fno-builtin-truncl
|
||||
CFLAGS-s_ufromfp.c += -fno-builtin-ufromfpl
|
||||
CFLAGS-s_ufromfpx.c += -fno-builtin-ufromfpxl
|
||||
CFLAGS-s_y0.c += -fno-builtin-y0l
|
||||
CFLAGS-s_y1.c += -fno-builtin-y1l
|
||||
CFLAGS-s_yn.c += -fno-builtin-ynl
|
||||
endif
|
||||
|
||||
# These files quiet sNaNs in a way that is optimized away without
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ tests := tst-dirname tst-tsearch tst-fdset tst-mntent tst-hsearch \
|
||||
tst-preadvwritev tst-preadvwritev64 tst-makedev tst-empty \
|
||||
tst-preadvwritev2 tst-preadvwritev64v2 tst-warn-wide \
|
||||
tst-ldbl-warn tst-ldbl-error tst-dbl-efgcvt tst-ldbl-efgcvt \
|
||||
tst-mntent-autofs
|
||||
tst-mntent-autofs tst-syscalls
|
||||
|
||||
# Tests which need libdl.
|
||||
ifeq (yes,$(build-shared))
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/* Test for syscall interfaces.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test verifies that the x32 system call handling zero-extends
|
||||
unsigned 32-bit arguments to the 64-bit argument registers for
|
||||
system calls (bug 25810). The bug is specific to x32, but the test
|
||||
should pass on all architectures. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
/* On x32, this can be passed in a single 64-bit integer register. */
|
||||
struct Array
|
||||
{
|
||||
size_t length;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static int error_count;
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
struct Array
|
||||
allocate (size_t bytes)
|
||||
{
|
||||
if (!bytes)
|
||||
return __extension__ (struct Array) {0, 0};
|
||||
|
||||
void *p = mmap (0x0, bytes, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
return __extension__ (struct Array) {0, 0};
|
||||
|
||||
return __extension__ (struct Array) {bytes, p};
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
void
|
||||
deallocate (struct Array b)
|
||||
{
|
||||
/* On x32, the 64-bit integer register containing `b' may be copied
|
||||
to another 64-bit integer register to pass the second argument to
|
||||
munmap. */
|
||||
if (b.length && munmap (b.ptr, b.length))
|
||||
{
|
||||
printf ("munmap error: %m\n");
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
void *
|
||||
do_mmap (void *addr, size_t length)
|
||||
{
|
||||
return mmap (addr, length, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
void *
|
||||
reallocate (struct Array b)
|
||||
{
|
||||
/* On x32, the 64-bit integer register containing `b' may be copied
|
||||
to another 64-bit integer register to pass the second argument to
|
||||
do_mmap. */
|
||||
if (b.length)
|
||||
return do_mmap (b.ptr, b.length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
void
|
||||
protect (struct Array b)
|
||||
{
|
||||
if (b.length)
|
||||
{
|
||||
/* On x32, the 64-bit integer register containing `b' may be copied
|
||||
to another 64-bit integer register to pass the second argument
|
||||
to mprotect. */
|
||||
if (mprotect (b.ptr, b.length,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
{
|
||||
printf ("mprotect error: %m\n");
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
ssize_t
|
||||
do_read (int fd, void *ptr, struct Array b)
|
||||
{
|
||||
/* On x32, the 64-bit integer register containing `b' may be copied
|
||||
to another 64-bit integer register to pass the second argument to
|
||||
read. */
|
||||
if (b.length)
|
||||
return read (fd, ptr, b.length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__ ((noclone, noinline))
|
||||
ssize_t
|
||||
do_write (int fd, void *ptr, struct Array b)
|
||||
{
|
||||
/* On x32, the 64-bit integer register containing `b' may be copied
|
||||
to another 64-bit integer register to pass the second argument to
|
||||
write. */
|
||||
if (b.length)
|
||||
return write (fd, ptr, b.length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct Array array;
|
||||
|
||||
array = allocate (1);
|
||||
protect (array);
|
||||
deallocate (array);
|
||||
void *p = reallocate (array);
|
||||
if (p == MAP_FAILED)
|
||||
{
|
||||
printf ("mmap error: %m\n");
|
||||
error_count++;
|
||||
}
|
||||
array.ptr = p;
|
||||
protect (array);
|
||||
deallocate (array);
|
||||
|
||||
int fd = xopen ("/dev/null", O_RDWR, 0);
|
||||
char buf[2];
|
||||
array.ptr = buf;
|
||||
if (do_read (fd, array.ptr, array) == -1)
|
||||
{
|
||||
printf ("read error: %m\n");
|
||||
error_count++;
|
||||
}
|
||||
if (do_write (fd, array.ptr, array) == -1)
|
||||
{
|
||||
printf ("write error: %m\n");
|
||||
error_count++;
|
||||
}
|
||||
xclose (fd);
|
||||
|
||||
return error_count ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+1
-1
@@ -332,7 +332,7 @@ tests-internal := tst-rwlock19 tst-rwlock20 \
|
||||
tst-mutexpi8 tst-mutexpi8-static tst-cancel25
|
||||
|
||||
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
|
||||
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
|
||||
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups
|
||||
|
||||
# This test can run into task limits because of a linux kernel bug
|
||||
# and then cause the make process to fail too, see bug 24537.
|
||||
|
||||
+7
-1
@@ -94,7 +94,13 @@ struct pthread_unwind_buf
|
||||
struct xid_command
|
||||
{
|
||||
int syscall_no;
|
||||
long int id[3];
|
||||
/* Enforce zero-extension for the pointer argument in
|
||||
|
||||
int setgroups (size_t size, const gid_t *list);
|
||||
|
||||
The kernel XID arguments are unsigned and do not require sign
|
||||
extension. */
|
||||
unsigned long int id[3];
|
||||
volatile int cntr;
|
||||
volatile int error; /* -1: no call yet, 0: success seen, >0: error seen. */
|
||||
};
|
||||
|
||||
@@ -57,7 +57,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;
|
||||
@@ -80,7 +81,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. */
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/* Test setgroups as root and in the presence of threads (Bug 26248)
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <grp.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <support/xthread.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
#include <support/xunistd.h>
|
||||
|
||||
/* The purpose of this test is to test the setgroups API as root and in
|
||||
the presence of threads. Once we create a thread the setgroups
|
||||
implementation must ensure that all threads are set to the same
|
||||
group and this operation should not fail. Lastly we test setgroups
|
||||
with a zero sized group and a bad address and verify we get EPERM. */
|
||||
|
||||
static void *
|
||||
start_routine (void *args)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
int size;
|
||||
/* NB: Stack address can be at 0xfffXXXXX on 32-bit OSes. */
|
||||
gid_t list[NGROUPS_MAX];
|
||||
int status = EXIT_SUCCESS;
|
||||
|
||||
pthread_t thread = xpthread_create (NULL, start_routine, NULL);
|
||||
|
||||
size = getgroups (sizeof (list) / sizeof (list[0]), list);
|
||||
if (size < 0)
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
error (0, errno, "getgroups failed");
|
||||
}
|
||||
if (setgroups (size, list) < 0)
|
||||
{
|
||||
if (errno == EPERM)
|
||||
status = EXIT_UNSUPPORTED;
|
||||
else
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
error (0, errno, "setgroups failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (status == EXIT_SUCCESS && setgroups (0, list) < 0)
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
error (0, errno, "setgroups failed");
|
||||
}
|
||||
|
||||
xpthread_join (thread);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+137
-112
@@ -24,6 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <scratch_buffer.h>
|
||||
|
||||
#include "../inet/netgroup.h"
|
||||
#include "nscd.h"
|
||||
@@ -66,6 +67,16 @@ struct dataset
|
||||
char strdata[0];
|
||||
};
|
||||
|
||||
/* Send a notfound response to FD. Always returns -1 to indicate an
|
||||
ephemeral error. */
|
||||
static time_t
|
||||
send_notfound (int fd)
|
||||
{
|
||||
if (fd != -1)
|
||||
TEMP_FAILURE_RETRY (send (fd, ¬found, sizeof (notfound), MSG_NOSIGNAL));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Sends a notfound message and prepares a notfound dataset to write to the
|
||||
cache. Returns true if there was enough memory to allocate the dataset and
|
||||
returns the dataset in DATASETP, total bytes to write in TOTALP and the
|
||||
@@ -84,8 +95,7 @@ do_notfound (struct database_dyn *db, int fd, request_header *req,
|
||||
total = sizeof (notfound);
|
||||
timeout = time (NULL) + db->negtimeout;
|
||||
|
||||
if (fd != -1)
|
||||
TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL));
|
||||
send_notfound (fd);
|
||||
|
||||
dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
|
||||
/* If we cannot permanently store the result, so be it. */
|
||||
@@ -110,11 +120,78 @@ do_notfound (struct database_dyn *db, int fd, request_header *req,
|
||||
return cacheable;
|
||||
}
|
||||
|
||||
struct addgetnetgrentX_scratch
|
||||
{
|
||||
/* This is the result that the caller should use. It can be NULL,
|
||||
point into buffer, or it can be in the cache. */
|
||||
struct dataset *dataset;
|
||||
|
||||
struct scratch_buffer buffer;
|
||||
|
||||
/* Used internally in addgetnetgrentX as a staging area. */
|
||||
struct scratch_buffer tmp;
|
||||
|
||||
/* Number of bytes in buffer that are actually used. */
|
||||
size_t buffer_used;
|
||||
};
|
||||
|
||||
static void
|
||||
addgetnetgrentX_scratch_init (struct addgetnetgrentX_scratch *scratch)
|
||||
{
|
||||
scratch->dataset = NULL;
|
||||
scratch_buffer_init (&scratch->buffer);
|
||||
scratch_buffer_init (&scratch->tmp);
|
||||
|
||||
/* Reserve space for the header. */
|
||||
scratch->buffer_used = sizeof (struct dataset);
|
||||
static_assert (sizeof (struct dataset) < sizeof (scratch->tmp.__space),
|
||||
"initial buffer space");
|
||||
memset (scratch->tmp.data, 0, sizeof (struct dataset));
|
||||
}
|
||||
|
||||
static void
|
||||
addgetnetgrentX_scratch_free (struct addgetnetgrentX_scratch *scratch)
|
||||
{
|
||||
scratch_buffer_free (&scratch->buffer);
|
||||
scratch_buffer_free (&scratch->tmp);
|
||||
}
|
||||
|
||||
/* Copy LENGTH bytes from S into SCRATCH. Returns NULL if SCRATCH
|
||||
could not be resized, otherwise a pointer to the copy. */
|
||||
static char *
|
||||
addgetnetgrentX_append_n (struct addgetnetgrentX_scratch *scratch,
|
||||
const char *s, size_t length)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
size_t remaining = scratch->buffer.length - scratch->buffer_used;
|
||||
if (remaining >= length)
|
||||
break;
|
||||
if (!scratch_buffer_grow_preserve (&scratch->buffer))
|
||||
return NULL;
|
||||
}
|
||||
char *copy = scratch->buffer.data + scratch->buffer_used;
|
||||
memcpy (copy, s, length);
|
||||
scratch->buffer_used += length;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/* Copy S into SCRATCH, including its null terminator. Returns false
|
||||
if SCRATCH could not be resized. */
|
||||
static bool
|
||||
addgetnetgrentX_append (struct addgetnetgrentX_scratch *scratch, const char *s)
|
||||
{
|
||||
if (s == NULL)
|
||||
s = "";
|
||||
return addgetnetgrentX_append_n (scratch, s, strlen (s) + 1) != NULL;
|
||||
}
|
||||
|
||||
/* Caller must initialize and free *SCRATCH. If the return value is
|
||||
negative, this function has sent a notfound response. */
|
||||
static time_t
|
||||
addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
struct datahead *dh, struct dataset **resultp,
|
||||
void **tofreep)
|
||||
struct datahead *dh, struct addgetnetgrentX_scratch *scratch)
|
||||
{
|
||||
if (__glibc_unlikely (debug_level > 0))
|
||||
{
|
||||
@@ -133,14 +210,10 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
|
||||
char *key_copy = NULL;
|
||||
struct __netgrent data;
|
||||
size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
|
||||
size_t buffilled = sizeof (*dataset);
|
||||
char *buffer = NULL;
|
||||
size_t nentries = 0;
|
||||
size_t group_len = strlen (key) + 1;
|
||||
struct name_list *first_needed
|
||||
= alloca (sizeof (struct name_list) + group_len);
|
||||
*tofreep = NULL;
|
||||
|
||||
if (netgroup_database == NULL
|
||||
&& __nss_database_lookup2 ("netgroup", NULL, NULL, &netgroup_database))
|
||||
@@ -148,12 +221,10 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
/* No such service. */
|
||||
cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
|
||||
&key_copy);
|
||||
goto writeout;
|
||||
goto maybe_cache_add;
|
||||
}
|
||||
|
||||
memset (&data, '\0', sizeof (data));
|
||||
buffer = xmalloc (buflen);
|
||||
*tofreep = buffer;
|
||||
first_needed->next = first_needed;
|
||||
memcpy (first_needed->name, key, group_len);
|
||||
data.needed_groups = first_needed;
|
||||
@@ -196,8 +267,8 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
while (1)
|
||||
{
|
||||
int e;
|
||||
status = getfct.f (&data, buffer + buffilled,
|
||||
buflen - buffilled - req->key_len, &e);
|
||||
status = getfct.f (&data, scratch->tmp.data,
|
||||
scratch->tmp.length, &e);
|
||||
if (status == NSS_STATUS_SUCCESS)
|
||||
{
|
||||
if (data.type == triple_val)
|
||||
@@ -205,68 +276,10 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *nhost = data.val.triple.host;
|
||||
const char *nuser = data.val.triple.user;
|
||||
const char *ndomain = data.val.triple.domain;
|
||||
|
||||
size_t hostlen = strlen (nhost ?: "") + 1;
|
||||
size_t userlen = strlen (nuser ?: "") + 1;
|
||||
size_t domainlen = strlen (ndomain ?: "") + 1;
|
||||
|
||||
if (nhost == NULL || nuser == NULL || ndomain == NULL
|
||||
|| nhost > nuser || nuser > ndomain)
|
||||
{
|
||||
const char *last = nhost;
|
||||
if (last == NULL
|
||||
|| (nuser != NULL && nuser > last))
|
||||
last = nuser;
|
||||
if (last == NULL
|
||||
|| (ndomain != NULL && ndomain > last))
|
||||
last = ndomain;
|
||||
|
||||
size_t bufused
|
||||
= (last == NULL
|
||||
? buffilled
|
||||
: last + strlen (last) + 1 - buffer);
|
||||
|
||||
/* We have to make temporary copies. */
|
||||
size_t needed = hostlen + userlen + domainlen;
|
||||
|
||||
if (buflen - req->key_len - bufused < needed)
|
||||
{
|
||||
buflen += MAX (buflen, 2 * needed);
|
||||
/* Save offset in the old buffer. We don't
|
||||
bother with the NULL check here since
|
||||
we'll do that later anyway. */
|
||||
size_t nhostdiff = nhost - buffer;
|
||||
size_t nuserdiff = nuser - buffer;
|
||||
size_t ndomaindiff = ndomain - buffer;
|
||||
|
||||
char *newbuf = xrealloc (buffer, buflen);
|
||||
/* Fix up the triplet pointers into the new
|
||||
buffer. */
|
||||
nhost = (nhost ? newbuf + nhostdiff
|
||||
: NULL);
|
||||
nuser = (nuser ? newbuf + nuserdiff
|
||||
: NULL);
|
||||
ndomain = (ndomain ? newbuf + ndomaindiff
|
||||
: NULL);
|
||||
buffer = newbuf;
|
||||
}
|
||||
|
||||
nhost = memcpy (buffer + bufused,
|
||||
nhost ?: "", hostlen);
|
||||
nuser = memcpy ((char *) nhost + hostlen,
|
||||
nuser ?: "", userlen);
|
||||
ndomain = memcpy ((char *) nuser + userlen,
|
||||
ndomain ?: "", domainlen);
|
||||
}
|
||||
|
||||
char *wp = buffer + buffilled;
|
||||
wp = memmove (wp, nhost ?: "", hostlen);
|
||||
wp += hostlen;
|
||||
wp = memmove (wp, nuser ?: "", userlen);
|
||||
wp += userlen;
|
||||
wp = memmove (wp, ndomain ?: "", domainlen);
|
||||
wp += domainlen;
|
||||
buffilled = wp - buffer;
|
||||
if (!(addgetnetgrentX_append (scratch, nhost)
|
||||
&& addgetnetgrentX_append (scratch, nuser)
|
||||
&& addgetnetgrentX_append (scratch, ndomain)))
|
||||
return send_notfound (fd);
|
||||
++nentries;
|
||||
}
|
||||
else
|
||||
@@ -318,8 +331,8 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
|
||||
{
|
||||
buflen *= 2;
|
||||
buffer = xrealloc (buffer, buflen);
|
||||
if (!scratch_buffer_grow (&scratch->tmp))
|
||||
return send_notfound (fd);
|
||||
}
|
||||
else if (status == NSS_STATUS_RETURN
|
||||
|| status == NSS_STATUS_NOTFOUND
|
||||
@@ -349,13 +362,20 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
{
|
||||
cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
|
||||
&key_copy);
|
||||
goto writeout;
|
||||
goto maybe_cache_add;
|
||||
}
|
||||
|
||||
total = buffilled;
|
||||
/* Capture the result size without the key appended. */
|
||||
total = scratch->buffer_used;
|
||||
|
||||
/* Make a copy of the key. The scratch buffer must not move after
|
||||
this point. */
|
||||
key_copy = addgetnetgrentX_append_n (scratch, key, req->key_len);
|
||||
if (key_copy == NULL)
|
||||
return send_notfound (fd);
|
||||
|
||||
/* Fill in the dataset. */
|
||||
dataset = (struct dataset *) buffer;
|
||||
dataset = scratch->buffer.data;
|
||||
timeout = datahead_init_pos (&dataset->head, total + req->key_len,
|
||||
total - offsetof (struct dataset, resp),
|
||||
he == NULL ? 0 : dh->nreloads + 1,
|
||||
@@ -364,11 +384,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
dataset->resp.version = NSCD_VERSION;
|
||||
dataset->resp.found = 1;
|
||||
dataset->resp.nresults = nentries;
|
||||
dataset->resp.result_len = buffilled - sizeof (*dataset);
|
||||
|
||||
assert (buflen - buffilled >= req->key_len);
|
||||
key_copy = memcpy (buffer + buffilled, key, req->key_len);
|
||||
buffilled += req->key_len;
|
||||
dataset->resp.result_len = total - sizeof (*dataset);
|
||||
|
||||
/* Now we can determine whether on refill we have to create a new
|
||||
record or not. */
|
||||
@@ -399,7 +415,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
if (__glibc_likely (newp != NULL))
|
||||
{
|
||||
/* Adjust pointer into the memory block. */
|
||||
key_copy = (char *) newp + (key_copy - buffer);
|
||||
key_copy = (char *) newp + (key_copy - (char *) dataset);
|
||||
|
||||
dataset = memcpy (newp, dataset, total + req->key_len);
|
||||
cacheable = true;
|
||||
@@ -411,14 +427,12 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
if (he == NULL && fd != -1)
|
||||
{
|
||||
/* We write the dataset before inserting it to the database
|
||||
since while inserting this thread might block and so would
|
||||
unnecessarily let the receiver wait. */
|
||||
writeout:
|
||||
/* We write the dataset before inserting it to the database since
|
||||
while inserting this thread might block and so would
|
||||
unnecessarily let the receiver wait. */
|
||||
writeall (fd, &dataset->resp, dataset->head.recsize);
|
||||
}
|
||||
|
||||
maybe_cache_add:
|
||||
if (cacheable)
|
||||
{
|
||||
/* If necessary, we also propagate the data to disk. */
|
||||
@@ -442,7 +456,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
out:
|
||||
*resultp = dataset;
|
||||
scratch->dataset = dataset;
|
||||
|
||||
return timeout;
|
||||
}
|
||||
@@ -463,6 +477,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
if (user != NULL)
|
||||
key = (char *) rawmemchr (key, '\0') + 1;
|
||||
const char *domain = *key++ ? key : NULL;
|
||||
struct addgetnetgrentX_scratch scratch;
|
||||
|
||||
addgetnetgrentX_scratch_init (&scratch);
|
||||
|
||||
if (__glibc_unlikely (debug_level > 0))
|
||||
{
|
||||
@@ -478,12 +495,8 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
group, group_len,
|
||||
db, uid);
|
||||
time_t timeout;
|
||||
void *tofree;
|
||||
if (result != NULL)
|
||||
{
|
||||
timeout = result->head.timeout;
|
||||
tofree = NULL;
|
||||
}
|
||||
timeout = result->head.timeout;
|
||||
else
|
||||
{
|
||||
request_header req_get =
|
||||
@@ -492,7 +505,10 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
.key_len = group_len
|
||||
};
|
||||
timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
|
||||
&result, &tofree);
|
||||
&scratch);
|
||||
result = scratch.dataset;
|
||||
if (timeout < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct indataset
|
||||
@@ -503,24 +519,26 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
= (struct indataset *) mempool_alloc (db,
|
||||
sizeof (*dataset) + req->key_len,
|
||||
1);
|
||||
struct indataset dataset_mem;
|
||||
bool cacheable = true;
|
||||
if (__glibc_unlikely (dataset == NULL))
|
||||
{
|
||||
cacheable = false;
|
||||
dataset = &dataset_mem;
|
||||
/* The alloca is safe because nscd_run_worker verfies that
|
||||
key_len is not larger than MAXKEYLEN. */
|
||||
dataset = alloca (sizeof (*dataset) + req->key_len);
|
||||
}
|
||||
|
||||
datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
|
||||
sizeof (innetgroup_response_header),
|
||||
he == NULL ? 0 : dh->nreloads + 1, result->head.ttl);
|
||||
he == NULL ? 0 : dh->nreloads + 1,
|
||||
result == NULL ? db->negtimeout : result->head.ttl);
|
||||
/* Set the notfound status and timeout based on the result from
|
||||
getnetgrent. */
|
||||
dataset->head.notfound = result->head.notfound;
|
||||
dataset->head.notfound = result == NULL || result->head.notfound;
|
||||
dataset->head.timeout = timeout;
|
||||
|
||||
dataset->resp.version = NSCD_VERSION;
|
||||
dataset->resp.found = result->resp.found;
|
||||
dataset->resp.found = result != NULL && result->resp.found;
|
||||
/* Until we find a matching entry the result is 0. */
|
||||
dataset->resp.result = 0;
|
||||
|
||||
@@ -568,7 +586,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (he == NULL)
|
||||
/* addgetnetgrentX may have already sent a notfound response. Do
|
||||
not send another one. */
|
||||
if (he == NULL && dataset->resp.found)
|
||||
{
|
||||
/* We write the dataset before inserting it to the database
|
||||
since while inserting this thread might block and so would
|
||||
@@ -602,7 +622,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
out:
|
||||
free (tofree);
|
||||
addgetnetgrentX_scratch_free (&scratch);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -612,11 +632,12 @@ addgetnetgrentX_ignore (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
struct datahead *dh)
|
||||
{
|
||||
struct dataset *ignore;
|
||||
void *tofree;
|
||||
time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh,
|
||||
&ignore, &tofree);
|
||||
free (tofree);
|
||||
struct addgetnetgrentX_scratch scratch;
|
||||
addgetnetgrentX_scratch_init (&scratch);
|
||||
time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh, &scratch);
|
||||
addgetnetgrentX_scratch_free (&scratch);
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -660,5 +681,9 @@ readdinnetgr (struct database_dyn *db, struct hashentry *he,
|
||||
.key_len = he->len
|
||||
};
|
||||
|
||||
return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
|
||||
time_t timeout = addinnetgrX (db, -1, &req, db->data + he->key, he->owner,
|
||||
he, dh);
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#ifdef HAVE_LIBAUDIT
|
||||
# include <libaudit.h>
|
||||
#endif
|
||||
#include <libc-diag.h>
|
||||
|
||||
#include "dbg_log.h"
|
||||
#include "selinux.h"
|
||||
@@ -320,6 +321,12 @@ avc_free_lock (void *lock)
|
||||
}
|
||||
|
||||
|
||||
/* avc_init (along with several other symbols) was marked as deprecated by the
|
||||
SELinux API starting from version 3.1. We use it here, but should
|
||||
eventually switch to the newer API. */
|
||||
DIAG_PUSH_NEEDS_COMMENT
|
||||
DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
|
||||
|
||||
/* Initialize the user space access vector cache (AVC) for NSCD along with
|
||||
log/thread/lock callbacks. */
|
||||
void
|
||||
@@ -335,8 +342,15 @@ nscd_avc_init (void)
|
||||
audit_init ();
|
||||
#endif
|
||||
}
|
||||
DIAG_POP_NEEDS_COMMENT
|
||||
|
||||
|
||||
/* security_context_t and sidput (along with several other symbols) were marked
|
||||
as deprecated by the SELinux API starting from version 3.1. We use them
|
||||
here, but should eventually switch to the newer API. */
|
||||
DIAG_PUSH_NEEDS_COMMENT
|
||||
DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
|
||||
|
||||
/* Check the permission from the caller (via getpeercon) to nscd.
|
||||
Returns 0 if access is allowed, 1 if denied, and -1 on error.
|
||||
|
||||
@@ -422,6 +436,7 @@ out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
DIAG_POP_NEEDS_COMMENT
|
||||
|
||||
|
||||
/* Wrapper to get AVC statistics. */
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include "nss_db/nss_db.h"
|
||||
#include <libc-diag.h>
|
||||
|
||||
/* Get libc version number. */
|
||||
#include "../version.h"
|
||||
@@ -841,6 +842,13 @@ print_database (int fd)
|
||||
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
|
||||
/* security_context_t and matchpathcon (along with several other symbols) were
|
||||
marked as deprecated by the SELinux API starting from version 3.1. We use
|
||||
them here, but should eventually switch to the newer API. */
|
||||
DIAG_PUSH_NEEDS_COMMENT
|
||||
DIAG_IGNORE_NEEDS_COMMENT (10, "-Wdeprecated-declarations");
|
||||
|
||||
static void
|
||||
set_file_creation_context (const char *outname, mode_t mode)
|
||||
{
|
||||
@@ -870,6 +878,7 @@ set_file_creation_context (const char *outname, mode_t mode)
|
||||
freecon (ctx);
|
||||
}
|
||||
}
|
||||
DIAG_POP_NEEDS_COMMENT
|
||||
|
||||
static void
|
||||
reset_file_creation_context (void)
|
||||
|
||||
@@ -142,7 +142,7 @@ _nss_compat_setgrent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endgrent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -163,6 +163,15 @@ internal_endgrent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endgrent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endgrent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endgrent (void)
|
||||
{
|
||||
@@ -483,7 +492,7 @@ _nss_compat_getgrnam_r (const char *name, struct group *grp,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endgrent (&ent);
|
||||
internal_endgrent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -612,7 +621,7 @@ _nss_compat_getgrgid_r (gid_t gid, struct group *grp,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endgrent (&ent);
|
||||
internal_endgrent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ internal_setgrent (ent_t *ent)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endgrent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -157,6 +157,15 @@ internal_endgrent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endgrent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endgrent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endgrent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
/* Add new group record. */
|
||||
static void
|
||||
add_group (long int *start, long int *size, gid_t **groupsp, long int limit,
|
||||
@@ -501,7 +510,7 @@ _nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
|
||||
done:
|
||||
scratch_buffer_free (&tmpbuf);
|
||||
|
||||
internal_endgrent (&intern);
|
||||
internal_endgrent_noerror (&intern);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ _nss_compat_setpwent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endpwent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -287,6 +287,15 @@ internal_endpwent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endpwent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endpwent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endpwent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endpwent (void)
|
||||
{
|
||||
@@ -822,7 +831,7 @@ _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endpwent (&ent);
|
||||
internal_endpwent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1061,7 +1070,7 @@ _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endpwent (&ent);
|
||||
internal_endpwent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ _nss_compat_setspent (int stayopen)
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
static enum nss_status __attribute_warn_unused_result__
|
||||
internal_endspent (ent_t *ent)
|
||||
{
|
||||
if (ent->stream != NULL)
|
||||
@@ -244,6 +244,15 @@ internal_endspent (ent_t *ent)
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Like internal_endspent, but preserve errno in all cases. */
|
||||
static void
|
||||
internal_endspent_noerror (ent_t *ent)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
enum nss_status unused __attribute__ ((unused)) = internal_endspent (ent);
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
|
||||
enum nss_status
|
||||
_nss_compat_endspent (void)
|
||||
{
|
||||
@@ -261,7 +270,6 @@ _nss_compat_endspent (void)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static enum nss_status
|
||||
getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent,
|
||||
char *group, char *buffer, size_t buflen,
|
||||
@@ -786,7 +794,7 @@ _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
|
||||
if (result == NSS_STATUS_SUCCESS)
|
||||
result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
|
||||
|
||||
internal_endspent (&ent);
|
||||
internal_endspent_noerror (&ent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-4
@@ -101,7 +101,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \
|
||||
tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \
|
||||
tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \
|
||||
bug-regex38 tst-regcomp-truncated tst-spawn-chdir \
|
||||
tst-wordexp-nocmd
|
||||
tst-wordexp-nocmd tst-wordexp-reuse
|
||||
|
||||
tests-internal := bug-regex5 bug-regex20 bug-regex33 \
|
||||
tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \
|
||||
tst-glob_lstat_compat tst-spawn4-compat
|
||||
@@ -134,11 +135,13 @@ generated += $(addprefix wordexp-test-result, 1 2 3 4 5 6 7 8 9 10) \
|
||||
bug-glob2.mtrace bug-glob2-mem.out tst-vfork3-mem.out \
|
||||
tst-vfork3.mtrace getconf.speclist tst-fnmatch-mem.out \
|
||||
tst-fnmatch.mtrace bug-regex36.mtrace \
|
||||
testcases.h ptestcases.h
|
||||
testcases.h ptestcases.h tst-wordexp-reuse-mem.out \
|
||||
tst-wordexp-reuse.mtrace
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
ifeq (yes,$(build-shared))
|
||||
tests-special += $(objpfx)globtest.out $(objpfx)wordexp-tst.out
|
||||
tests-special += $(objpfx)globtest.out $(objpfx)wordexp-tst.out \
|
||||
$(objpfx)wordexp-tst.out
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -152,7 +155,7 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \
|
||||
$(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \
|
||||
$(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \
|
||||
$(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \
|
||||
$(objpfx)tst-glob-tilde-mem.out
|
||||
$(objpfx)tst-glob-tilde-mem.out $(objpfx)tst-wordexp-reuse.out
|
||||
xtests-special += $(objpfx)bug-ga2-mem.out
|
||||
endif
|
||||
|
||||
@@ -397,3 +400,9 @@ $(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
|
||||
|
||||
$(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+13
-12
@@ -827,31 +827,32 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
|
||||
{
|
||||
size_t home_len = strlen (p->pw_dir);
|
||||
size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
|
||||
char *d;
|
||||
char *d, *newp;
|
||||
bool use_alloca = glob_use_alloca (alloca_used,
|
||||
home_len + rest_len + 1);
|
||||
|
||||
if (__glibc_unlikely (malloc_dirname))
|
||||
free (dirname);
|
||||
malloc_dirname = 0;
|
||||
|
||||
if (glob_use_alloca (alloca_used, home_len + rest_len + 1))
|
||||
dirname = alloca_account (home_len + rest_len + 1,
|
||||
alloca_used);
|
||||
if (use_alloca)
|
||||
newp = alloca_account (home_len + rest_len + 1, alloca_used);
|
||||
else
|
||||
{
|
||||
dirname = malloc (home_len + rest_len + 1);
|
||||
if (dirname == NULL)
|
||||
newp = malloc (home_len + rest_len + 1);
|
||||
if (newp == NULL)
|
||||
{
|
||||
scratch_buffer_free (&pwtmpbuf);
|
||||
retval = GLOB_NOSPACE;
|
||||
goto out;
|
||||
}
|
||||
malloc_dirname = 1;
|
||||
}
|
||||
d = mempcpy (dirname, p->pw_dir, home_len);
|
||||
d = mempcpy (newp, p->pw_dir, home_len);
|
||||
if (end_name != NULL)
|
||||
d = mempcpy (d, end_name, rest_len);
|
||||
*d = '\0';
|
||||
|
||||
if (__glibc_unlikely (malloc_dirname))
|
||||
free (dirname);
|
||||
dirname = newp;
|
||||
malloc_dirname = !use_alloca;
|
||||
|
||||
dirlen = home_len + rest_len;
|
||||
dirname_modified = 1;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -183,6 +183,7 @@ struct test_case_struct
|
||||
{ 0, NULL, "$var", 0, 0, { NULL, }, IFS },
|
||||
{ 0, NULL, "\"\\n\"", 0, 1, { "\\n", }, IFS },
|
||||
{ 0, NULL, "", 0, 0, { NULL, }, IFS },
|
||||
{ 0, NULL, "${1234567890123456789012}", 0, 0, { NULL, }, IFS },
|
||||
|
||||
/* Flags not already covered (testit() has special handling for these) */
|
||||
{ 0, NULL, "one two", WRDE_DOOFFS, 2, { "one", "two", }, IFS },
|
||||
|
||||
+3
-1
@@ -1399,7 +1399,7 @@ envsubst:
|
||||
/* Is it a numeric parameter? */
|
||||
else if (isdigit (env[0]))
|
||||
{
|
||||
int n = atoi (env);
|
||||
unsigned long n = strtoul (env, NULL, 10);
|
||||
|
||||
if (n >= __libc_argc)
|
||||
/* Substitute NULL. */
|
||||
@@ -2220,7 +2220,9 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
|
||||
{
|
||||
/* 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)
|
||||
|
||||
+5
-1
@@ -29,10 +29,14 @@ headers := sys/socket.h sys/un.h bits/sockaddr.h bits/socket.h \
|
||||
routines := accept bind connect getpeername getsockname getsockopt \
|
||||
listen recv recvfrom recvmsg send sendmsg sendto \
|
||||
setsockopt shutdown socket socketpair isfdtype opensock \
|
||||
sockatmark accept4 recvmmsg sendmmsg
|
||||
sockatmark accept4 recvmmsg sendmmsg sockaddr_un_set
|
||||
|
||||
tests := tst-accept4
|
||||
|
||||
tests-internal := \
|
||||
tst-sockaddr_un_set \
|
||||
# tests-internal
|
||||
|
||||
aux := sa_len
|
||||
|
||||
include ../Rules
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Set the sun_path member of struct sockaddr_un.
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
int
|
||||
__sockaddr_un_set (struct sockaddr_un *addr, const char *pathname)
|
||||
{
|
||||
size_t name_length = strlen (pathname);
|
||||
|
||||
/* The kernel supports names of exactly sizeof (addr->sun_path)
|
||||
bytes, without a null terminator, but userspace does not; see the
|
||||
SUN_LEN macro. */
|
||||
if (name_length >= sizeof (addr->sun_path))
|
||||
{
|
||||
__set_errno (EINVAL); /* Error code used by the kernel. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr->sun_family = AF_UNIX;
|
||||
memcpy (addr->sun_path, pathname, name_length + 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* Test the __sockaddr_un_set function.
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Re-compile the function because the version in libc is not
|
||||
exported. */
|
||||
#include "sockaddr_un_set.c"
|
||||
|
||||
#include <support/check.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
|
||||
memset (&sun, 0xcc, sizeof (sun));
|
||||
__sockaddr_un_set (&sun, "");
|
||||
TEST_COMPARE (sun.sun_family, AF_UNIX);
|
||||
TEST_COMPARE (__sockaddr_un_set (&sun, ""), 0);
|
||||
|
||||
memset (&sun, 0xcc, sizeof (sun));
|
||||
TEST_COMPARE (__sockaddr_un_set (&sun, "/example"), 0);
|
||||
TEST_COMPARE_STRING (sun.sun_path, "/example");
|
||||
|
||||
{
|
||||
char pathname[108]; /* Length of sun_path (ABI constant). */
|
||||
memset (pathname, 'x', sizeof (pathname));
|
||||
pathname[sizeof (pathname) - 1] = '\0';
|
||||
memset (&sun, 0xcc, sizeof (sun));
|
||||
TEST_COMPARE (__sockaddr_un_set (&sun, pathname), 0);
|
||||
TEST_COMPARE (sun.sun_family, AF_UNIX);
|
||||
TEST_COMPARE_STRING (sun.sun_path, pathname);
|
||||
}
|
||||
|
||||
{
|
||||
char pathname[109];
|
||||
memset (pathname, 'x', sizeof (pathname));
|
||||
pathname[sizeof (pathname) - 1] = '\0';
|
||||
memset (&sun, 0xcc, sizeof (sun));
|
||||
errno = 0;
|
||||
TEST_COMPARE (__sockaddr_un_set (&sun, pathname), -1);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+11
-2
@@ -66,6 +66,10 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
|
||||
tst-scanf-round \
|
||||
tst-renameat2 tst-bz11319 tst-bz11319-fortify2 \
|
||||
scanf14a scanf16a \
|
||||
tst-printf-bz25691 \
|
||||
tst-vfprintf-width-prec-alloc \
|
||||
tst-grouping2 \
|
||||
# tests
|
||||
|
||||
|
||||
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
|
||||
@@ -75,10 +79,12 @@ tests-special += $(objpfx)tst-unbputc.out $(objpfx)tst-printf.out \
|
||||
$(objpfx)tst-printf-bz18872-mem.out \
|
||||
$(objpfx)tst-setvbuf1-cmp.out \
|
||||
$(objpfx)tst-vfprintf-width-prec-mem.out \
|
||||
$(objpfx)tst-printfsz-islongdouble.out
|
||||
$(objpfx)tst-printfsz-islongdouble.out \
|
||||
$(objpfx)tst-printf-bz25691-mem.out
|
||||
generated += tst-printf-bz18872.c tst-printf-bz18872.mtrace \
|
||||
tst-printf-bz18872-mem.out \
|
||||
tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out
|
||||
tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out \
|
||||
tst-printf-bz25691.mtrace tst-printf-bz25691-mem.out
|
||||
endif
|
||||
|
||||
include ../Rules
|
||||
@@ -91,6 +97,7 @@ $(objpfx)bug14.out: $(gen-locales)
|
||||
$(objpfx)scanf13.out: $(gen-locales)
|
||||
$(objpfx)test-vfprintf.out: $(gen-locales)
|
||||
$(objpfx)tst-grouping.out: $(gen-locales)
|
||||
$(objpfx)tst-grouping2.out: $(gen-locales)
|
||||
$(objpfx)tst-sprintf.out: $(gen-locales)
|
||||
$(objpfx)tst-sscanf.out: $(gen-locales)
|
||||
$(objpfx)tst-swprintf.out: $(gen-locales)
|
||||
@@ -100,6 +107,8 @@ endif
|
||||
tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace
|
||||
tst-vfprintf-width-prec-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace
|
||||
tst-printf-bz25691-ENV = \
|
||||
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace
|
||||
|
||||
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
|
||||
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' > $@; \
|
||||
|
||||
@@ -57,7 +57,7 @@ do_test (void)
|
||||
|
||||
ret = fprintf (fp, "%." SN3 "d", 1);
|
||||
printf ("ret = %d\n", ret);
|
||||
if (ret != -1 || errno != EOVERFLOW)
|
||||
if (ret != N3)
|
||||
return 1;
|
||||
|
||||
/* GCC 9 warns about output of more than INT_MAX characters; this is
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Test printf with grouping and large width (bug 29530)
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
const int field_width = 1000;
|
||||
char buf[field_width + 1];
|
||||
|
||||
xsetlocale (LC_NUMERIC, "de_DE.UTF-8");
|
||||
|
||||
/* This used to crash in group_number. */
|
||||
TEST_COMPARE (sprintf (buf, "%'*d", field_width, 1000), field_width);
|
||||
TEST_COMPARE_STRING (buf + field_width - 6, " 1.000");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Test for memory leak with large width (BZ#25691).
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdint.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <mcheck.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
mtrace ();
|
||||
|
||||
/* For 's' conversion specifier with 'l' modifier the array must be
|
||||
converted to multibyte characters up to the precision specific
|
||||
value. */
|
||||
{
|
||||
/* The input size value is to force a heap allocation on temporary
|
||||
buffer (in the old implementation). */
|
||||
const size_t winputsize = 64 * 1024 + 1;
|
||||
wchar_t *winput = xmalloc (winputsize * sizeof (wchar_t));
|
||||
wmemset (winput, L'a', winputsize - 1);
|
||||
winput[winputsize - 1] = L'\0';
|
||||
|
||||
char result[9];
|
||||
const char expected[] = "aaaaaaaa";
|
||||
int ret;
|
||||
|
||||
ret = snprintf (result, sizeof (result), "%.65537ls", winput);
|
||||
TEST_COMPARE (ret, winputsize - 1);
|
||||
TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
|
||||
|
||||
ret = snprintf (result, sizeof (result), "%ls", winput);
|
||||
TEST_COMPARE (ret, winputsize - 1);
|
||||
TEST_COMPARE_BLOB (result, sizeof (result), expected, sizeof (expected));
|
||||
|
||||
free (winput);
|
||||
}
|
||||
|
||||
/* For 's' converstion specifier the array is interpreted as a multibyte
|
||||
character sequence and converted to wide characters up to the precision
|
||||
specific value. */
|
||||
{
|
||||
/* The input size value is to force a heap allocation on temporary
|
||||
buffer (in the old implementation). */
|
||||
const size_t mbssize = 32 * 1024;
|
||||
char *mbs = xmalloc (mbssize);
|
||||
memset (mbs, 'a', mbssize - 1);
|
||||
mbs[mbssize - 1] = '\0';
|
||||
|
||||
const size_t expectedsize = 32 * 1024;
|
||||
wchar_t *expected = xmalloc (expectedsize * sizeof (wchar_t));
|
||||
wmemset (expected, L'a', expectedsize - 1);
|
||||
expected[expectedsize-1] = L'\0';
|
||||
|
||||
const size_t resultsize = mbssize * sizeof (wchar_t);
|
||||
wchar_t *result = xmalloc (resultsize);
|
||||
int ret;
|
||||
|
||||
ret = swprintf (result, resultsize, L"%.65537s", mbs);
|
||||
TEST_COMPARE (ret, mbssize - 1);
|
||||
TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
|
||||
expected, expectedsize * sizeof (wchar_t));
|
||||
|
||||
ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
|
||||
TEST_COMPARE (ret, mbssize - 1);
|
||||
TEST_COMPARE_BLOB (result, (ret + 1) * sizeof (wchar_t),
|
||||
expected, expectedsize * sizeof (wchar_t));
|
||||
|
||||
/* Same test, but with an invalid multibyte sequence. */
|
||||
mbs[mbssize - 2] = 0xff;
|
||||
|
||||
ret = swprintf (result, resultsize, L"%.65537s", mbs);
|
||||
TEST_COMPARE (ret, -1);
|
||||
|
||||
ret = swprintf (result, resultsize, L"%1$.65537s", mbs);
|
||||
TEST_COMPARE (ret, -1);
|
||||
|
||||
free (mbs);
|
||||
free (result);
|
||||
free (expected);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Test large width or precision does not involve large allocation.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/resource.h>
|
||||
#include <support/check.h>
|
||||
|
||||
char test_string[] = "test";
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct rlimit limit;
|
||||
TEST_VERIFY_EXIT (getrlimit (RLIMIT_AS, &limit) == 0);
|
||||
limit.rlim_cur = 200 * 1024 * 1024;
|
||||
TEST_VERIFY_EXIT (setrlimit (RLIMIT_AS, &limit) == 0);
|
||||
FILE *fp = fopen ("/dev/null", "w");
|
||||
TEST_VERIFY_EXIT (fp != NULL);
|
||||
TEST_COMPARE (fprintf (fp, "%1000000000d", 1), 1000000000);
|
||||
TEST_COMPARE (fprintf (fp, "%.1000000000s", test_string), 4);
|
||||
TEST_COMPARE (fprintf (fp, "%1000000000d %1000000000d", 1, 2), 2000000001);
|
||||
TEST_COMPARE (fprintf (fp, "%2$.*1$s", 0x7fffffff, test_string), 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
+183
-264
@@ -31,6 +31,7 @@
|
||||
#include <locale/localeinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <scratch_buffer.h>
|
||||
#include <intprops.h>
|
||||
|
||||
/* This code is shared between the standard stdio implementation found
|
||||
in GNU C library and the libio implementation originally found in
|
||||
@@ -45,10 +46,6 @@
|
||||
#include <wctype.h>
|
||||
#endif
|
||||
|
||||
/* In some cases we need extra space for all the output which is not
|
||||
counted in the width of the string. We assume 32 characters is
|
||||
enough. */
|
||||
#define EXTSIZ 32
|
||||
#define ARGCHECK(S, Format) \
|
||||
do \
|
||||
{ \
|
||||
@@ -119,22 +116,38 @@
|
||||
while (0)
|
||||
#endif
|
||||
|
||||
#define done_add(val) \
|
||||
do { \
|
||||
unsigned int _val = val; \
|
||||
assert ((unsigned int) done < (unsigned int) INT_MAX); \
|
||||
if (__glibc_unlikely (INT_MAX - done < _val)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
__set_errno (EOVERFLOW); \
|
||||
goto all_done; \
|
||||
} \
|
||||
done += _val; \
|
||||
} while (0)
|
||||
/* Add LENGTH to DONE. Return the new value of DONE, or -1 on
|
||||
overflow (and set errno accordingly). */
|
||||
static inline int
|
||||
done_add_func (size_t length, int done)
|
||||
{
|
||||
if (done < 0)
|
||||
return done;
|
||||
int ret;
|
||||
if (INT_ADD_WRAPV (done, length, &ret))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define done_add(val) \
|
||||
do \
|
||||
{ \
|
||||
/* Ensure that VAL has a type similar to int. */ \
|
||||
_Static_assert (sizeof (val) == sizeof (int), "value int size"); \
|
||||
_Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \
|
||||
done = done_add_func ((val), done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#ifndef COMPILE_WPRINTF
|
||||
# define vfprintf __vfprintf_internal
|
||||
# define CHAR_T char
|
||||
# define OTHER_CHAR_T wchar_t
|
||||
# define UCHAR_T unsigned char
|
||||
# define INT_T int
|
||||
typedef const char *THOUSANDS_SEP_T;
|
||||
@@ -143,25 +156,14 @@ typedef const char *THOUSANDS_SEP_T;
|
||||
# define STR_LEN(Str) strlen (Str)
|
||||
|
||||
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
|
||||
# define PAD(Padchar) \
|
||||
do { \
|
||||
if (width > 0) \
|
||||
{ \
|
||||
ssize_t written = _IO_padn (s, (Padchar), width); \
|
||||
if (__glibc_unlikely (written != width)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done_add (written); \
|
||||
} \
|
||||
} while (0)
|
||||
# define PUTC(C, F) _IO_putc_unlocked (C, F)
|
||||
# define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
|
||||
return -1
|
||||
# define CONVERT_FROM_OTHER_STRING __wcsrtombs
|
||||
#else
|
||||
# define vfprintf __vfwprintf_internal
|
||||
# define CHAR_T wchar_t
|
||||
# define OTHER_CHAR_T char
|
||||
/* This is a hack!!! There should be a type uwchar_t. */
|
||||
# define UCHAR_T unsigned int /* uwchar_t */
|
||||
# define INT_T wint_t
|
||||
@@ -173,21 +175,9 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
# include <_itowa.h>
|
||||
|
||||
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
|
||||
# define PAD(Padchar) \
|
||||
do { \
|
||||
if (width > 0) \
|
||||
{ \
|
||||
ssize_t written = _IO_wpadn (s, (Padchar), width); \
|
||||
if (__glibc_unlikely (written != width)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done_add (written); \
|
||||
} \
|
||||
} while (0)
|
||||
# define PUTC(C, F) _IO_putwc_unlocked (C, F)
|
||||
# define ORIENT if (_IO_fwide (s, 1) != 1) return -1
|
||||
# define CONVERT_FROM_OTHER_STRING __mbsrtowcs
|
||||
|
||||
# undef _itoa
|
||||
# define _itoa(Val, Buf, Base, Case) _itowa (Val, Buf, Base, Case)
|
||||
@@ -196,6 +186,33 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
# define EOF WEOF
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
pad_func (FILE *s, CHAR_T padchar, int width, int done)
|
||||
{
|
||||
if (width > 0)
|
||||
{
|
||||
ssize_t written;
|
||||
#ifndef COMPILE_WPRINTF
|
||||
written = _IO_padn (s, padchar, width);
|
||||
#else
|
||||
written = _IO_wpadn (s, padchar, width);
|
||||
#endif
|
||||
if (__glibc_unlikely (written != width))
|
||||
return -1;
|
||||
return done_add_func (width, done);
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
#define PAD(Padchar) \
|
||||
do \
|
||||
{ \
|
||||
done = pad_func (s, (Padchar), width, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#include "_i18n_number.h"
|
||||
|
||||
/* Include the shared code for parsing the format string. */
|
||||
@@ -215,24 +232,115 @@ typedef wchar_t THOUSANDS_SEP_T;
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define outstring(String, Len) \
|
||||
do \
|
||||
{ \
|
||||
assert ((size_t) done <= (size_t) INT_MAX); \
|
||||
if ((size_t) PUT (s, (String), (Len)) != (size_t) (Len)) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
if (__glibc_unlikely (INT_MAX - done < (Len))) \
|
||||
{ \
|
||||
done = -1; \
|
||||
__set_errno (EOVERFLOW); \
|
||||
goto all_done; \
|
||||
} \
|
||||
done += (Len); \
|
||||
} \
|
||||
while (0)
|
||||
static inline int
|
||||
outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
|
||||
{
|
||||
assert ((size_t) done <= (size_t) INT_MAX);
|
||||
if ((size_t) PUT (s, string, length) != (size_t) (length))
|
||||
return -1;
|
||||
return done_add_func (length, done);
|
||||
}
|
||||
|
||||
#define outstring(String, Len) \
|
||||
do \
|
||||
{ \
|
||||
const void *string_ = (String); \
|
||||
done = outstring_func (s, string_, (Len), done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* Write the string SRC to S. If PREC is non-negative, write at most
|
||||
PREC bytes. If LEFT is true, perform left justification. */
|
||||
static int
|
||||
outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
|
||||
int width, bool left, int done)
|
||||
{
|
||||
/* Use a small buffer to combine processing of multiple characters.
|
||||
CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
|
||||
characters, and buf_length counts that. */
|
||||
enum { buf_length = 256 / sizeof (CHAR_T) };
|
||||
CHAR_T buf[buf_length];
|
||||
_Static_assert (sizeof (buf) > MB_LEN_MAX,
|
||||
"buffer is large enough for a single multi-byte character");
|
||||
|
||||
/* Add the initial padding if needed. */
|
||||
if (width > 0 && !left)
|
||||
{
|
||||
/* Make a first pass to find the output width, so that we can
|
||||
add the required padding. */
|
||||
mbstate_t mbstate = { 0 };
|
||||
const OTHER_CHAR_T *src_copy = src;
|
||||
size_t total_written;
|
||||
if (prec < 0)
|
||||
total_written = CONVERT_FROM_OTHER_STRING
|
||||
(NULL, &src_copy, 0, &mbstate);
|
||||
else
|
||||
{
|
||||
/* The source might not be null-terminated. Enforce the
|
||||
limit manually, based on the output length. */
|
||||
total_written = 0;
|
||||
size_t limit = prec;
|
||||
while (limit > 0 && src_copy != NULL)
|
||||
{
|
||||
size_t write_limit = buf_length;
|
||||
if (write_limit > limit)
|
||||
write_limit = limit;
|
||||
size_t written = CONVERT_FROM_OTHER_STRING
|
||||
(buf, &src_copy, write_limit, &mbstate);
|
||||
if (written == (size_t) -1)
|
||||
return -1;
|
||||
if (written == 0)
|
||||
break;
|
||||
total_written += written;
|
||||
limit -= written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Output initial padding. */
|
||||
if (total_written < width)
|
||||
{
|
||||
done = pad_func (s, L_(' '), width - total_written, done);
|
||||
if (done < 0)
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert the input string, piece by piece. */
|
||||
size_t total_written = 0;
|
||||
{
|
||||
mbstate_t mbstate = { 0 };
|
||||
/* If prec is negative, remaining is not decremented, otherwise,
|
||||
it serves as the write limit. */
|
||||
size_t remaining = -1;
|
||||
if (prec >= 0)
|
||||
remaining = prec;
|
||||
while (remaining > 0 && src != NULL)
|
||||
{
|
||||
size_t write_limit = buf_length;
|
||||
if (remaining < write_limit)
|
||||
write_limit = remaining;
|
||||
size_t written = CONVERT_FROM_OTHER_STRING
|
||||
(buf, &src, write_limit, &mbstate);
|
||||
if (written == (size_t) -1)
|
||||
return -1;
|
||||
if (written == 0)
|
||||
break;
|
||||
done = outstring_func (s, (const UCHAR_T *) buf, written, done);
|
||||
if (done < 0)
|
||||
return done;
|
||||
total_written += written;
|
||||
if (prec >= 0)
|
||||
remaining -= written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add final padding. */
|
||||
if (width > 0 && left && total_written < width)
|
||||
return pad_func (s, L_(' '), width - total_written, done);
|
||||
return done;
|
||||
}
|
||||
|
||||
/* For handling long_double and longlong we use the same flag. If
|
||||
`long' and `long long' are effectively the same type define it to
|
||||
@@ -1022,7 +1130,6 @@ static const uint8_t jump_table[] =
|
||||
LABEL (form_string): \
|
||||
{ \
|
||||
size_t len; \
|
||||
int string_malloced; \
|
||||
\
|
||||
/* The string argument could in fact be `char *' or `wchar_t *'. \
|
||||
But this should not make a difference here. */ \
|
||||
@@ -1034,7 +1141,6 @@ static const uint8_t jump_table[] =
|
||||
/* Entry point for printing other strings. */ \
|
||||
LABEL (print_string): \
|
||||
\
|
||||
string_malloced = 0; \
|
||||
if (string == NULL) \
|
||||
{ \
|
||||
/* Write "(null)" if there's space. */ \
|
||||
@@ -1051,41 +1157,12 @@ static const uint8_t jump_table[] =
|
||||
} \
|
||||
else if (!is_long && spec != L_('S')) \
|
||||
{ \
|
||||
/* This is complicated. We have to transform the multibyte \
|
||||
string into a wide character string. */ \
|
||||
const char *mbs = (const char *) string; \
|
||||
mbstate_t mbstate; \
|
||||
\
|
||||
len = prec != -1 ? __strnlen (mbs, (size_t) prec) : strlen (mbs); \
|
||||
\
|
||||
/* Allocate dynamically an array which definitely is long \
|
||||
enough for the wide character version. Each byte in the \
|
||||
multi-byte string can produce at most one wide character. */ \
|
||||
if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t))) \
|
||||
{ \
|
||||
__set_errno (EOVERFLOW); \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else if (__libc_use_alloca (len * sizeof (wchar_t))) \
|
||||
string = (CHAR_T *) alloca (len * sizeof (wchar_t)); \
|
||||
else if ((string = (CHAR_T *) malloc (len * sizeof (wchar_t))) \
|
||||
== NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
\
|
||||
memset (&mbstate, '\0', sizeof (mbstate_t)); \
|
||||
len = __mbsrtowcs (string, &mbs, len, &mbstate); \
|
||||
if (len == (size_t) -1) \
|
||||
{ \
|
||||
/* Illegal multibyte character. */ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done = outstring_converted_wide_string \
|
||||
(s, (const char *) string, prec, width, left, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
/* The padding has already been written. */ \
|
||||
break; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -1108,8 +1185,6 @@ static const uint8_t jump_table[] =
|
||||
outstring (string, len); \
|
||||
if (left) \
|
||||
PAD (L' '); \
|
||||
if (__glibc_unlikely (string_malloced)) \
|
||||
free (string); \
|
||||
} \
|
||||
break;
|
||||
#else
|
||||
@@ -1158,7 +1233,6 @@ static const uint8_t jump_table[] =
|
||||
LABEL (form_string): \
|
||||
{ \
|
||||
size_t len; \
|
||||
int string_malloced; \
|
||||
\
|
||||
/* The string argument could in fact be `char *' or `wchar_t *'. \
|
||||
But this should not make a difference here. */ \
|
||||
@@ -1170,7 +1244,6 @@ static const uint8_t jump_table[] =
|
||||
/* Entry point for printing other strings. */ \
|
||||
LABEL (print_string): \
|
||||
\
|
||||
string_malloced = 0; \
|
||||
if (string == NULL) \
|
||||
{ \
|
||||
/* Write "(null)" if there's space. */ \
|
||||
@@ -1196,51 +1269,12 @@ static const uint8_t jump_table[] =
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
const wchar_t *s2 = (const wchar_t *) string; \
|
||||
mbstate_t mbstate; \
|
||||
\
|
||||
memset (&mbstate, '\0', sizeof (mbstate_t)); \
|
||||
\
|
||||
if (prec >= 0) \
|
||||
{ \
|
||||
/* The string `s2' might not be NUL terminated. */ \
|
||||
if (__libc_use_alloca (prec)) \
|
||||
string = (char *) alloca (prec); \
|
||||
else if ((string = (char *) malloc (prec)) == NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
len = __wcsrtombs (string, &s2, prec, &mbstate); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
len = __wcsrtombs (NULL, &s2, 0, &mbstate); \
|
||||
if (len != (size_t) -1) \
|
||||
{ \
|
||||
assert (__mbsinit (&mbstate)); \
|
||||
s2 = (const wchar_t *) string; \
|
||||
if (__libc_use_alloca (len + 1)) \
|
||||
string = (char *) alloca (len + 1); \
|
||||
else if ((string = (char *) malloc (len + 1)) == NULL) \
|
||||
{ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
else \
|
||||
string_malloced = 1; \
|
||||
(void) __wcsrtombs (string, &s2, len + 1, &mbstate); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
if (len == (size_t) -1) \
|
||||
{ \
|
||||
/* Illegal wide-character string. */ \
|
||||
done = -1; \
|
||||
goto all_done; \
|
||||
} \
|
||||
done = outstring_converted_wide_string \
|
||||
(s, (const wchar_t *) string, prec, width, left, done); \
|
||||
if (done < 0) \
|
||||
goto all_done; \
|
||||
/* The padding has already been written. */ \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if ((width -= len) < 0) \
|
||||
@@ -1254,8 +1288,6 @@ static const uint8_t jump_table[] =
|
||||
outstring (string, len); \
|
||||
if (left) \
|
||||
PAD (' '); \
|
||||
if (__glibc_unlikely (string_malloced)) \
|
||||
free (string); \
|
||||
} \
|
||||
break;
|
||||
#endif
|
||||
@@ -1307,7 +1339,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
|
||||
/* Buffer intermediate results. */
|
||||
CHAR_T work_buffer[WORK_BUFFER_SIZE];
|
||||
CHAR_T *workstart = NULL;
|
||||
CHAR_T *workend;
|
||||
|
||||
/* We have to save the original argument pointer. */
|
||||
@@ -1416,7 +1447,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
UCHAR_T pad = L_(' ');/* Padding character. */
|
||||
CHAR_T spec;
|
||||
|
||||
workstart = NULL;
|
||||
workend = work_buffer + WORK_BUFFER_SIZE;
|
||||
|
||||
/* Get current character in format string. */
|
||||
@@ -1508,31 +1538,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
pad = L_(' ');
|
||||
left = 1;
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
if (width >= WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* We have to use a special buffer. */
|
||||
size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + width + EXTSIZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
JUMP (*f, step1_jumps);
|
||||
|
||||
@@ -1540,31 +1545,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
LABEL (width):
|
||||
width = read_int (&f);
|
||||
|
||||
if (__glibc_unlikely (width == -1
|
||||
|| width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
if (__glibc_unlikely (width == -1))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
|
||||
if (width >= WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* We have to use a special buffer. */
|
||||
size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + width + EXTSIZ;
|
||||
}
|
||||
}
|
||||
if (*f == L_('$'))
|
||||
/* Oh, oh. The argument comes from a positional parameter. */
|
||||
goto do_positional;
|
||||
@@ -1613,34 +1600,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
}
|
||||
else
|
||||
prec = 0;
|
||||
if (prec > width && prec > WORK_BUFFER_SIZE - EXTSIZ)
|
||||
{
|
||||
/* Deallocate any previously allocated buffer because it is
|
||||
too small. */
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
|
||||
{
|
||||
__set_errno (EOVERFLOW);
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
size_t needed = ((size_t) prec + EXTSIZ) * sizeof (CHAR_T);
|
||||
|
||||
if (__libc_use_alloca (needed))
|
||||
workend = (CHAR_T *) alloca (needed) + prec + EXTSIZ;
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc (needed);
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + prec + EXTSIZ;
|
||||
}
|
||||
}
|
||||
JUMP (*f, step2_jumps);
|
||||
|
||||
/* Process 'h' modifier. There might another 'h' following. */
|
||||
@@ -1704,10 +1663,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
/* The format is correctly handled. */
|
||||
++nspecs_done;
|
||||
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
|
||||
/* Look for next format specifier. */
|
||||
#ifdef COMPILE_WPRINTF
|
||||
f = __find_specwc ((end_of_spec = ++f));
|
||||
@@ -1725,18 +1680,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
|
||||
|
||||
/* Hand off processing for positional parameters. */
|
||||
do_positional:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
{
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
}
|
||||
done = printf_positional (s, format, readonly_format, ap, &ap_save,
|
||||
done, nspecs_done, lead_str_end, work_buffer,
|
||||
save_errno, grouping, thousands_sep, mode_flags);
|
||||
|
||||
all_done:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
/* Unlock the stream. */
|
||||
_IO_funlockfile (s);
|
||||
_IO_cleanup_region_end (0);
|
||||
@@ -1780,8 +1728,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
/* Just a counter. */
|
||||
size_t cnt;
|
||||
|
||||
CHAR_T *workstart = NULL;
|
||||
|
||||
if (grouping == (const char *) -1)
|
||||
{
|
||||
#ifdef COMPILE_WPRINTF
|
||||
@@ -1974,7 +1920,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
char pad = specs[nspecs_done].info.pad;
|
||||
CHAR_T spec = specs[nspecs_done].info.spec;
|
||||
|
||||
workstart = NULL;
|
||||
CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
|
||||
|
||||
/* Fill in last information. */
|
||||
@@ -2008,27 +1953,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
prec = specs[nspecs_done].info.prec;
|
||||
}
|
||||
|
||||
/* Maybe the buffer is too small. */
|
||||
if (MAX (prec, width) + EXTSIZ > WORK_BUFFER_SIZE)
|
||||
{
|
||||
if (__libc_use_alloca ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T)))
|
||||
workend = ((CHAR_T *) alloca ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T))
|
||||
+ (MAX (prec, width) + EXTSIZ));
|
||||
else
|
||||
{
|
||||
workstart = (CHAR_T *) malloc ((MAX (prec, width) + EXTSIZ)
|
||||
* sizeof (CHAR_T));
|
||||
if (workstart == NULL)
|
||||
{
|
||||
done = -1;
|
||||
goto all_done;
|
||||
}
|
||||
workend = workstart + (MAX (prec, width) + EXTSIZ);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process format specifiers. */
|
||||
while (1)
|
||||
{
|
||||
@@ -2102,18 +2026,12 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
|
||||
break;
|
||||
}
|
||||
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
workstart = NULL;
|
||||
|
||||
/* Write the following constant string. */
|
||||
outstring (specs[nspecs_done].end_of_fmt,
|
||||
specs[nspecs_done].next_fmt
|
||||
- specs[nspecs_done].end_of_fmt);
|
||||
}
|
||||
all_done:
|
||||
if (__glibc_unlikely (workstart != NULL))
|
||||
free (workstart);
|
||||
scratch_buffer_free (&argsbuf);
|
||||
scratch_buffer_free (&specsbuf);
|
||||
return done;
|
||||
@@ -2236,7 +2154,8 @@ group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
|
||||
copy_rest:
|
||||
/* No further grouping to be done. Copy the rest of the
|
||||
number. */
|
||||
memmove (w, s, (front_ptr -s) * sizeof (CHAR_T));
|
||||
w -= s - front_ptr;
|
||||
memmove (w, front_ptr, (s - front_ptr) * sizeof (CHAR_T));
|
||||
break;
|
||||
}
|
||||
else if (*grouping != '\0')
|
||||
|
||||
+2
-1
@@ -70,7 +70,7 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
|
||||
test-canon test-canon2 tst-strtoll tst-environ \
|
||||
tst-xpg-basename tst-random tst-random2 tst-bsearch \
|
||||
tst-limits tst-rand48 bug-strtod tst-setcontext \
|
||||
tst-setcontext2 test-a64l tst-qsort tst-system testmb2 \
|
||||
tst-setcontext2 test-a64l tst-qsort testmb2 \
|
||||
bug-strtod2 tst-atof1 tst-atof2 tst-strtod2 \
|
||||
tst-rand48-2 tst-makecontext tst-strtod5 \
|
||||
tst-qsort2 tst-makecontext2 tst-strtod6 tst-unsetenv1 \
|
||||
@@ -92,6 +92,7 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \
|
||||
tests-internal := tst-strtod1i tst-strtod3 tst-strtod4 tst-strtod5i \
|
||||
tst-tls-atexit tst-tls-atexit-nodelete
|
||||
tests-static := tst-secure-getenv
|
||||
tests-container := tst-system
|
||||
|
||||
ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
tests += tst-empty-env
|
||||
|
||||
+18
-181
@@ -30,167 +30,12 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static char MAGIC_ARGUMENT[] = "run-actual-test";
|
||||
#define MAGIC_STATUS 19
|
||||
|
||||
/* Return a GID which is not our current GID, but is present in the
|
||||
supplementary group list. */
|
||||
static gid_t
|
||||
choose_gid (void)
|
||||
{
|
||||
int count = getgroups (0, NULL);
|
||||
if (count < 0)
|
||||
{
|
||||
printf ("getgroups: %m\n");
|
||||
exit (1);
|
||||
}
|
||||
gid_t *groups;
|
||||
groups = xcalloc (count, sizeof (*groups));
|
||||
int ret = getgroups (count, groups);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf ("getgroups: %m\n");
|
||||
exit (1);
|
||||
}
|
||||
gid_t current = getgid ();
|
||||
gid_t not_current = 0;
|
||||
for (int i = 0; i < ret; ++i)
|
||||
{
|
||||
if (groups[i] != current)
|
||||
{
|
||||
not_current = groups[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
free (groups);
|
||||
return not_current;
|
||||
}
|
||||
|
||||
|
||||
/* Copies the executable into a restricted directory, so that we can
|
||||
safely make it SGID with the TARGET group ID. Then runs the
|
||||
executable. */
|
||||
static int
|
||||
run_executable_sgid (gid_t target)
|
||||
{
|
||||
char *dirname = xasprintf ("%s/secure-getenv.%jd",
|
||||
test_dir, (intmax_t) getpid ());
|
||||
char *execname = xasprintf ("%s/bin", dirname);
|
||||
int infd = -1;
|
||||
int outfd = -1;
|
||||
int ret = -1;
|
||||
if (mkdir (dirname, 0700) < 0)
|
||||
{
|
||||
printf ("mkdir: %m\n");
|
||||
goto err;
|
||||
}
|
||||
infd = open ("/proc/self/exe", O_RDONLY);
|
||||
if (infd < 0)
|
||||
{
|
||||
printf ("open (/proc/self/exe): %m\n");
|
||||
goto err;
|
||||
}
|
||||
outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
|
||||
if (outfd < 0)
|
||||
{
|
||||
printf ("open (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
char buf[4096];
|
||||
for (;;)
|
||||
{
|
||||
ssize_t rdcount = read (infd, buf, sizeof (buf));
|
||||
if (rdcount < 0)
|
||||
{
|
||||
printf ("read: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (rdcount == 0)
|
||||
break;
|
||||
char *p = buf;
|
||||
char *end = buf + rdcount;
|
||||
while (p != end)
|
||||
{
|
||||
ssize_t wrcount = write (outfd, buf, end - p);
|
||||
if (wrcount == 0)
|
||||
errno = ENOSPC;
|
||||
if (wrcount <= 0)
|
||||
{
|
||||
printf ("write: %m\n");
|
||||
goto err;
|
||||
}
|
||||
p += wrcount;
|
||||
}
|
||||
}
|
||||
if (fchown (outfd, getuid (), target) < 0)
|
||||
{
|
||||
printf ("fchown (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (fchmod (outfd, 02750) < 0)
|
||||
{
|
||||
printf ("fchmod (%s): %m\n", execname);
|
||||
goto err;
|
||||
}
|
||||
if (close (outfd) < 0)
|
||||
{
|
||||
printf ("close (outfd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (close (infd) < 0)
|
||||
{
|
||||
printf ("close (infd): %m\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
int kid = fork ();
|
||||
if (kid < 0)
|
||||
{
|
||||
printf ("fork: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (kid == 0)
|
||||
{
|
||||
/* Child process. */
|
||||
char *args[] = { execname, MAGIC_ARGUMENT, NULL };
|
||||
execve (execname, args, environ);
|
||||
printf ("execve (%s): %m\n", execname);
|
||||
_exit (1);
|
||||
}
|
||||
int status;
|
||||
if (waitpid (kid, &status, 0) < 0)
|
||||
{
|
||||
printf ("waitpid: %m\n");
|
||||
goto err;
|
||||
}
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != MAGIC_STATUS)
|
||||
{
|
||||
printf ("Unexpected exit status %d from child process\n",
|
||||
status);
|
||||
goto err;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
if (outfd >= 0)
|
||||
close (outfd);
|
||||
if (infd >= 0)
|
||||
close (infd);
|
||||
if (execname)
|
||||
{
|
||||
unlink (execname);
|
||||
free (execname);
|
||||
}
|
||||
if (dirname)
|
||||
{
|
||||
rmdir (dirname);
|
||||
free (dirname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
@@ -212,15 +57,15 @@ do_test (void)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
gid_t target = choose_gid ();
|
||||
if (target == 0)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Could not find a suitable GID for user %jd, skipping test\n",
|
||||
(intmax_t) getuid ());
|
||||
exit (0);
|
||||
}
|
||||
return run_executable_sgid (target);
|
||||
int status = support_capture_subprogram_self_sgid (MAGIC_ARGUMENT);
|
||||
|
||||
if (WEXITSTATUS (status) == EXIT_UNSUPPORTED)
|
||||
return EXIT_UNSUPPORTED;
|
||||
|
||||
if (!WIFEXITED (status))
|
||||
FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -229,23 +74,15 @@ alternative_main (int argc, char **argv)
|
||||
if (argc == 2 && strcmp (argv[1], MAGIC_ARGUMENT) == 0)
|
||||
{
|
||||
if (getgid () == getegid ())
|
||||
{
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
fprintf (stderr, "SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
exit (MAGIC_STATUS);
|
||||
}
|
||||
/* This can happen if the file system is mounted nosuid. */
|
||||
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
|
||||
(intmax_t) getgid ());
|
||||
if (getenv ("PATH") == NULL)
|
||||
{
|
||||
printf ("PATH variable not present\n");
|
||||
exit (3);
|
||||
}
|
||||
FAIL_EXIT (3, "PATH variable not present\n");
|
||||
if (secure_getenv ("PATH") != NULL)
|
||||
{
|
||||
printf ("PATH variable not filtered out\n");
|
||||
exit (4);
|
||||
}
|
||||
exit (MAGIC_STATUS);
|
||||
FAIL_EXIT (4, "PATH variable not filtered out\n");
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+120
-4
@@ -17,14 +17,130 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <paths.h>
|
||||
|
||||
#include <support/capture_subprocess.h>
|
||||
#include <support/check.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/support.h>
|
||||
|
||||
static char *tmpdir;
|
||||
static long int namemax;
|
||||
|
||||
static void
|
||||
do_prepare (int argc, char *argv[])
|
||||
{
|
||||
tmpdir = support_create_temp_directory ("tst-system-");
|
||||
/* Include the last '/0'. */
|
||||
namemax = pathconf (tmpdir, _PC_NAME_MAX) + 1;
|
||||
TEST_VERIFY_EXIT (namemax != -1);
|
||||
}
|
||||
#define PREPARE do_prepare
|
||||
|
||||
struct args
|
||||
{
|
||||
const char *command;
|
||||
int exit_status;
|
||||
int term_sig;
|
||||
const char *path;
|
||||
};
|
||||
|
||||
static void
|
||||
call_system (void *closure)
|
||||
{
|
||||
struct args *args = (struct args *) closure;
|
||||
int ret;
|
||||
|
||||
if (args->path != NULL)
|
||||
TEST_COMPARE (setenv ("PATH", args->path, 1), 0);
|
||||
ret = system (args->command);
|
||||
if (args->term_sig == 0)
|
||||
{
|
||||
/* Expect regular termination. */
|
||||
TEST_VERIFY (WIFEXITED (ret) != 0);
|
||||
TEST_COMPARE (WEXITSTATUS (ret), args->exit_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* status_or_signal < 0. Expect termination by signal. */
|
||||
TEST_VERIFY (WIFSIGNALED (ret) != 0);
|
||||
TEST_COMPARE (WTERMSIG (ret), args->term_sig);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
return system (":");
|
||||
TEST_VERIFY (system (NULL) != 0);
|
||||
|
||||
{
|
||||
char cmd[namemax];
|
||||
memset (cmd, 'a', sizeof(cmd));
|
||||
cmd[sizeof(cmd) - 1] = '\0';
|
||||
|
||||
struct support_capture_subprocess result;
|
||||
result = support_capture_subprocess (call_system,
|
||||
&(struct args) {
|
||||
cmd, 127, 0, tmpdir
|
||||
});
|
||||
support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
|
||||
|
||||
char *returnerr = xasprintf ("%s: execing %s failed: "
|
||||
"No such file or directory",
|
||||
basename(_PATH_BSHELL), cmd);
|
||||
TEST_COMPARE_STRING (result.err.buffer, returnerr);
|
||||
free (returnerr);
|
||||
}
|
||||
|
||||
{
|
||||
char cmd[namemax + 1];
|
||||
memset (cmd, 'a', sizeof(cmd));
|
||||
cmd[sizeof(cmd) - 1] = '\0';
|
||||
|
||||
struct support_capture_subprocess result;
|
||||
result = support_capture_subprocess (call_system,
|
||||
&(struct args) {
|
||||
cmd, 127, 0, tmpdir
|
||||
});
|
||||
support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
|
||||
|
||||
char *returnerr = xasprintf ("%s: execing %s failed: "
|
||||
"File name too long",
|
||||
basename(_PATH_BSHELL), cmd);
|
||||
TEST_COMPARE_STRING (result.err.buffer, returnerr);
|
||||
free (returnerr);
|
||||
}
|
||||
|
||||
{
|
||||
struct support_capture_subprocess result;
|
||||
result = support_capture_subprocess (call_system,
|
||||
&(struct args) {
|
||||
"kill $$", 0, SIGTERM
|
||||
});
|
||||
support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
|
||||
}
|
||||
|
||||
{
|
||||
struct support_capture_subprocess result;
|
||||
result = support_capture_subprocess (call_system,
|
||||
&(struct args) { "echo ...", 0 });
|
||||
support_capture_subprocess_check (&result, "system", 0, sc_allow_stdout);
|
||||
TEST_COMPARE_STRING (result.out.buffer, "...\n");
|
||||
}
|
||||
|
||||
{
|
||||
struct support_capture_subprocess result;
|
||||
result = support_capture_subprocess (call_system,
|
||||
&(struct args) { "exit 1", 1 });
|
||||
support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
|
||||
}
|
||||
|
||||
TEST_COMPARE (system (""), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
||||
#include <support/test-driver.c>
|
||||
|
||||
+36
-3
@@ -65,8 +65,8 @@ do_one_test (impl_t *impl, const CHAR *s, int c, size_t n, CHAR *exp_res)
|
||||
CHAR *res = CALL (impl, s, c, n);
|
||||
if (res != exp_res)
|
||||
{
|
||||
error (0, 0, "Wrong result in function %s %p %p", impl->name,
|
||||
res, exp_res);
|
||||
error (0, 0, "Wrong result in function %s (%p, %d, %zu) -> %p != %p",
|
||||
impl->name, s, c, n, res, exp_res);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ do_test (size_t align, size_t pos, size_t len, size_t n, int seek_char)
|
||||
}
|
||||
buf[align + len] = 0;
|
||||
|
||||
if (pos < len)
|
||||
if (pos < MIN(n, len))
|
||||
{
|
||||
buf[align + pos] = seek_char;
|
||||
buf[align + len] = -seek_char;
|
||||
@@ -107,6 +107,38 @@ do_test (size_t align, size_t pos, size_t len, size_t n, int seek_char)
|
||||
do_one_test (impl, (CHAR *) (buf + align), seek_char, n, result);
|
||||
}
|
||||
|
||||
static void
|
||||
do_overflow_tests (void)
|
||||
{
|
||||
size_t i, j, len;
|
||||
const size_t one = 1;
|
||||
uintptr_t buf_addr = (uintptr_t) buf1;
|
||||
|
||||
for (i = 0; i < 750; ++i)
|
||||
{
|
||||
do_test (0, i, 751, SIZE_MAX - i, BIG_CHAR);
|
||||
do_test (0, i, 751, i - buf_addr, BIG_CHAR);
|
||||
do_test (0, i, 751, -buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, SIZE_MAX - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, SIZE_MAX - buf_addr + i, BIG_CHAR);
|
||||
|
||||
len = 0;
|
||||
for (j = 8 * sizeof(size_t) - 1; j ; --j)
|
||||
{
|
||||
len |= one << j;
|
||||
do_test (0, i, 751, len - i, BIG_CHAR);
|
||||
do_test (0, i, 751, len + i, BIG_CHAR);
|
||||
do_test (0, i, 751, len - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, len - buf_addr + i, BIG_CHAR);
|
||||
|
||||
do_test (0, i, 751, ~len - i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len + i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len - buf_addr - i, BIG_CHAR);
|
||||
do_test (0, i, 751, ~len - buf_addr + i, BIG_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_tests (void)
|
||||
{
|
||||
@@ -221,6 +253,7 @@ test_main (void)
|
||||
do_test (page_size / 2 - i, i, i, 1, 0x9B);
|
||||
|
||||
do_random_tests ();
|
||||
do_overflow_tests ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,66 @@ do_test (size_t align1, size_t align2, size_t len1, size_t len2,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_overflow_tests (void)
|
||||
{
|
||||
size_t i, j, len;
|
||||
const size_t one = 1;
|
||||
CHAR *s1, *s2;
|
||||
uintptr_t s1_addr;
|
||||
s1 = (CHAR *) buf1;
|
||||
s2 = (CHAR *) buf2;
|
||||
s1_addr = (uintptr_t)s1;
|
||||
for (j = 0; j < 200; ++j)
|
||||
s2[j] = 32 + 23 * j % (BIG_CHAR - 32);
|
||||
s2[200] = 0;
|
||||
for (i = 0; i < 750; ++i) {
|
||||
for (j = 0; j < i; ++j)
|
||||
s1[j] = 32 + 23 * j % (BIG_CHAR - 32);
|
||||
s1[i] = '\0';
|
||||
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
{
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, i - s1_addr);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, -s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, SIZE_MAX - s1_addr + i);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
for (j = 8 * sizeof(size_t) - 1; j ; --j)
|
||||
{
|
||||
len |= one << j;
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
{
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len + i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, len - s1_addr + i);
|
||||
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len + i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - s1_addr - i);
|
||||
s2[200] = '\0';
|
||||
do_one_test (impl, s2, s1, ~len - s1_addr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_tests (void)
|
||||
{
|
||||
@@ -316,6 +376,7 @@ test_main (void)
|
||||
}
|
||||
|
||||
do_random_tests ();
|
||||
do_overflow_tests ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -403,6 +403,18 @@ check2 (void)
|
||||
free (s2);
|
||||
}
|
||||
|
||||
static void
|
||||
check3 (void)
|
||||
{
|
||||
const CHAR *s1 = L ("abc");
|
||||
CHAR *s2 = STRDUP (s1);
|
||||
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
check_result (impl, s1, s2, SIZE_MAX, 0);
|
||||
|
||||
free (s2);
|
||||
}
|
||||
|
||||
int
|
||||
test_main (void)
|
||||
{
|
||||
@@ -412,6 +424,7 @@ test_main (void)
|
||||
|
||||
check1 ();
|
||||
check2 ();
|
||||
check3 ();
|
||||
|
||||
printf ("%23s", "");
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
|
||||
+85
-2
@@ -27,6 +27,7 @@
|
||||
|
||||
#ifndef WIDE
|
||||
# define STRNLEN strnlen
|
||||
# define MEMSET memset
|
||||
# define CHAR char
|
||||
# define BIG_CHAR CHAR_MAX
|
||||
# define MIDDLE_CHAR 127
|
||||
@@ -34,6 +35,7 @@
|
||||
#else
|
||||
# include <wchar.h>
|
||||
# define STRNLEN wcsnlen
|
||||
# define MEMSET wmemset
|
||||
# define CHAR wchar_t
|
||||
# define BIG_CHAR WCHAR_MAX
|
||||
# define MIDDLE_CHAR 1121
|
||||
@@ -73,7 +75,7 @@ do_test (size_t align, size_t len, size_t maxlen, int max_char)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
align &= 63;
|
||||
align &= (getpagesize () / sizeof (CHAR) - 1);
|
||||
if ((align + len) * sizeof (CHAR) >= page_size)
|
||||
return;
|
||||
|
||||
@@ -87,6 +89,56 @@ do_test (size_t align, size_t len, size_t maxlen, int max_char)
|
||||
do_one_test (impl, (CHAR *) (buf + align), maxlen, MIN (len, maxlen));
|
||||
}
|
||||
|
||||
static void
|
||||
do_overflow_tests (void)
|
||||
{
|
||||
size_t i, j, al_idx, repeats, len;
|
||||
const size_t one = 1;
|
||||
uintptr_t buf_addr = (uintptr_t) buf1;
|
||||
const size_t alignments[] = { 0, 1, 7, 9, 31, 33, 63, 65, 95, 97, 127, 129 };
|
||||
|
||||
for (al_idx = 0; al_idx < sizeof (alignments) / sizeof (alignments[0]);
|
||||
al_idx++)
|
||||
{
|
||||
for (repeats = 0; repeats < 2; ++repeats)
|
||||
{
|
||||
size_t align = repeats ? (getpagesize () - alignments[al_idx])
|
||||
: alignments[al_idx];
|
||||
align /= sizeof (CHAR);
|
||||
for (i = 0; i < 750; ++i)
|
||||
{
|
||||
do_test (align, i, SIZE_MAX, BIG_CHAR);
|
||||
|
||||
do_test (align, i, SIZE_MAX - i, BIG_CHAR);
|
||||
do_test (align, i, i - buf_addr, BIG_CHAR);
|
||||
do_test (align, i, -buf_addr - i, BIG_CHAR);
|
||||
do_test (align, i, SIZE_MAX - buf_addr - i, BIG_CHAR);
|
||||
do_test (align, i, SIZE_MAX - buf_addr + i, BIG_CHAR);
|
||||
|
||||
len = 0;
|
||||
for (j = 8 * sizeof (size_t) - 1; j; --j)
|
||||
{
|
||||
len |= one << j;
|
||||
do_test (align, i, len, BIG_CHAR);
|
||||
do_test (align, i, len - i, BIG_CHAR);
|
||||
do_test (align, i, len + i, BIG_CHAR);
|
||||
do_test (align, i, len - buf_addr - i, BIG_CHAR);
|
||||
do_test (align, i, len - buf_addr + i, BIG_CHAR);
|
||||
|
||||
do_test (align, i, ~len - i, BIG_CHAR);
|
||||
do_test (align, i, ~len + i, BIG_CHAR);
|
||||
do_test (align, i, ~len - buf_addr - i, BIG_CHAR);
|
||||
do_test (align, i, ~len - buf_addr + i, BIG_CHAR);
|
||||
|
||||
do_test (align, i, -buf_addr, BIG_CHAR);
|
||||
do_test (align, i, j - buf_addr, BIG_CHAR);
|
||||
do_test (align, i, -buf_addr - j, BIG_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_tests (void)
|
||||
{
|
||||
@@ -153,7 +205,7 @@ do_page_tests (void)
|
||||
size_t last_offset = (page_size / sizeof (CHAR)) - 1;
|
||||
|
||||
CHAR *s = (CHAR *) buf2;
|
||||
memset (s, 65, (last_offset - 1));
|
||||
MEMSET (s, 65, (last_offset - 1));
|
||||
s[last_offset] = 0;
|
||||
|
||||
/* Place short strings ending at page boundary. */
|
||||
@@ -196,6 +248,35 @@ do_page_tests (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Tests meant to unveil fail on implementations that access bytes
|
||||
beyond the maxium length. */
|
||||
|
||||
static void
|
||||
do_page_2_tests (void)
|
||||
{
|
||||
size_t i, exp_len, offset;
|
||||
size_t last_offset = page_size / sizeof (CHAR);
|
||||
|
||||
CHAR *s = (CHAR *) buf2;
|
||||
MEMSET (s, 65, last_offset);
|
||||
|
||||
/* Place short strings ending at page boundary without the null
|
||||
byte. */
|
||||
offset = last_offset;
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
/* Decrease offset to stress several sizes and alignments. */
|
||||
offset--;
|
||||
exp_len = last_offset - offset;
|
||||
FOR_EACH_IMPL (impl, 0)
|
||||
{
|
||||
/* If an implementation goes beyond EXP_LEN, it will trigger
|
||||
the segfault. */
|
||||
do_one_test (impl, (CHAR *) (s + offset), exp_len, exp_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
test_main (void)
|
||||
{
|
||||
@@ -242,6 +323,8 @@ test_main (void)
|
||||
|
||||
do_random_tests ();
|
||||
do_page_tests ();
|
||||
do_page_2_tests ();
|
||||
do_overflow_tests ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -95,7 +95,8 @@ others += rpcgen
|
||||
endif
|
||||
|
||||
tests = tst-xdrmem tst-xdrmem2 test-rpcent tst-udp-error tst-udp-timeout \
|
||||
tst-udp-nonblocking
|
||||
tst-udp-nonblocking tst-bug22542 tst-bug28768
|
||||
|
||||
xtests := tst-getmyaddr
|
||||
|
||||
ifeq ($(have-thread-library),yes)
|
||||
@@ -246,3 +247,5 @@ $(objpfx)tst-udp-timeout: $(common-objpfx)linkobj/libc.so
|
||||
$(objpfx)tst-udp-nonblocking: $(common-objpfx)linkobj/libc.so
|
||||
$(objpfx)tst-udp-garbage: \
|
||||
$(common-objpfx)linkobj/libc.so $(shared-thread-library)
|
||||
|
||||
$(objpfx)tst-bug22542: $(common-objpfx)linkobj/libc.so
|
||||
|
||||
+7
-3
@@ -57,9 +57,13 @@ clnt_create (const char *hostname, u_long prog, u_long vers,
|
||||
|
||||
if (strcmp (proto, "unix") == 0)
|
||||
{
|
||||
memset ((char *)&sun, 0, sizeof (sun));
|
||||
sun.sun_family = AF_UNIX;
|
||||
strcpy (sun.sun_path, hostname);
|
||||
if (__sockaddr_un_set (&sun, hostname) < 0)
|
||||
{
|
||||
struct rpc_createerr *ce = &get_rpc_createerr ();
|
||||
ce->cf_stat = RPC_SYSTEMERROR;
|
||||
ce->cf_error.re_errno = errno;
|
||||
return NULL;
|
||||
}
|
||||
sock = RPC_ANYSOCK;
|
||||
client = clntunix_create (&sun, prog, vers, &sock, 0, 0);
|
||||
if (client == NULL)
|
||||
|
||||
+4
-7
@@ -154,7 +154,10 @@ svcunix_create (int sock, u_int sendsize, u_int recvsize, char *path)
|
||||
SVCXPRT *xprt;
|
||||
struct unix_rendezvous *r;
|
||||
struct sockaddr_un addr;
|
||||
socklen_t len = sizeof (struct sockaddr_in);
|
||||
socklen_t len = sizeof (addr);
|
||||
|
||||
if (__sockaddr_un_set (&addr, path) < 0)
|
||||
return NULL;
|
||||
|
||||
if (sock == RPC_ANYSOCK)
|
||||
{
|
||||
@@ -165,12 +168,6 @@ svcunix_create (int sock, u_int sendsize, u_int recvsize, char *path)
|
||||
}
|
||||
madesock = TRUE;
|
||||
}
|
||||
memset (&addr, '\0', sizeof (addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
len = strlen (path) + 1;
|
||||
memcpy (addr.sun_path, path, len);
|
||||
len += sizeof (addr.sun_family);
|
||||
|
||||
__bind (sock, (struct sockaddr *) &addr, len);
|
||||
|
||||
if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* Test to verify that overlong hostname is rejected by clnt_create
|
||||
and doesn't cause a buffer overflow (bug 22542).
|
||||
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <rpc/clnt.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Create an arbitrary hostname that's longer than fits in sun_path. */
|
||||
char name [sizeof ((struct sockaddr_un*)0)->sun_path * 2];
|
||||
memset (name, 'x', sizeof name - 1);
|
||||
name [sizeof name - 1] = '\0';
|
||||
|
||||
errno = 0;
|
||||
CLIENT *clnt = clnt_create (name, 0, 0, "unix");
|
||||
|
||||
TEST_VERIFY (clnt == NULL);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Test to verify that long path is rejected by svcunix_create (bug 28768).
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <rpc/svc.h>
|
||||
#include <shlib-compat.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
|
||||
/* svcunix_create does not have a default version in linkobj/libc.so. */
|
||||
compat_symbol_reference (libc, svcunix_create, svcunix_create, GLIBC_2_1);
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
char pathname[109];
|
||||
memset (pathname, 'x', sizeof (pathname));
|
||||
pathname[sizeof (pathname) - 1] = '\0';
|
||||
|
||||
errno = 0;
|
||||
TEST_VERIFY (svcunix_create (RPC_ANYSOCK, 4096, 4096, pathname) == NULL);
|
||||
TEST_COMPARE (errno, EINVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -83,8 +83,10 @@ libsupport-routines = \
|
||||
xasprintf \
|
||||
xbind \
|
||||
xcalloc \
|
||||
xchdir \
|
||||
xchroot \
|
||||
xclock_gettime \
|
||||
xclone \
|
||||
xclose \
|
||||
xconnect \
|
||||
xcopy_file_range \
|
||||
|
||||
@@ -41,6 +41,12 @@ struct support_capture_subprocess support_capture_subprocess
|
||||
struct support_capture_subprocess support_capture_subprogram
|
||||
(const char *file, char *const argv[]);
|
||||
|
||||
/* Copy the running program into a setgid binary and run it with CHILD_ID
|
||||
argument. If execution is successful, return the exit status of the child
|
||||
program, otherwise return a non-zero failure exit code. */
|
||||
int support_capture_subprogram_self_sgid
|
||||
(char *child_id);
|
||||
|
||||
/* Deallocate the subprocess data captured by
|
||||
support_capture_subprocess. */
|
||||
void support_capture_subprocess_free (struct support_capture_subprocess *);
|
||||
|
||||
@@ -135,6 +135,37 @@ copy_func (char **argv)
|
||||
|
||||
}
|
||||
|
||||
/* Emulate the 'exit' builtin. The exit value is optional. */
|
||||
static int
|
||||
exit_func (char **argv)
|
||||
{
|
||||
int exit_val = 0;
|
||||
|
||||
if (argv[0] != 0)
|
||||
exit_val = atoi (argv[0]) & 0xff;
|
||||
exit (exit_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Emulate the "/bin/kill" command. Options are ignored. */
|
||||
static int
|
||||
kill_func (char **argv)
|
||||
{
|
||||
int signum = SIGTERM;
|
||||
int i;
|
||||
|
||||
for (i = 0; argv[i]; i++)
|
||||
{
|
||||
pid_t pid;
|
||||
if (strcmp (argv[i], "$$") == 0)
|
||||
pid = getpid ();
|
||||
else
|
||||
pid = atoi (argv[i]);
|
||||
kill (pid, signum);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is a list of all the built-in commands we understand. */
|
||||
static struct {
|
||||
const char *name;
|
||||
@@ -143,6 +174,8 @@ static struct {
|
||||
{ "true", true_func },
|
||||
{ "echo", echo_func },
|
||||
{ "cp", copy_func },
|
||||
{ "exit", exit_func },
|
||||
{ "kill", kill_func },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -238,7 +271,7 @@ run_command_array (char **argv)
|
||||
|
||||
fprintf (stderr, "sh: execing %s failed: %s",
|
||||
argv[0], strerror (errno));
|
||||
exit (1);
|
||||
exit (127);
|
||||
}
|
||||
|
||||
waitpid (pid, &status, 0);
|
||||
@@ -251,6 +284,11 @@ run_command_array (char **argv)
|
||||
if (rv)
|
||||
exit (rv);
|
||||
}
|
||||
else if (WIFSIGNALED (status))
|
||||
{
|
||||
int sig = WTERMSIG (status);
|
||||
raise (sig);
|
||||
}
|
||||
else
|
||||
exit (1);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ struct support_subprocess support_subprocess
|
||||
struct support_subprocess support_subprogram
|
||||
(const char *file, char *const argv[]);
|
||||
|
||||
/* Invoke program FILE with ARGV arguments by using posix_spawn and wait for it
|
||||
to complete. Return program exit status. */
|
||||
int support_subprogram_wait
|
||||
(const char *file, char *const argv[]);
|
||||
|
||||
/* Wait for the subprocess indicated by PROC::PID. Return the status
|
||||
indicate by waitpid call. */
|
||||
int support_process_wait (struct support_subprocess *proc);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifndef SUPPORT_H
|
||||
#define SUPPORT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/cdefs.h>
|
||||
/* For mode_t. */
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
#include <support/capture_subprocess.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <support/xsocket.h>
|
||||
#include <support/xspawn.h>
|
||||
#include <support/support.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static void
|
||||
transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
|
||||
@@ -36,7 +39,7 @@ transfer (const char *what, struct pollfd *pfd, struct xmemstream *stream)
|
||||
if (ret < 0)
|
||||
{
|
||||
support_record_failure ();
|
||||
printf ("error: reading from subprocess %s: %m", what);
|
||||
printf ("error: reading from subprocess %s: %m\n", what);
|
||||
pfd->events = 0;
|
||||
pfd->revents = 0;
|
||||
}
|
||||
@@ -102,6 +105,129 @@ support_capture_subprogram (const char *file, char *const argv[])
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Copies the executable into a restricted directory, so that we can
|
||||
safely make it SGID with the TARGET group ID. Then runs the
|
||||
executable. */
|
||||
static int
|
||||
copy_and_spawn_sgid (char *child_id, gid_t gid)
|
||||
{
|
||||
char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd",
|
||||
test_dir, (intmax_t) getpid ());
|
||||
char *execname = xasprintf ("%s/bin", dirname);
|
||||
int infd = -1;
|
||||
int outfd = -1;
|
||||
int ret = 1, status = 1;
|
||||
|
||||
TEST_VERIFY (mkdir (dirname, 0700) == 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
|
||||
infd = open ("/proc/self/exe", O_RDONLY);
|
||||
if (infd < 0)
|
||||
FAIL_UNSUPPORTED ("unsupported: Cannot read binary from procfs\n");
|
||||
|
||||
outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700);
|
||||
TEST_VERIFY (outfd >= 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
|
||||
char buf[4096];
|
||||
for (;;)
|
||||
{
|
||||
ssize_t rdcount = read (infd, buf, sizeof (buf));
|
||||
TEST_VERIFY (rdcount >= 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
if (rdcount == 0)
|
||||
break;
|
||||
char *p = buf;
|
||||
char *end = buf + rdcount;
|
||||
while (p != end)
|
||||
{
|
||||
ssize_t wrcount = write (outfd, buf, end - p);
|
||||
if (wrcount == 0)
|
||||
errno = ENOSPC;
|
||||
TEST_VERIFY (wrcount > 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
p += wrcount;
|
||||
}
|
||||
}
|
||||
TEST_VERIFY (fchown (outfd, getuid (), gid) == 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
TEST_VERIFY (fchmod (outfd, 02750) == 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
TEST_VERIFY (close (outfd) == 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
TEST_VERIFY (close (infd) == 0);
|
||||
if (support_record_failure_is_failed ())
|
||||
goto err;
|
||||
|
||||
/* We have the binary, now spawn the subprocess. Avoid using
|
||||
support_subprogram because we only want the program exit status, not the
|
||||
contents. */
|
||||
ret = 0;
|
||||
|
||||
char * const args[] = {execname, child_id, NULL};
|
||||
|
||||
status = support_subprogram_wait (args[0], args);
|
||||
|
||||
err:
|
||||
if (outfd >= 0)
|
||||
close (outfd);
|
||||
if (infd >= 0)
|
||||
close (infd);
|
||||
if (execname != NULL)
|
||||
{
|
||||
unlink (execname);
|
||||
free (execname);
|
||||
}
|
||||
if (dirname != NULL)
|
||||
{
|
||||
rmdir (dirname);
|
||||
free (dirname);
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
FAIL_EXIT1("Failed to make sgid executable for test\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
support_capture_subprogram_self_sgid (char *child_id)
|
||||
{
|
||||
gid_t target = 0;
|
||||
const int count = 64;
|
||||
gid_t groups[count];
|
||||
|
||||
/* Get a GID which is not our current GID, but is present in the
|
||||
supplementary group list. */
|
||||
int ret = getgroups (count, groups);
|
||||
if (ret < 0)
|
||||
FAIL_UNSUPPORTED("Could not get group list for user %jd\n",
|
||||
(intmax_t) getuid ());
|
||||
|
||||
gid_t current = getgid ();
|
||||
for (int i = 0; i < ret; ++i)
|
||||
{
|
||||
if (groups[i] != current)
|
||||
{
|
||||
target = groups[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == 0)
|
||||
FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
|
||||
(intmax_t) getuid ());
|
||||
|
||||
return copy_and_spawn_sgid (child_id, target);
|
||||
}
|
||||
|
||||
void
|
||||
support_capture_subprocess_free (struct support_capture_subprocess *p)
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <support/subprocess.h>
|
||||
|
||||
static struct support_subprocess
|
||||
support_suprocess_init (void)
|
||||
support_subprocess_init (void)
|
||||
{
|
||||
struct support_subprocess result;
|
||||
|
||||
@@ -48,7 +48,7 @@ support_suprocess_init (void)
|
||||
struct support_subprocess
|
||||
support_subprocess (void (*callback) (void *), void *closure)
|
||||
{
|
||||
struct support_subprocess result = support_suprocess_init ();
|
||||
struct support_subprocess result = support_subprocess_init ();
|
||||
|
||||
result.pid = xfork ();
|
||||
if (result.pid == 0)
|
||||
@@ -71,7 +71,7 @@ support_subprocess (void (*callback) (void *), void *closure)
|
||||
struct support_subprocess
|
||||
support_subprogram (const char *file, char *const argv[])
|
||||
{
|
||||
struct support_subprocess result = support_suprocess_init ();
|
||||
struct support_subprocess result = support_subprocess_init ();
|
||||
|
||||
posix_spawn_file_actions_t fa;
|
||||
/* posix_spawn_file_actions_init does not fail. */
|
||||
@@ -84,7 +84,7 @@ support_subprogram (const char *file, char *const argv[])
|
||||
xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
|
||||
xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
|
||||
|
||||
result.pid = xposix_spawn (file, &fa, NULL, argv, NULL);
|
||||
result.pid = xposix_spawn (file, &fa, NULL, argv, environ);
|
||||
|
||||
xclose (result.stdout_pipe[1]);
|
||||
xclose (result.stderr_pipe[1]);
|
||||
@@ -92,6 +92,19 @@ support_subprogram (const char *file, char *const argv[])
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
support_subprogram_wait (const char *file, char *const argv[])
|
||||
{
|
||||
posix_spawn_file_actions_t fa;
|
||||
|
||||
posix_spawn_file_actions_init (&fa);
|
||||
struct support_subprocess res = support_subprocess_init ();
|
||||
|
||||
res.pid = xposix_spawn (file, &fa, NULL, argv, environ);
|
||||
|
||||
return support_process_wait (&res);
|
||||
}
|
||||
|
||||
int
|
||||
support_process_wait (struct support_subprocess *proc)
|
||||
{
|
||||
|
||||
+159
-14
@@ -1,5 +1,6 @@
|
||||
/* Temporary file handling for tests.
|
||||
Copyright (C) 1998-2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 1998-2022 Free Software Foundation, Inc.
|
||||
Copyright The GNU Tools Authors.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
@@ -20,15 +21,17 @@
|
||||
some 32-bit platforms. */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/temp_file.h>
|
||||
#include <support/temp_file-internal.h>
|
||||
#include <support/support.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <xunistd.h>
|
||||
|
||||
/* List of temporary files. */
|
||||
static struct temp_name_list
|
||||
@@ -36,14 +39,20 @@ static struct temp_name_list
|
||||
struct temp_name_list *next;
|
||||
char *name;
|
||||
pid_t owner;
|
||||
bool toolong;
|
||||
} *temp_name_list;
|
||||
|
||||
/* Location of the temporary files. Set by the test skeleton via
|
||||
support_set_test_dir. The string is not be freed. */
|
||||
static const char *test_dir = _PATH_TMP;
|
||||
|
||||
void
|
||||
add_temp_file (const char *name)
|
||||
/* Name of subdirectories in a too long temporary directory tree. */
|
||||
static char toolong_subdir[NAME_MAX + 1];
|
||||
static bool toolong_initialized;
|
||||
static size_t toolong_path_max;
|
||||
|
||||
static void
|
||||
add_temp_file_internal (const char *name, bool toolong)
|
||||
{
|
||||
struct temp_name_list *newp
|
||||
= (struct temp_name_list *) xcalloc (sizeof (*newp), 1);
|
||||
@@ -53,21 +62,26 @@ add_temp_file (const char *name)
|
||||
newp->name = newname;
|
||||
newp->next = temp_name_list;
|
||||
newp->owner = getpid ();
|
||||
newp->toolong = toolong;
|
||||
temp_name_list = newp;
|
||||
}
|
||||
else
|
||||
free (newp);
|
||||
}
|
||||
|
||||
void
|
||||
add_temp_file (const char *name)
|
||||
{
|
||||
add_temp_file_internal (name, false);
|
||||
}
|
||||
|
||||
int
|
||||
create_temp_file (const char *base, char **filename)
|
||||
create_temp_file_in_dir (const char *base, const char *dir, char **filename)
|
||||
{
|
||||
char *fname;
|
||||
int fd;
|
||||
|
||||
fname = (char *) xmalloc (strlen (test_dir) + 1 + strlen (base)
|
||||
+ sizeof ("XXXXXX"));
|
||||
strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
|
||||
fname = xasprintf ("%s/%sXXXXXX", dir, base);
|
||||
|
||||
fd = mkstemp (fname);
|
||||
if (fd == -1)
|
||||
@@ -86,8 +100,14 @@ create_temp_file (const char *base, char **filename)
|
||||
return fd;
|
||||
}
|
||||
|
||||
char *
|
||||
support_create_temp_directory (const char *base)
|
||||
int
|
||||
create_temp_file (const char *base, char **filename)
|
||||
{
|
||||
return create_temp_file_in_dir (base, test_dir, filename);
|
||||
}
|
||||
|
||||
static char *
|
||||
create_temp_directory_internal (const char *base, bool toolong)
|
||||
{
|
||||
char *path = xasprintf ("%s/%sXXXXXX", test_dir, base);
|
||||
if (mkdtemp (path) == NULL)
|
||||
@@ -95,16 +115,132 @@ support_create_temp_directory (const char *base)
|
||||
printf ("error: mkdtemp (\"%s\"): %m", path);
|
||||
exit (1);
|
||||
}
|
||||
add_temp_file (path);
|
||||
add_temp_file_internal (path, toolong);
|
||||
return path;
|
||||
}
|
||||
|
||||
char *
|
||||
support_create_temp_directory (const char *base)
|
||||
{
|
||||
return create_temp_directory_internal (base, false);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_toolong_initialized (void)
|
||||
{
|
||||
if (!toolong_initialized)
|
||||
FAIL_EXIT1 ("uninitialized toolong directory tree\n");
|
||||
}
|
||||
|
||||
static void
|
||||
initialize_toolong (const char *base)
|
||||
{
|
||||
long name_max = pathconf (base, _PC_NAME_MAX);
|
||||
name_max = (name_max < 0 ? 64
|
||||
: (name_max < sizeof (toolong_subdir) ? name_max
|
||||
: sizeof (toolong_subdir) - 1));
|
||||
|
||||
long path_max = pathconf (base, _PC_PATH_MAX);
|
||||
path_max = (path_max < 0 ? 1024
|
||||
: path_max <= PTRDIFF_MAX ? path_max : PTRDIFF_MAX);
|
||||
|
||||
/* Sanity check to ensure that the test does not create temporary directories
|
||||
in different filesystems because this API doesn't support it. */
|
||||
if (toolong_initialized)
|
||||
{
|
||||
if (name_max != strlen (toolong_subdir))
|
||||
FAIL_UNSUPPORTED ("name_max: Temporary directories in different"
|
||||
" filesystems not supported yet\n");
|
||||
if (path_max != toolong_path_max)
|
||||
FAIL_UNSUPPORTED ("path_max: Temporary directories in different"
|
||||
" filesystems not supported yet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
toolong_path_max = path_max;
|
||||
|
||||
size_t len = name_max;
|
||||
memset (toolong_subdir, 'X', len);
|
||||
toolong_initialized = true;
|
||||
}
|
||||
|
||||
char *
|
||||
support_create_and_chdir_toolong_temp_directory (const char *basename)
|
||||
{
|
||||
char *base = create_temp_directory_internal (basename, true);
|
||||
xchdir (base);
|
||||
|
||||
initialize_toolong (base);
|
||||
|
||||
size_t sz = strlen (toolong_subdir);
|
||||
|
||||
/* Create directories and descend into them so that the final path is larger
|
||||
than PATH_MAX. */
|
||||
for (size_t i = 0; i <= toolong_path_max / sz; i++)
|
||||
{
|
||||
int ret = mkdir (toolong_subdir, S_IRWXU);
|
||||
if (ret != 0 && errno == ENAMETOOLONG)
|
||||
FAIL_UNSUPPORTED ("Filesystem does not support creating too long "
|
||||
"directory trees\n");
|
||||
else if (ret != 0)
|
||||
FAIL_EXIT1 ("Failed to create directory tree: %m\n");
|
||||
xchdir (toolong_subdir);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
void
|
||||
support_chdir_toolong_temp_directory (const char *base)
|
||||
{
|
||||
ensure_toolong_initialized ();
|
||||
|
||||
xchdir (base);
|
||||
|
||||
size_t sz = strlen (toolong_subdir);
|
||||
for (size_t i = 0; i <= toolong_path_max / sz; i++)
|
||||
xchdir (toolong_subdir);
|
||||
}
|
||||
|
||||
/* Helper functions called by the test skeleton follow. */
|
||||
|
||||
void
|
||||
support_set_test_dir (const char *path)
|
||||
static void
|
||||
remove_toolong_subdirs (const char *base)
|
||||
{
|
||||
test_dir = path;
|
||||
ensure_toolong_initialized ();
|
||||
|
||||
if (chdir (base) != 0)
|
||||
{
|
||||
printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n",
|
||||
base);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Descend. */
|
||||
int levels = 0;
|
||||
size_t sz = strlen (toolong_subdir);
|
||||
for (levels = 0; levels <= toolong_path_max / sz; levels++)
|
||||
if (chdir (toolong_subdir) != 0)
|
||||
{
|
||||
printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n",
|
||||
toolong_subdir);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ascend and remove. */
|
||||
while (--levels >= 0)
|
||||
{
|
||||
if (chdir ("..") != 0)
|
||||
{
|
||||
printf ("warning: toolong cleanup failed: chdir (\"..\"): %m\n");
|
||||
return;
|
||||
}
|
||||
if (remove (toolong_subdir) != 0)
|
||||
{
|
||||
printf ("warning: could not remove subdirectory: %s: %m\n",
|
||||
toolong_subdir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -119,6 +255,9 @@ support_delete_temp_files (void)
|
||||
around, to prevent PID reuse.) */
|
||||
if (temp_name_list->owner == pid)
|
||||
{
|
||||
if (temp_name_list->toolong)
|
||||
remove_toolong_subdirs (temp_name_list->name);
|
||||
|
||||
if (remove (temp_name_list->name) != 0)
|
||||
printf ("warning: could not remove temporary file: %s: %m\n",
|
||||
temp_name_list->name);
|
||||
@@ -143,3 +282,9 @@ support_print_temp_files (FILE *f)
|
||||
fprintf (f, ")\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
support_set_test_dir (const char *path)
|
||||
{
|
||||
test_dir = path;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user