diff --git a/SOURCES/glibc-rh2116938.patch b/SOURCES/glibc-rh2116938.patch new file mode 100644 index 0000000..f642aba --- /dev/null +++ b/SOURCES/glibc-rh2116938.patch @@ -0,0 +1,449 @@ +1. Added "$(objpfx)tst-cmsghdr: $(libdl)" to socket/Makefile since we still + need $(libdl) in RHEL8. + +2. Included stddef.h in socket/tst-cmsghdr-skeleton.c because it uses NULL. + +commit 9c443ac4559a47ed99859bd80d14dc4b6dd220a1 +Author: Arjun Shankar +Date: Tue Aug 2 11:10:25 2022 +0200 + + socket: Check lengths before advancing pointer in CMSG_NXTHDR + + The inline and library functions that the CMSG_NXTHDR macro may expand + to increment the pointer to the header before checking the stride of + the increment against available space. Since C only allows incrementing + pointers to one past the end of an array, the increment must be done + after a length check. This commit fixes that and includes a regression + test for CMSG_FIRSTHDR and CMSG_NXTHDR. + + The Linux, Hurd, and generic headers are all changed. + + Tested on Linux on armv7hl, i686, x86_64, aarch64, ppc64le, and s390x. + + [BZ #28846] + + Reviewed-by: Siddhesh Poyarekar + +Conflicts: + socket/Makefile + (usual test backport differences) + +diff --git a/bits/socket.h b/bits/socket.h +index 725798882e4b803b..0474613a9c003eeb 100644 +--- a/bits/socket.h ++++ b/bits/socket.h +@@ -245,6 +245,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -254,18 +260,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/socket/Makefile b/socket/Makefile +index 8975a65c2aabbfbc..a445383f8739351e 100644 +--- a/socket/Makefile ++++ b/socket/Makefile +@@ -31,7 +31,12 @@ routines := accept bind connect getpeername getsockname getsockopt \ + setsockopt shutdown socket socketpair isfdtype opensock \ + sockatmark accept4 recvmmsg sendmmsg sockaddr_un_set + +-tests := tst-accept4 ++tests := \ ++ tst-accept4 \ ++ tst-cmsghdr \ ++ # tests ++ ++$(objpfx)tst-cmsghdr: $(libdl) + + tests-internal := \ + tst-sockaddr_un_set \ +diff --git a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c +new file mode 100644 +index 0000000000000000..7accfa6e54708e2a +--- /dev/null ++++ b/socket/tst-cmsghdr-skeleton.c +@@ -0,0 +1,93 @@ ++/* Test ancillary data header creation. ++ 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 ++ . */ ++ ++/* We use the preprocessor to generate the function/macro tests instead of ++ using indirection because having all the macro expansions alongside ++ each other lets the compiler warn us about suspicious pointer ++ arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */ ++ ++#include ++#include ++ ++#define RUN_TEST_CONCAT(suffix) run_test_##suffix ++#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix) ++ ++static void ++RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void) ++{ ++ struct msghdr m = {0}; ++ struct cmsghdr *cmsg; ++ char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0}; ++ ++ m.msg_control = cmsgbuf; ++ m.msg_controllen = sizeof (cmsgbuf); ++ ++ /* First header should point to the start of the buffer. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ ++ /* If the first header length consumes the entire buffer, there is no ++ space remaining for additional headers. */ ++ cmsg->cmsg_len = sizeof (cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header length is so big, using it would cause an overflow. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = SIZE_MAX; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The first header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The second header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD)); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len = sizeof (cmsgbuf) ++ - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */ ++ - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The second header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ return; ++} +diff --git a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c +new file mode 100644 +index 0000000000000000..68c96d3c9dd2bce8 +--- /dev/null ++++ b/socket/tst-cmsghdr.c +@@ -0,0 +1,56 @@ ++/* Test ancillary data header creation. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++#define PAYLOAD "Hello, World!" ++ ++/* CMSG_NXTHDR is a macro that calls an inline function defined in ++ bits/socket.h. In case the function cannot be inlined, libc.so carries ++ a copy. Both versions need to be tested. */ ++ ++#define CMSG_NXTHDR_IMPL CMSG_NXTHDR ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *); ++ ++#define CMSG_NXTHDR_IMPL cmsg_nxthdr ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static int ++do_test (void) ++{ ++ static void *handle; ++ ++ run_test_CMSG_NXTHDR (); ++ ++ handle = xdlopen (LIBC_SO, RTLD_LAZY); ++ cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *)) ++ xdlsym (handle, "__cmsg_nxthdr"); ++ ++ run_test_cmsg_nxthdr (); ++ ++ return 0; ++} ++ ++#include +diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h +index 18959139dc7d325b..cc66684061e3e179 100644 +--- a/sysdeps/mach/hurd/bits/socket.h ++++ b/sysdeps/mach/hurd/bits/socket.h +@@ -249,6 +249,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -258,18 +264,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h +index c3fbb2110296273c..6b895b89831d2cb5 100644 +--- a/sysdeps/unix/sysv/linux/bits/socket.h ++++ b/sysdeps/unix/sysv/linux/bits/socket.h +@@ -302,6 +302,12 @@ struct cmsghdr + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -311,18 +317,38 @@ extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff --git a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +index bab0be6884d9da1c..16594622211c1c8b 100644 +--- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c ++++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +@@ -23,18 +23,38 @@ + struct cmsghdr * + __cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg) + { ++ /* We may safely assume that cmsg lies between mhdr->msg_control and ++ mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control; ++ unsigned char * cmsg_ptr = (unsigned char *) cmsg; ++ ++ size_t size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ +- return NULL; ++ return (struct cmsghdr *) 0; ++ ++ /* There isn't enough space between cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr) ++ < size_needed) ++ || ((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr ++ - size_needed) ++ < cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; + ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + cmsg = (struct cmsghdr *) ((unsigned char *) cmsg + + CMSG_ALIGN (cmsg->cmsg_len)); +- if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control +- + mhdr->msg_controllen) +- || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len) +- > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen))) +- /* No more entries. */ +- return NULL; + return cmsg; + } + libc_hidden_def (__cmsg_nxthdr) diff --git a/SOURCES/glibc-rh2121746-1.patch b/SOURCES/glibc-rh2121746-1.patch new file mode 100644 index 0000000..a27c0eb --- /dev/null +++ b/SOURCES/glibc-rh2121746-1.patch @@ -0,0 +1,202 @@ +From d0e357ff45a75553dee3b17ed7d303bfa544f6fe Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Fri, 26 Aug 2022 21:15:43 +0200 +Subject: elf: Call __libc_early_init for reused namespaces (bug 29528) + +libc_map is never reset to NULL, neither during dlclose nor on a +dlopen call which reuses the namespace structure. As a result, if a +namespace is reused, its libc is not initialized properly. The most +visible result is a crash in the functions. + +To prevent similar bugs on namespace reuse from surfacing, +unconditionally initialize the chosen namespace to zero using memset. + +[Note from DJ: Regenerated for new line numbers and context, added +link dependency on libdl]] + +diff -rupN a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2022-10-05 15:04:11.814901849 -0400 ++++ b/elf/Makefile 2022-10-05 17:02:19.858635958 -0400 +@@ -367,6 +367,7 @@ tests += \ + tst-dlmopen3 \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ ++ tst-dlmopen-twice \ + tst-dlopenfail \ + tst-dlopenfail-2 \ + tst-dlopenrpath \ +@@ -671,6 +672,8 @@ modules-names = \ + tst-dlmopen1mod \ + tst-dlmopen-dlerror-mod \ + tst-dlmopen-gethostbyname-mod \ ++ tst-dlmopen-twice-mod1 \ ++ tst-dlmopen-twice-mod2 \ + tst-dlopenfaillinkmod \ + tst-dlopenfailmod1 \ + tst-dlopenfailmod2 \ +@@ -2569,3 +2572,9 @@ $(objpfx)tst-audit-tlsdesc.out: $(objpfx + tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so + $(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so + tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so ++ ++ ++$(objpfx)tst-dlmopen-twice: $(libdl) ++$(objpfx)tst-dlmopen-twice.out: \ ++ $(objpfx)tst-dlmopen-twice-mod1.so \ ++ $(objpfx)tst-dlmopen-twice-mod2.so +diff -rupN a/elf/dl-open.c b/elf/dl-open.c +--- a/elf/dl-open.c 2022-10-05 15:04:11.635894932 -0400 ++++ b/elf/dl-open.c 2022-10-05 15:10:31.667638060 -0400 +@@ -836,11 +836,14 @@ _dl_open (const char *file, int mode, co + _dl_signal_error (EINVAL, file, NULL, N_("\ + no more namespaces available for dlmopen()")); + } +- else if (nsid == GL(dl_nns)) +- { +- __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock); +- ++GL(dl_nns); +- } ++ ++ if (nsid == GL(dl_nns)) ++ ++GL(dl_nns); ++ ++ /* Initialize the new namespace. Most members are ++ zero-initialized, only the lock needs special treatment. */ ++ memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid])); ++ __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock); + + _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT; + } +diff -rupN a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c +--- a/elf/tst-dlmopen-twice-mod1.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlmopen-twice-mod1.c 2022-10-05 15:10:31.671638216 -0400 +@@ -0,0 +1,37 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 1. ++ 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 ++ . */ ++ ++#include ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod1.so loaded"); ++ fflush (stdout); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod1.so about to be unloaded"); ++ fflush (stdout); ++} ++ ++/* Large allocation. The second module does not have this, so it ++ should load libc at a different address. */ ++char large_allocate[16 * 1024 * 1024]; +diff -rupN a/elf/tst-dlmopen-twice-mod2.c b/elf/tst-dlmopen-twice-mod2.c +--- a/elf/tst-dlmopen-twice-mod2.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlmopen-twice-mod2.c 2022-10-05 15:10:31.676638411 -0400 +@@ -0,0 +1,50 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 2. ++ 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 ++ . */ ++ ++#include ++#include ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod2.so loaded"); ++ fflush (stdout); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod2.so about to be unloaded"); ++ fflush (stdout); ++} ++ ++int ++run_check (void) ++{ ++ puts ("info: about to call isalpha"); ++ fflush (stdout); ++ ++ volatile char ch = 'a'; ++ if (!isalpha (ch)) ++ { ++ puts ("error: isalpha ('a') is not true"); ++ fflush (stdout); ++ return 1; ++ } ++ return 0; ++} +diff -rupN a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c +--- a/elf/tst-dlmopen-twice.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlmopen-twice.c 2022-10-05 15:10:31.679638528 -0400 +@@ -0,0 +1,34 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Main. ++ 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 ++ . */ ++ ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW); ++ xdlclose (handle); ++ handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW); ++ int (*run_check) (void) = xdlsym (handle, "run_check"); ++ TEST_COMPARE (run_check (), 0); ++ xdlclose (handle); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-rh2121746-2.patch b/SOURCES/glibc-rh2121746-2.patch new file mode 100644 index 0000000..5bd43c8 --- /dev/null +++ b/SOURCES/glibc-rh2121746-2.patch @@ -0,0 +1,98 @@ +From 2c42257314536b94cc8d52edede86e94e98c1436 Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Fri, 14 Oct 2022 11:02:25 +0200 +Subject: [PATCH] elf: Do not completely clear reused namespace in dlmopen (bug + 29600) +Content-type: text/plain; charset=UTF-8 + +The data in the _ns_debug member must be preserved, otherwise +_dl_debug_initialize enters an infinite loop. To be conservative, +only clear the libc_map member for now, to fix bug 29528. + +Fixes commit d0e357ff45a75553dee3b17ed7d303bfa544f6fe +("elf: Call __libc_early_init for reused namespaces (bug 29528)"), +by reverting most of it. + +Reviewed-by: Carlos O'Donell +Tested-by: Carlos O'Donell +--- + elf/dl-open.c | 14 ++++++-------- + elf/tst-dlmopen-twice.c | 28 ++++++++++++++++++++++++---- + 2 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 46e8066fd8..e7db5e9642 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -836,15 +836,13 @@ _dl_open (const char *file, int mode, co + _dl_signal_error (EINVAL, file, NULL, N_("\ + no more namespaces available for dlmopen()")); + } ++ else if (nsid == GL(dl_nns)) ++ { ++ __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock); ++ ++GL(dl_nns); ++ } + +- if (nsid == GL(dl_nns)) +- ++GL(dl_nns); +- +- /* Initialize the new namespace. Most members are +- zero-initialized, only the lock needs special treatment. */ +- memset (&GL(dl_ns)[nsid], 0, sizeof (GL(dl_ns)[nsid])); +- __rtld_lock_initialize (GL(dl_ns)[nsid]._ns_unique_sym_table.lock); +- ++ GL(dl_ns)[nsid].libc_map = NULL; + _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT; + } + /* Never allow loading a DSO in a namespace which is empty. Such +diff --git a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c +index 449f3c8fa9..70c71fe19c 100644 +--- a/elf/tst-dlmopen-twice.c ++++ b/elf/tst-dlmopen-twice.c +@@ -16,18 +16,38 @@ + License along with the GNU C Library; if not, see + . */ + +-#include ++#include + #include ++#include + +-static int +-do_test (void) ++/* Run the test multiple times, to check finding a new namespace while ++ another namespace is already in use. This used to trigger bug 29600. */ ++static void ++recurse (int depth) + { +- void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", RTLD_NOW); ++ if (depth == 0) ++ return; ++ ++ printf ("info: running at depth %d\n", depth); ++ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", ++ RTLD_NOW); + xdlclose (handle); + handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW); + int (*run_check) (void) = xdlsym (handle, "run_check"); + TEST_COMPARE (run_check (), 0); ++ recurse (depth - 1); + xdlclose (handle); ++} ++ ++static int ++do_test (void) ++{ ++ /* First run the test without nesting. */ ++ recurse (1); ++ ++ /* Then with nesting. The constant needs to be less than the ++ internal DL_NNS namespace constant. */ ++ recurse (10); + return 0; + } + +-- +2.31.1 + diff --git a/SOURCES/glibc-rh2122501-1.patch b/SOURCES/glibc-rh2122501-1.patch new file mode 100644 index 0000000..75d333e --- /dev/null +++ b/SOURCES/glibc-rh2122501-1.patch @@ -0,0 +1,472 @@ +commit c6fad4fa149485a307207f707e5851216f190fc8 +Author: Florian Weimer +Date: Thu Mar 19 18:32:28 2020 -0300 + + stdio: Remove memory leak from multibyte convertion [BZ#25691] + + This is an updated version of a previous patch [1] with the + following changes: + + - Use compiler overflow builtins on done_add_func function. + - Define the scratch +utstring_converted_wide_string using + CHAR_T. + - Added a testcase and mention the bug report. + + Both default and wide printf functions might leak memory when + manipulate multibyte characters conversion depending of the size + of the input (whether __libc_use_alloca trigger or not the fallback + heap allocation). + + This patch fixes it by removing the extra memory allocation on + string formatting with conversion parts. + + The testcase uses input argument size that trigger memory leaks + on unpatched code (using a scratch buffer the threashold to use + heap allocation is lower). + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Adhemerval Zanella + + [1] https://sourceware.org/pipermail/libc-alpha/2017-June/082098.html + + (cherry picked from commit 3cc4a8367c23582b7db14cf4e150e4068b7fd461) + +diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c +index ae412e4b8444aea2..dab56b6ba2c7bdbe 100644 +--- a/stdio-common/vfprintf.c ++++ b/stdio-common/vfprintf.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + /* This code is shared between the standard stdio implementation found + in GNU C library and the libio implementation originally found in +@@ -64,23 +65,40 @@ + } while (0) + #define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED) + +-#define done_add(val) \ +- do { \ +- unsigned int _val = val; \ +- assert ((unsigned int) done < (unsigned int) INT_MAX); \ +- if (__glibc_unlikely (INT_MAX - done < _val)) \ +- { \ +- done = -1; \ +- __set_errno (EOVERFLOW); \ +- goto all_done; \ +- } \ +- done += _val; \ +- } while (0) ++/* Add LENGTH to DONE. Return the new value of DONE, or -1 on ++ overflow (and set errno accordingly). */ ++static inline int ++done_add_func (size_t length, int done) ++{ ++ if (done < 0) ++ return done; ++ int ret; ++ if (INT_ADD_WRAPV (done, length, &ret)) ++ { ++ __set_errno (EOVERFLOW); ++ return -1; ++ } ++ return ret; ++} ++ ++#define done_add(val) \ ++ do \ ++ { \ ++ /* Ensure that VAL has a type similar to int. */ \ ++ _Static_assert (sizeof (val) == sizeof (int), "value int size"); \ ++ _Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \ ++ done = done_add_func ((val), done); \ ++ if (done < 0) \ ++ goto all_done; \ ++ } \ ++ while (0) + + #ifndef COMPILE_WPRINTF + # define vfprintf _IO_vfprintf_internal + # define CHAR_T char ++# define CHAR_T char + # define UCHAR_T unsigned char ++# define OTHER_CHAR_T wchar_t + # define INT_T int + typedef const char *THOUSANDS_SEP_T; + # define L_(Str) Str +@@ -88,22 +106,10 @@ 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 _IO_vfwprintf + # define CHAR_T wchar_t +@@ -118,21 +124,11 @@ 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 ++# define CHAR_T wchar_t ++# define OTHER_CHAR_T char + + # undef _itoa + # define _itoa(Val, Buf, Base, Case) _itowa (Val, Buf, Base, Case) +@@ -141,6 +137,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. */ +@@ -160,24 +183,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 +@@ -975,7 +1089,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. */ \ +@@ -987,7 +1100,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. */ \ +@@ -1004,41 +1116,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 \ + { \ +@@ -1061,8 +1144,6 @@ static const uint8_t jump_table[] = + outstring (string, len); \ + if (left) \ + PAD (L' '); \ +- if (__glibc_unlikely (string_malloced)) \ +- free (string); \ + } \ + break; + #else +@@ -1111,7 +1192,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. */ \ +@@ -1123,7 +1203,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. */ \ +@@ -1149,51 +1228,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) \ +@@ -1207,8 +1247,6 @@ static const uint8_t jump_table[] = + outstring (string, len); \ + if (left) \ + PAD (' '); \ +- if (__glibc_unlikely (string_malloced)) \ +- free (string); \ + } \ + break; + #endif diff --git a/SOURCES/glibc-rh2122501-2.patch b/SOURCES/glibc-rh2122501-2.patch new file mode 100644 index 0000000..8cac488 --- /dev/null +++ b/SOURCES/glibc-rh2122501-2.patch @@ -0,0 +1,160 @@ +commit 29b12753b51866b227a6c0ac96c2c6c0e20f3497 +Author: Adhemerval Zanella +Date: Thu Mar 19 18:35:46 2020 -0300 + + stdio: Add tests for printf multibyte convertion leak [BZ#25691] + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + (cherry picked from commit 910a835dc96c1f518ac2a6179fc622ba81ffb159) + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index a10f12ab3ccbd76e..51062a7dbf698931 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -63,6 +63,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ + tst-vfprintf-mbs-prec \ + tst-scanf-round \ + tst-renameat2 \ ++ tst-printf-bz25691 \ + + test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble + +@@ -71,10 +72,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 +@@ -96,6 +99,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)' > $@; \ +diff --git a/stdio-common/tst-printf-bz25691.c b/stdio-common/tst-printf-bz25691.c +new file mode 100644 +index 0000000000000000..37b30a3a8a7dc5e2 +--- /dev/null ++++ b/stdio-common/tst-printf-bz25691.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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 diff --git a/SOURCES/glibc-rh2122501-3.patch b/SOURCES/glibc-rh2122501-3.patch new file mode 100644 index 0000000..331dd92 --- /dev/null +++ b/SOURCES/glibc-rh2122501-3.patch @@ -0,0 +1,356 @@ +commit e1c0c00cc2bdd147bfcf362ada1443bee90465ec +Author: Joseph Myers +Date: Tue Jul 7 14:54:12 2020 +0000 + + Remove most vfprintf width/precision-dependent allocations (bug 14231, bug 26211). + + The vfprintf implementation (used for all printf-family functions) + contains complicated logic to allocate internal buffers of a size + depending on the width and precision used for a format, using either + malloc or alloca depending on that size, and with consequent checks + for size overflow and allocation failure. + + As noted in bug 26211, the version of that logic used when '$' plus + argument number formats are in use is missing the overflow checks, + which can result in segfaults (quite possibly exploitable, I didn't + try to work that out) when the width or precision is in the range + 0x7fffffe0 through 0x7fffffff (maybe smaller values as well in the + wprintf case on 32-bit systems, when the multiplication by sizeof + (CHAR_T) can overflow). + + All that complicated logic in fact appears to be useless. As far as I + can tell, there has been no need (outside the floating-point printf + code, which does its own allocations) for allocations depending on + width or precision since commit + 3e95f6602b226e0de06aaff686dc47b282d7cc16 ("Remove limitation on size + of precision for integers", Sun Sep 12 21:23:32 1999 +0000). Thus, + this patch removes that logic completely, thereby fixing both problems + with excessive allocations for large width and precision for + non-floating-point formats, and the problem with missing overflow + checks with such allocations. Note that this does have the + consequence that width and precision up to INT_MAX are now allowed + where previously INT_MAX / sizeof (CHAR_T) - EXTSIZ or more would have + been rejected, so could potentially expose any other overflows where + the value would previously have been rejected by those removed checks. + + I believe this completely fixes bugs 14231 and 26211. + + Excessive allocations are still possible in the floating-point case + (bug 21127), as are other integer or buffer overflows (see bug 26201). + This does not address the cases where a precision larger than INT_MAX + (embedded in the format string) would be meaningful without printf's + return value overflowing (when it's used with a string format, or %g + without the '#' flag, so the actual output will be much smaller), as + mentioned in bug 17829 comment 8; using size_t internally for + precision to handle that case would be complicated by struct + printf_info being a public ABI. Nor does it address the matter of an + INT_MIN width being negated (bug 17829 comment 7; the same logic + appears a second time in the file as well, in the form of multiplying + by -1). There may be other sources of memory allocations with malloc + in printf functions as well (bug 24988, bug 16060). From inspection, + I think there are also integer overflows in two copies of "if ((width + -= len) < 0)" logic (where width is int, len is size_t and a very long + string could result in spurious padding being output on a 32-bit + system before printf overflows the count of output characters). + + Tested for x86-64 and x86. + + (cherry picked from commit 6caddd34bd7ffb5ac4f36c8e036eee100c2cc535) + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index 51062a7dbf698931..d76b47bd5f932f69 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -64,6 +64,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ + tst-scanf-round \ + tst-renameat2 \ + tst-printf-bz25691 \ ++ tst-vfprintf-width-prec-alloc + + test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble + +diff --git a/stdio-common/bug22.c b/stdio-common/bug22.c +index b26399acb7dfc775..e12b01731e1b4ac8 100644 +--- a/stdio-common/bug22.c ++++ b/stdio-common/bug22.c +@@ -42,7 +42,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; + + ret = fprintf (fp, "%" SN2 "d%" SN2 "d", 1, 1); +diff --git a/stdio-common/tst-vfprintf-width-prec-alloc.c b/stdio-common/tst-vfprintf-width-prec-alloc.c +new file mode 100644 +index 0000000000000000..0a74b53a3389d699 +--- /dev/null ++++ b/stdio-common/tst-vfprintf-width-prec-alloc.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 ++ . */ ++ ++#include ++#include ++#include ++ ++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 +diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c +index dab56b6ba2c7bdbe..6b83ba91a12cdcd5 100644 +--- a/stdio-common/vfprintf.c ++++ b/stdio-common/vfprintf.c +@@ -42,10 +42,6 @@ + + #include + +-/* 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 \ + { \ +@@ -1295,7 +1291,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + + /* 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. */ +@@ -1404,7 +1399,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + UCHAR_T pad = L_(' ');/* Padding character. */ + CHAR_T spec; + +- workstart = NULL; + workend = work_buffer + WORK_BUFFER_SIZE; + + /* Get current character in format string. */ +@@ -1496,31 +1490,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + 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); + +@@ -1528,31 +1497,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + 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; +@@ -1601,34 +1552,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + } + 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. */ +@@ -1692,10 +1615,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + /* 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)); +@@ -1713,18 +1632,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) + + /* 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); + + all_done: +- if (__glibc_unlikely (workstart != NULL)) +- free (workstart); + /* Unlock the stream. */ + _IO_funlockfile (s); + _IO_cleanup_region_end (0); +@@ -1767,8 +1679,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 +@@ -1957,7 +1867,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. */ +@@ -1991,27 +1900,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) + { +@@ -2085,18 +1973,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; diff --git a/SOURCES/glibc-rh2122501-4.patch b/SOURCES/glibc-rh2122501-4.patch new file mode 100644 index 0000000..97436f4 --- /dev/null +++ b/SOURCES/glibc-rh2122501-4.patch @@ -0,0 +1,86 @@ +commit 211a30a92b72a18ea4caa35ed503b70bc644923e +Author: Joseph Myers +Date: Mon Nov 8 19:11:51 2021 +0000 + + Fix memmove call in vfprintf-internal.c:group_number + + A recent GCC mainline change introduces errors of the form: + + vfprintf-internal.c: In function 'group_number': + vfprintf-internal.c:2093:15: error: 'memmove' specified bound between 9223372036854775808 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Werror=stringop-overflow=] + 2093 | memmove (w, s, (front_ptr -s) * sizeof (CHAR_T)); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + This is a genuine bug in the glibc code: s > front_ptr is always true + at this point in the code, and the intent is clearly for the + subtraction to be the other way round. The other arguments to the + memmove call here also appear to be wrong; w and s point just *after* + the destination and source for copying the rest of the number, so the + size needs to be subtracted to get appropriate pointers for the + copying. Adjust the memmove call to conform to the apparent intent of + the code, so fixing the -Wstringop-overflow error. + + Now, if the original code were ever executed, a buffer overrun would + result. However, I believe this code (introduced in commit + edc1686af0c0fc2eb535f1d38cdf63c1a5a03675, "vfprintf: Reuse work_buffer + in group_number", so in glibc 2.26) is unreachable in prior glibc + releases (so there is no need for a bug in Bugzilla, no need to + consider any backports unless someone wants to build older glibc + releases with GCC 12 and no possibility of this buffer overrun + resulting in a security issue). + + work_buffer is 1000 bytes / 250 wide characters. This case is only + reachable if an initial part of the number, plus a grouped copy of the + rest of the number, fail to fit in that space; that is, if the grouped + number fails to fit in the space. In the wide character case, + grouping is always one wide character, so even with a locale (of which + there aren't any in glibc) grouping every digit, a number would need + to occupy at least 125 wide characters to overflow, and a 64-bit + integer occupies at most 23 characters in octal including a leading 0. + In the narrow character case, the multibyte encoding of the grouping + separator would need to be at least 42 bytes to overflow, again + supposing grouping every digit, but MB_LEN_MAX is 16. So even if we + admit the case of artificially constructed locales not shipped with + glibc, given that such a locale would need to use one of the character + sets supported by glibc, this code cannot be reached at present. (And + POSIX only actually specifies the ' flag for grouping for decimal + output, though glibc acts on it for other bases as well.) + + With binary output (if you consider use of grouping there to be + valid), you'd need a 15-byte multibyte character for overflow; I don't + know if any supported character set has such a character (if, again, + we admit constructed locales using grouping every digit and a grouping + separator chosen to have a multibyte encoding as long as possible, as + well as accepting use of grouping with binary), but given that we have + this code at all (clearly it's not *correct*, or in accordance with + the principle of avoiding arbitrary limits, to skip grouping on + running out of internal space like that), I don't think it should need + any further changes for binary printf support to go in. + + On the other hand, support for large sizes of _BitInt in printf (see + the N2858 proposal) *would* require something to be done about such + arbitrary limits (presumably using dynamic allocation in printf again, + for sufficiently large _BitInt arguments only - currently only + floating-point uses dynamic allocation, and, as previously discussed, + that could actually be replaced by bounded allocation given smarter + code). + + Tested with build-many-glibcs.py for aarch64-linux-gnu (GCC mainline). + Also tested natively for x86_64. + + (cherry picked from commit db6c4935fae6005d46af413b32aa92f4f6059dce) + +diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c +index 6b83ba91a12cdcd5..2d434ba45a67911e 100644 +--- a/stdio-common/vfprintf.c ++++ b/stdio-common/vfprintf.c +@@ -2101,7 +2101,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') diff --git a/SOURCES/glibc-rh2122501-5.patch b/SOURCES/glibc-rh2122501-5.patch new file mode 100644 index 0000000..f088e5f --- /dev/null +++ b/SOURCES/glibc-rh2122501-5.patch @@ -0,0 +1,81 @@ +commit 8b915921fbf4d32bf68fc3d637413cf96236b3fd +Author: Andreas Schwab +Date: Mon Aug 29 15:05:40 2022 +0200 + + Add test for bug 29530 + + This tests for a bug that was introduced in commit edc1686af0 ("vfprintf: + Reuse work_buffer in group_number") and fixed as a side effect of commit + 6caddd34bd ("Remove most vfprintf width/precision-dependent allocations + (bug 14231, bug 26211)."). + + (cherry picked from commit ca6466e8be32369a658035d69542d47603e58a99) + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index d76b47bd5f932f69..ac61093660ef9063 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -64,7 +64,9 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ + tst-scanf-round \ + tst-renameat2 \ + tst-printf-bz25691 \ +- tst-vfprintf-width-prec-alloc ++ tst-vfprintf-width-prec-alloc \ ++ tst-grouping2 \ ++ # tests + + test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble + +@@ -91,6 +93,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) +diff --git a/stdio-common/tst-grouping2.c b/stdio-common/tst-grouping2.c +new file mode 100644 +index 0000000000000000..3024c942a60e51bf +--- /dev/null ++++ b/stdio-common/tst-grouping2.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index 2565954..3e84499 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.28 %define glibcversion 2.28 -%define glibcrelease 216%{?dist} +%define glibcrelease 219%{?dist} # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -978,6 +978,14 @@ Patch785: glibc-rh1871383-4.patch Patch786: glibc-rh1871383-5.patch Patch787: glibc-rh1871383-6.patch Patch788: glibc-rh1871383-7.patch +Patch789: glibc-rh2122501-1.patch +Patch790: glibc-rh2122501-2.patch +Patch791: glibc-rh2122501-3.patch +Patch792: glibc-rh2122501-4.patch +Patch793: glibc-rh2122501-5.patch +Patch794: glibc-rh2121746-1.patch +Patch795: glibc-rh2121746-2.patch +Patch796: glibc-rh2116938.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2808,6 +2816,16 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Mon Oct 24 2022 Arjun Shankar - 2.28-219 +- Fix -Wstrict-overflow warning when using CMSG_NXTHDR macro (#2116938) + +* Fri Oct 14 2022 DJ Delorie - 2.28-218 +- Fix dlmopen/dlclose/dlmopen sequence and libc initialization (#2121746) + +* Thu Oct 13 2022 Arjun Shankar - 2.28-217 +- Fix memory corruption in printf with thousands separators and large + integer width (#2122501) + * Wed Oct 05 2022 Arjun Shankar - 2.28-216 - Retain .gnu_debuglink section for libc.so.6 (#2115830) - Remove .annobin* symbols from ld.so