From 01d571523d3006cffcdaab185778bb33d4f66882 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 26 May 2026 04:42:42 -0400 Subject: [PATCH] import CS git glibc-2.28-251.el8_10.37 --- SOURCES/glibc-RHEL-162891-1.patch | 77 ++++ SOURCES/glibc-RHEL-162891-2.patch | 369 ++++++++++++++++ SOURCES/glibc-RHEL-162891-3.patch | 700 ++++++++++++++++++++++++++++++ SOURCES/glibc-RHEL-162891-4.patch | 325 ++++++++++++++ SOURCES/glibc-RHEL-168095.patch | 181 ++++++++ SOURCES/glibc-RHEL-173358-1.patch | 224 ++++++++++ SOURCES/glibc-RHEL-173358-2.patch | 304 +++++++++++++ SPECS/glibc.spec | 20 +- 8 files changed, 2199 insertions(+), 1 deletion(-) create mode 100644 SOURCES/glibc-RHEL-162891-1.patch create mode 100644 SOURCES/glibc-RHEL-162891-2.patch create mode 100644 SOURCES/glibc-RHEL-162891-3.patch create mode 100644 SOURCES/glibc-RHEL-162891-4.patch create mode 100644 SOURCES/glibc-RHEL-168095.patch create mode 100644 SOURCES/glibc-RHEL-173358-1.patch create mode 100644 SOURCES/glibc-RHEL-173358-2.patch diff --git a/SOURCES/glibc-RHEL-162891-1.patch b/SOURCES/glibc-RHEL-162891-1.patch new file mode 100644 index 0000000..0ff57d6 --- /dev/null +++ b/SOURCES/glibc-RHEL-162891-1.patch @@ -0,0 +1,77 @@ +commit 7ac6fad987c28dc5e0e7964084260f163179be68 +Author: Florian Weimer +Date: Wed May 15 07:16:47 2019 +0200 + + iconv: Remove public declaration of __gconv_transliterate + + Commit ba7b4d294b01870ce3497971e9d07ee261cdc540 ("Complete the + removal of __gconv_translit_find") added a declaration of the + GLIBC_PRIVATE function, __gconv_transliterate, to the installed + header . It should have been added to the internal + header. + + Reviewed-by: Carlos O'Donell + +diff --git a/iconv/gconv.h b/iconv/gconv.h +index e53f4dba0e3530fc..9dcc509c12c4a343 100644 +--- a/iconv/gconv.h ++++ b/iconv/gconv.h +@@ -142,13 +142,4 @@ typedef struct __gconv_info + __extension__ struct __gconv_step_data __data[0]; + } *__gconv_t; + +-/* Transliteration using the locale's data. */ +-extern int __gconv_transliterate (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char *inbufstart, +- const unsigned char **inbufp, +- const unsigned char *inbufend, +- unsigned char **outbufstart, +- size_t *irreversible); +- + #endif /* gconv.h */ +diff --git a/iconv/gconv_int.h b/iconv/gconv_int.h +index 8067a341b0903e1b..6f908f283720520a 100644 +--- a/iconv/gconv_int.h ++++ b/iconv/gconv_int.h +@@ -276,6 +276,14 @@ extern void __gconv_get_builtin_trans (const char *name, + struct __gconv_step *step) + attribute_hidden; + ++/* Transliteration using the locale's data. */ ++extern int __gconv_transliterate (struct __gconv_step *step, ++ struct __gconv_step_data *step_data, ++ const unsigned char *inbufstart, ++ const unsigned char **inbufp, ++ const unsigned char *inbufend, ++ unsigned char **outbufstart, ++ size_t *irreversible); + libc_hidden_proto (__gconv_transliterate) + + /* If NAME is an codeset alias expand it. */ +diff --git a/iconv/loop.c b/iconv/loop.c +index d571b593c7b8da93..4adbe04ae3698411 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -49,7 +49,7 @@ + + #include + #include +-#include ++#include + #include + #include + #include +diff --git a/iconv/skeleton.c b/iconv/skeleton.c +index fa799305236001c7..9eb0200cc50a0afc 100644 +--- a/iconv/skeleton.c ++++ b/iconv/skeleton.c +@@ -133,7 +133,7 @@ + */ + + #include +-#include ++#include + #include + #define __need_size_t + #define __need_NULL diff --git a/SOURCES/glibc-RHEL-162891-2.patch b/SOURCES/glibc-RHEL-162891-2.patch new file mode 100644 index 0000000..70f10fe --- /dev/null +++ b/SOURCES/glibc-RHEL-162891-2.patch @@ -0,0 +1,369 @@ +commit 5729e0e9af590807df66a3db688008f9547bce9f +Author: Adhemerval Zanella +Date: Fri Feb 10 14:09:10 2023 -0300 + + iconv: Remove _STRING_ARCH_unaligned usage for get/set macros + + And use a packed structure instead. The compiler generates optimized + unaligned code if the architecture supports it. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Wilco Dijkstra + +diff --git a/iconv/gconv_int.h b/iconv/gconv_int.h +index 6f908f283720520a..ebaf3518a95219f0 100644 +--- a/iconv/gconv_int.h ++++ b/iconv/gconv_int.h +@@ -27,6 +27,34 @@ + + __BEGIN_DECLS + ++/* We have to provide support for machines which are not able to handled ++ unaligned memory accesses. Some of the character encodings have ++ representations with a fixed width of 2 or 4 bytes. */ ++#define get16(addr) \ ++({ \ ++ const struct { uint16_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r; \ ++}) ++#define get32(addr) \ ++({ \ ++ const struct { uint32_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r; \ ++}) ++ ++#define put16(addr, val) \ ++do { \ ++ struct { uint16_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r = val; \ ++} while (0) ++#define put32(addr, val) \ ++do { \ ++ struct { uint32_t r; } __attribute__ ((__packed__)) *__ptr \ ++ = (__typeof(__ptr))(addr); \ ++ __ptr->r = val; \ ++} while (0) + + /* Type to represent search path. */ + struct path_elem +diff --git a/iconv/loop.c b/iconv/loop.c +index 4adbe04ae3698411..a9aa201eadc07bd9 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -58,75 +58,10 @@ + #include + #include + +-/* We have to provide support for machines which are not able to handled +- unaligned memory accesses. Some of the character encodings have +- representations with a fixed width of 2 or 4 bytes. But if we cannot +- access unaligned memory we still have to read byte-wise. */ + #undef FCTNAME2 + #if _STRING_ARCH_unaligned || !defined DEFINE_UNALIGNED +-/* We can handle unaligned memory access. */ +-# define get16(addr) *((const uint16_t *) (addr)) +-# define get32(addr) *((const uint32_t *) (addr)) +- +-/* We need no special support for writing values either. */ +-# define put16(addr, val) *((uint16_t *) (addr)) = (val) +-# define put32(addr, val) *((uint32_t *) (addr)) = (val) +- + # define FCTNAME2(name) name + #else +-/* Distinguish between big endian and little endian. */ +-# if __BYTE_ORDER == __LITTLE_ENDIAN +-# define get16(addr) \ +- (((const unsigned char *) (addr))[1] << 8 \ +- | ((const unsigned char *) (addr))[0]) +-# define get32(addr) \ +- (((((const unsigned char *) (addr))[3] << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[0]) +- +-# define put16(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- ((unsigned char *) (addr))[1] = __val >> 8; \ +- (void) 0; }) +-# define put32(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[3] = __val; \ +- (void) 0; }) +-# else +-# define get16(addr) \ +- (((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) +-# define get32(addr) \ +- (((((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[3]) +- +-# define put16(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[1] = __val; \ +- ((unsigned char *) (addr))[0] = __val >> 8; \ +- (void) 0; }) +-# define put32(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[3] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[0] = __val; \ +- (void) 0; }) +-# endif +- + # define FCTNAME2(name) name##_unaligned + #endif + #define FCTNAME(name) FCTNAME2(name) +@@ -350,10 +285,6 @@ FCTNAME (LOOPFCT) (struct __gconv_step *step, + #if !defined DEFINE_UNALIGNED && !_STRING_ARCH_unaligned \ + && MIN_NEEDED_INPUT != 1 && MAX_NEEDED_INPUT % MIN_NEEDED_INPUT == 0 \ + && MIN_NEEDED_OUTPUT != 1 && MAX_NEEDED_OUTPUT % MIN_NEEDED_OUTPUT == 0 +-# undef get16 +-# undef get32 +-# undef put16 +-# undef put32 + # undef unaligned + + # define DEFINE_UNALIGNED +@@ -527,8 +458,4 @@ gconv_btowc (struct __gconv_step *step, unsigned char c) + #undef LOOP_NEED_STATE + #undef LOOP_NEED_FLAGS + #undef LOOP_NEED_DATA +-#undef get16 +-#undef get32 +-#undef put16 +-#undef put32 + #undef unaligned +diff --git a/iconv/skeleton.c b/iconv/skeleton.c +index 9eb0200cc50a0afc..4f20a009d7ffb1db 100644 +--- a/iconv/skeleton.c ++++ b/iconv/skeleton.c +@@ -200,73 +200,6 @@ + #endif + + +-/* Define macros which can access unaligned buffers. These macros are +- supposed to be used only in code outside the inner loops. For the inner +- loops we have other definitions which allow optimized access. */ +-#if _STRING_ARCH_unaligned +-/* We can handle unaligned memory access. */ +-# define get16u(addr) *((const uint16_t *) (addr)) +-# define get32u(addr) *((const uint32_t *) (addr)) +- +-/* We need no special support for writing values either. */ +-# define put16u(addr, val) *((uint16_t *) (addr)) = (val) +-# define put32u(addr, val) *((uint32_t *) (addr)) = (val) +-#else +-/* Distinguish between big endian and little endian. */ +-# if __BYTE_ORDER == __LITTLE_ENDIAN +-# define get16u(addr) \ +- (((const unsigned char *) (addr))[1] << 8 \ +- | ((const unsigned char *) (addr))[0]) +-# define get32u(addr) \ +- (((((const unsigned char *) (addr))[3] << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[0]) +- +-# define put16u(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- ((unsigned char *) (addr))[1] = __val >> 8; \ +- (void) 0; }) +-# define put32u(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[0] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[3] = __val; \ +- (void) 0; }) +-# else +-# define get16u(addr) \ +- (((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) +-# define get32u(addr) \ +- (((((const unsigned char *) (addr))[0] << 8 \ +- | ((const unsigned char *) (addr))[1]) << 8 \ +- | ((const unsigned char *) (addr))[2]) << 8 \ +- | ((const unsigned char *) (addr))[3]) +- +-# define put16u(addr, val) \ +- ({ uint16_t __val = (val); \ +- ((unsigned char *) (addr))[1] = __val; \ +- ((unsigned char *) (addr))[0] = __val >> 8; \ +- (void) 0; }) +-# define put32u(addr, val) \ +- ({ uint32_t __val = (val); \ +- ((unsigned char *) (addr))[3] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[2] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[1] = __val; \ +- __val >>= 8; \ +- ((unsigned char *) (addr))[0] = __val; \ +- (void) 0; }) +-# endif +-#endif +- +- + /* For conversions from a fixed width character set to another fixed width + character set we can define RESET_INPUT_BUFFER in a very fast way. */ + #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE +diff --git a/iconvdata/iso-2022-jp-3.c b/iconvdata/iso-2022-jp-3.c +index a2b33b171e56392a..122e4593c5f6a0b4 100644 +--- a/iconvdata/iso-2022-jp-3.c ++++ b/iconvdata/iso-2022-jp-3.c +@@ -93,7 +93,7 @@ enum + if (__glibc_likely (outbuf + 4 <= outend)) \ + { \ + /* Write out the last character. */ \ +- put32u (outbuf, ch); \ ++ put32 (outbuf, ch); \ + outbuf += 4; \ + data->__statep->__count &= 7; \ + data->__statep->__count |= ASCII_set; \ +diff --git a/iconvdata/unicode.c b/iconvdata/unicode.c +index d0db4239093fd33f..9b17e3a355e7f8a6 100644 +--- a/iconvdata/unicode.c ++++ b/iconvdata/unicode.c +@@ -52,10 +52,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get16u (inptr) == BOM) \ ++ if (get16 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 2; \ +- else if (get16u (inptr) == BOM_OE) \ ++ else if (get16 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 2; \ +@@ -68,7 +68,7 @@ + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM); \ ++ put16 (outbuf, BOM); \ + outbuf += 2; \ + } \ + swap = data->__flags & __GCONV_SWAP; +diff --git a/iconvdata/utf-16.c b/iconvdata/utf-16.c +index ed5f5b2f1f350ddf..6e714928fee8bdca 100644 +--- a/iconvdata/utf-16.c ++++ b/iconvdata/utf-16.c +@@ -56,10 +56,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get16u (inptr) == BOM) \ ++ if (get16 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 2; \ +- else if (get16u (inptr) == BOM_OE) \ ++ else if (get16 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 2; \ +@@ -71,7 +71,7 @@ + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM); \ ++ put16 (outbuf, BOM); \ + outbuf += 2; \ + } \ + } \ +diff --git a/iconvdata/utf-32.c b/iconvdata/utf-32.c +index 7ed0e9bdec672683..dada94f2cca3b752 100644 +--- a/iconvdata/utf-32.c ++++ b/iconvdata/utf-32.c +@@ -52,10 +52,10 @@ + return (inptr == inend \ + ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT); \ + \ +- if (get32u (inptr) == BOM) \ ++ if (get32 (inptr) == BOM) \ + /* Simply ignore the BOM character. */ \ + *inptrp = inptr += 4; \ +- else if (get32u (inptr) == BOM_OE) \ ++ else if (get32 (inptr) == BOM_OE) \ + { \ + data->__flags |= __GCONV_SWAP; \ + *inptrp = inptr += 4; \ +@@ -69,7 +69,7 @@ + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM); \ ++ put32 (outbuf, BOM); \ + outbuf += 4; \ + } \ + else if (__builtin_expect (data->__invocation_counter == 0, 0) \ +diff --git a/sysdeps/s390/utf16-utf32-z9.c b/sysdeps/s390/utf16-utf32-z9.c +index 27086d3e8a1f44ed..4f0d09ab94da59b5 100644 +--- a/sysdeps/s390/utf16-utf32-z9.c ++++ b/sysdeps/s390/utf16-utf32-z9.c +@@ -177,7 +177,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM_UTF16); \ ++ put16 (outbuf, BOM_UTF16); \ + outbuf += 2; \ + } \ + else \ +@@ -186,7 +186,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM_UTF32); \ ++ put32 (outbuf, BOM_UTF32); \ + outbuf += 4; \ + } \ + } +diff --git a/sysdeps/s390/utf8-utf16-z9.c b/sysdeps/s390/utf8-utf16-z9.c +index 409d64c578baa623..8e361890e4ea4ba4 100644 +--- a/sysdeps/s390/utf8-utf16-z9.c ++++ b/sysdeps/s390/utf8-utf16-z9.c +@@ -217,7 +217,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 2 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put16u (outbuf, BOM_UTF16); \ ++ put16 (outbuf, BOM_UTF16); \ + outbuf += 2; \ + } + +diff --git a/sysdeps/s390/utf8-utf32-z9.c b/sysdeps/s390/utf8-utf32-z9.c +index c09d9b5bbde46e4d..422a999bfb690187 100644 +--- a/sysdeps/s390/utf8-utf32-z9.c ++++ b/sysdeps/s390/utf8-utf32-z9.c +@@ -217,7 +217,7 @@ gconv_end (struct __gconv_step *data) + if (__glibc_unlikely (outbuf + 4 > outend)) \ + return __GCONV_FULL_OUTPUT; \ + \ +- put32u (outbuf, BOM); \ ++ put32 (outbuf, BOM); \ + outbuf += 4; \ + } + diff --git a/SOURCES/glibc-RHEL-162891-3.patch b/SOURCES/glibc-RHEL-162891-3.patch new file mode 100644 index 0000000..6e28f9a --- /dev/null +++ b/SOURCES/glibc-RHEL-162891-3.patch @@ -0,0 +1,700 @@ +commit 3e20ddade31d9c392d8ccf7ec902172f4bb01c2b +Author: Adhemerval Zanella +Date: Fri Feb 10 16:37:36 2023 -0300 + + iconv: Remove _STRING_ARCH_unaligned usage + + Use put/get macros __builtin_bswap32 instead. It allows to remove + the unaligned routines, the compiler will generate unaligned access + if the ABI allows it. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Wilco Dijkstra + +Conflicts: + iconv/gconv_simple.c + (fixup context) + iconv/loop.c + (fixup context) + iconv/skeleton.c + (fixup context) + +diff --git a/iconv/gconv_simple.c b/iconv/gconv_simple.c +index 506c92caf228d61f..d95a5e75e3cd80f1 100644 +--- a/iconv/gconv_simple.c ++++ b/iconv/gconv_simple.c +@@ -87,68 +87,22 @@ internal_ucs4_loop (struct __gconv_step *step, + #if __BYTE_ORDER == __LITTLE_ENDIAN + /* Sigh, we have to do some real work. */ + size_t cnt; +- uint32_t *outptr32 = (uint32_t *) outptr; +- +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) +- *outptr32++ = bswap_32 (*(const uint32_t *) inptr); +- +- *inptrp = inptr; +- *outptrp = (unsigned char *) outptr32; +-#elif __BYTE_ORDER == __BIG_ENDIAN +- /* Simply copy the data. */ +- *inptrp = inptr + n_convert * 4; +- *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-#else +-# error "This endianess is not supported." +-#endif +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*outptrp + 4 > outend) +- result = __GCONV_FULL_OUTPUT; +- else +- result = __GCONV_INCOMPLETE_INPUT; +- +- return result; +-} +- +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-internal_ucs4_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, unsigned char *outend, +- size_t *irreversible) +-{ +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- size_t n_convert = MIN (inend - inptr, outend - outptr) / 4; +- int result; +- +-# if __BYTE_ORDER == __LITTLE_ENDIAN +- /* Sigh, we have to do some real work. */ +- size_t cnt; + + for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) + { +- outptr[0] = inptr[3]; +- outptr[1] = inptr[2]; +- outptr[2] = inptr[1]; +- outptr[3] = inptr[0]; ++ uint32_t val = get32 (inptr); ++ put32 (outptr, __builtin_bswap32 (val)); + } + + *inptrp = inptr; + *outptrp = outptr; +-# elif __BYTE_ORDER == __BIG_ENDIAN ++#elif __BYTE_ORDER == __BIG_ENDIAN + /* Simply copy the data. */ + *inptrp = inptr + n_convert * 4; + *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-# else +-# error "This endianess is not supported." +-# endif ++#else ++# error "This endianess is not supported." ++#endif + + /* Determine the status. */ + if (*inptrp == inend) +@@ -160,8 +114,6 @@ internal_ucs4_loop_unaligned (struct __gconv_step *step, + + return result; + } +-#endif +- + + static inline int + __attribute ((always_inline)) +@@ -243,12 +195,9 @@ ucs4_internal_loop (struct __gconv_step *step, + + for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) + { +- uint32_t inval; +- ++ uint32_t inval = get32 (inptr); + #if __BYTE_ORDER == __LITTLE_ENDIAN +- inval = bswap_32 (*(const uint32_t *) inptr); +-#else +- inval = *(const uint32_t *) inptr; ++ inval = __builtin_bswap32 (inval); + #endif + + if (__glibc_unlikely (inval > 0x7fffffff)) +@@ -273,7 +222,7 @@ ucs4_internal_loop (struct __gconv_step *step, + return __GCONV_ILLEGAL_INPUT; + } + +- *((uint32_t *) outptr) = inval; ++ put32 (outptr, inval); + outptr += sizeof (uint32_t); + } + +@@ -291,77 +240,6 @@ ucs4_internal_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-ucs4_internal_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, unsigned char *outend, +- size_t *irreversible) +-{ +- 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) +- { +- if (__glibc_unlikely (inptr[0] > 0x80)) +- { +- /* The value is too large. We don't try transliteration here since +- this is not an error because of the lack of possibilities to +- represent the result. This is a genuine bug in the input since +- UCS4 does not allow such values. */ +- if (irreversible == NULL) +- /* We are transliterating, don't try to correct anything. */ +- return __GCONV_ILLEGAL_INPUT; +- +- if (flags & __GCONV_IGNORE_ERRORS) +- { +- /* Just ignore this character. */ +- ++*irreversible; +- continue; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- return __GCONV_ILLEGAL_INPUT; +- } +- +-# if __BYTE_ORDER == __LITTLE_ENDIAN +- outptr[3] = inptr[0]; +- outptr[2] = inptr[1]; +- outptr[1] = inptr[2]; +- outptr[0] = inptr[3]; +-# else +- outptr[0] = inptr[0]; +- outptr[1] = inptr[1]; +- outptr[2] = inptr[2]; +- outptr[3] = inptr[3]; +-# endif +- outptr += 4; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*outptrp + 4 > outend) +- result = __GCONV_FULL_OUTPUT; +- else +- result = __GCONV_INCOMPLETE_INPUT; +- +- return result; +-} +-#endif +- +- + static inline int + __attribute ((always_inline)) + ucs4_internal_loop_single (struct __gconv_step *step, +@@ -454,11 +332,12 @@ internal_ucs4le_loop (struct __gconv_step *step, + #if __BYTE_ORDER == __BIG_ENDIAN + /* Sigh, we have to do some real work. */ + size_t cnt; +- uint32_t *outptr32 = (uint32_t *) outptr; + +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) +- *outptr32++ = bswap_32 (*(const uint32_t *) inptr); +- outptr = (unsigned char *) outptr32; ++ for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) ++ { ++ uint32_t val = get32 (inptr); ++ put32 (outptr, __builtin_bswap32 (val)); ++ } + + *inptrp = inptr; + *outptrp = outptr; +@@ -481,59 +360,6 @@ internal_ucs4le_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-internal_ucs4le_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, unsigned char *outend, +- size_t *irreversible) +-{ +- const unsigned char *inptr = *inptrp; +- unsigned char *outptr = *outptrp; +- size_t n_convert = MIN (inend - inptr, outend - outptr) / 4; +- int result; +- +-# if __BYTE_ORDER == __BIG_ENDIAN +- /* Sigh, we have to do some real work. */ +- size_t cnt; +- +- for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4, outptr += 4) +- { +- outptr[0] = inptr[3]; +- outptr[1] = inptr[2]; +- outptr[2] = inptr[1]; +- outptr[3] = inptr[0]; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +-# elif __BYTE_ORDER == __LITTLE_ENDIAN +- /* Simply copy the data. */ +- *inptrp = inptr + n_convert * 4; +- *outptrp = __mempcpy (outptr, inptr, n_convert * 4); +-# else +-# error "This endianess is not supported." +-# endif +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*inptrp + 4 > inend) +- result = __GCONV_INCOMPLETE_INPUT; +- else +- { +- assert (*outptrp + 4 > outend); +- result = __GCONV_FULL_OUTPUT; +- } +- +- return result; +-} +-#endif +- +- + static inline int + __attribute ((always_inline)) + internal_ucs4le_loop_single (struct __gconv_step *step, +@@ -613,12 +439,9 @@ ucs4le_internal_loop (struct __gconv_step *step, + + for (cnt = 0; cnt < n_convert; ++cnt, inptr += 4) + { +- uint32_t inval; +- ++ uint32_t inval = get32 (inptr); + #if __BYTE_ORDER == __BIG_ENDIAN +- inval = bswap_32 (*(const uint32_t *) inptr); +-#else +- inval = *(const uint32_t *) inptr; ++ inval = __builtin_bswap32 (inval); + #endif + + if (__glibc_unlikely (inval > 0x7fffffff)) +@@ -643,7 +466,7 @@ ucs4le_internal_loop (struct __gconv_step *step, + return __GCONV_ILLEGAL_INPUT; + } + +- *((uint32_t *) outptr) = inval; ++ put32 (outptr, inval); + outptr += sizeof (uint32_t); + } + +@@ -664,81 +487,6 @@ ucs4le_internal_loop (struct __gconv_step *step, + return result; + } + +-#if !_STRING_ARCH_unaligned +-static inline int +-__attribute ((always_inline)) +-ucs4le_internal_loop_unaligned (struct __gconv_step *step, +- struct __gconv_step_data *step_data, +- const unsigned char **inptrp, +- const unsigned char *inend, +- unsigned char **outptrp, unsigned char *outend, +- size_t *irreversible) +-{ +- 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) +- { +- if (__glibc_unlikely (inptr[3] > 0x80)) +- { +- /* The value is too large. We don't try transliteration here since +- this is not an error because of the lack of possibilities to +- represent the result. This is a genuine bug in the input since +- UCS4 does not allow such values. */ +- if (irreversible == NULL) +- /* We are transliterating, don't try to correct anything. */ +- return __GCONV_ILLEGAL_INPUT; +- +- if (flags & __GCONV_IGNORE_ERRORS) +- { +- /* Just ignore this character. */ +- ++*irreversible; +- continue; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- return __GCONV_ILLEGAL_INPUT; +- } +- +-# if __BYTE_ORDER == __BIG_ENDIAN +- outptr[3] = inptr[0]; +- outptr[2] = inptr[1]; +- outptr[1] = inptr[2]; +- outptr[0] = inptr[3]; +-# else +- outptr[0] = inptr[0]; +- outptr[1] = inptr[1]; +- outptr[2] = inptr[2]; +- outptr[3] = inptr[3]; +-# endif +- +- outptr += 4; +- } +- +- *inptrp = inptr; +- *outptrp = outptr; +- +- /* Determine the status. */ +- if (*inptrp == inend) +- result = __GCONV_EMPTY_INPUT; +- else if (*inptrp + 4 > inend) +- result = __GCONV_INCOMPLETE_INPUT; +- else +- { +- assert (*outptrp + 4 > outend); +- result = __GCONV_FULL_OUTPUT; +- } +- +- return result; +-} +-#endif +- +- + static inline int + __attribute ((always_inline)) + ucs4le_internal_loop_single (struct __gconv_step *step, +diff --git a/iconv/loop.c b/iconv/loop.c +index a9aa201eadc07bd9..9ef2657f4cbe051c 100644 +--- a/iconv/loop.c ++++ b/iconv/loop.c +@@ -59,12 +59,7 @@ + #include + + #undef FCTNAME2 +-#if _STRING_ARCH_unaligned || !defined DEFINE_UNALIGNED +-# define FCTNAME2(name) name +-#else +-# define FCTNAME2(name) name##_unaligned +-#endif +-#define FCTNAME(name) FCTNAME2(name) ++#define FCTNAME(name) name + + + /* We need at least one byte for the next round. */ +@@ -280,20 +275,9 @@ FCTNAME (LOOPFCT) (struct __gconv_step *step, + } + + +-/* Include the file a second time to define the function to handle +- unaligned access. */ +-#if !defined DEFINE_UNALIGNED && !_STRING_ARCH_unaligned \ +- && MIN_NEEDED_INPUT != 1 && MAX_NEEDED_INPUT % MIN_NEEDED_INPUT == 0 \ +- && MIN_NEEDED_OUTPUT != 1 && MAX_NEEDED_OUTPUT % MIN_NEEDED_OUTPUT == 0 +-# undef unaligned +- +-# define DEFINE_UNALIGNED +-# include "loop.c" +-# undef DEFINE_UNALIGNED +-#else +-# if MAX_NEEDED_INPUT > 1 +-# define SINGLE(fct) SINGLE2 (fct) +-# define SINGLE2(fct) fct##_single ++#if MAX_NEEDED_INPUT > 1 ++# define SINGLE(fct) SINGLE2 (fct) ++# define SINGLE2(fct) fct##_single + static inline int + __attribute ((always_inline)) + SINGLE(LOOPFCT) (struct __gconv_step *step, +@@ -303,37 +287,37 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + size_t *irreversible EXTRA_LOOP_DECLS) + { + mbstate_t *state = step_data->__statep; +-# ifdef LOOP_NEED_FLAGS ++# ifdef LOOP_NEED_FLAGS + int flags = step_data->__flags; +-# endif +-# ifdef LOOP_NEED_DATA ++# endif ++# ifdef LOOP_NEED_DATA + void *data = step->__data; +-# endif ++# endif + int result = __GCONV_OK; + unsigned char bytebuf[MAX_NEEDED_INPUT]; + const unsigned char *inptr = *inptrp; + unsigned char *outptr = *outptrp; + size_t inlen; + +-# ifdef INIT_PARAMS ++# ifdef INIT_PARAMS + INIT_PARAMS; +-# endif ++# endif + +-# ifdef UNPACK_BYTES ++# ifdef UNPACK_BYTES + UNPACK_BYTES +-# else ++# else + /* Add the bytes from the state to the input buffer. */ + assert ((state->__count & 7) <= sizeof (state->__value)); + for (inlen = 0; inlen < (size_t) (state->__count & 7); ++inlen) + bytebuf[inlen] = state->__value.__wchb[inlen]; +-# endif ++# endif + + /* Are there enough bytes in the input buffer? */ + if (MIN_NEEDED_INPUT > 1 + && __builtin_expect (inptr + (MIN_NEEDED_INPUT - inlen) > inend, 0)) + { + *inptrp = inend; +-# ifdef STORE_REST ++# ifdef STORE_REST + + /* Building with -O3 GCC emits a `array subscript is above array + bounds' warning. GCC BZ #64739 has been opened for this. */ +@@ -348,12 +332,12 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + inend = &bytebuf[inlen]; + + STORE_REST +-# else ++# else + /* We don't have enough input for another complete input + character. */ + while (inptr < inend) + state->__value.__wchb[inlen++] = *inptr++; +-# endif ++# endif + + return __GCONV_INCOMPLETE_INPUT; + } +@@ -393,11 +377,11 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + result = __GCONV_OK; + + /* Clear the state buffer. */ +-# ifdef CLEAR_STATE ++# ifdef CLEAR_STATE + CLEAR_STATE; +-# else ++# else + state->__count &= ~7; +-# endif ++# endif + } + else if (result == __GCONV_INCOMPLETE_INPUT) + { +@@ -406,11 +390,11 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + assert (inend != &bytebuf[MAX_NEEDED_INPUT]); + + *inptrp += inend - bytebuf - (state->__count & 7); +-# ifdef STORE_REST ++# ifdef STORE_REST + inptrp = &inptr; + + STORE_REST +-# else ++# else + /* We don't have enough input for another complete input + character. */ + assert (inend - inptr > (state->__count & ~7)); +@@ -419,14 +403,13 @@ SINGLE(LOOPFCT) (struct __gconv_step *step, + inlen = 0; + while (inptr < inend) + state->__value.__wchb[inlen++] = *inptr++; +-# endif ++# endif + } + + return result; + } +-# undef SINGLE +-# undef SINGLE2 +-# endif ++# undef SINGLE ++# undef SINGLE2 + + + # ifdef ONEBYTE_BODY +@@ -458,4 +441,3 @@ gconv_btowc (struct __gconv_step *step, unsigned char c) + #undef LOOP_NEED_STATE + #undef LOOP_NEED_FLAGS + #undef LOOP_NEED_DATA +-#undef unaligned +diff --git a/iconv/skeleton.c b/iconv/skeleton.c +index 4f20a009d7ffb1db..bf2855c84f0270cd 100644 +--- a/iconv/skeleton.c ++++ b/iconv/skeleton.c +@@ -446,33 +446,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + size_t lirreversible = 0; + size_t *lirreversiblep = irreversible ? &lirreversible : NULL; + +- /* The following assumes that encodings, which have a variable length +- what might unalign a buffer even though it is an aligned in the +- beginning, either don't have the minimal number of bytes as a divisor +- of the maximum length or have a minimum length of 1. This is true +- for all known and supported encodings. +- We use && instead of || to combine the subexpression for the FROM +- encoding and for the TO encoding, because usually one of them is +- INTERNAL, for which the subexpression evaluates to 1, but INTERNAL +- buffers are always aligned correctly. */ +-#define POSSIBLY_UNALIGNED \ +- (!_STRING_ARCH_unaligned \ +- && (((FROM_LOOP_MIN_NEEDED_FROM != 1 \ +- && FROM_LOOP_MAX_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_FROM == 0) \ +- && (FROM_LOOP_MIN_NEEDED_TO != 1 \ +- && FROM_LOOP_MAX_NEEDED_TO % FROM_LOOP_MIN_NEEDED_TO == 0)) \ +- || ((TO_LOOP_MIN_NEEDED_FROM != 1 \ +- && TO_LOOP_MAX_NEEDED_FROM % TO_LOOP_MIN_NEEDED_FROM == 0) \ +- && (TO_LOOP_MIN_NEEDED_TO != 1 \ +- && TO_LOOP_MAX_NEEDED_TO % TO_LOOP_MIN_NEEDED_TO == 0)))) +-#if POSSIBLY_UNALIGNED +- int unaligned; +-# define GEN_unaligned(name) GEN_unaligned2 (name) +-# define GEN_unaligned2(name) name##_unaligned +-#else +-# define unaligned 0 +-#endif +- + #ifdef PREPARE_LOOP + PREPARE_LOOP + #endif +@@ -512,18 +485,6 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + } + #endif + +-#if POSSIBLY_UNALIGNED +- unaligned = +- ((FROM_DIRECTION +- && ((uintptr_t) inptr % FROM_LOOP_MIN_NEEDED_FROM != 0 +- || ((data->__flags & __GCONV_IS_LAST) +- && (uintptr_t) outbuf % FROM_LOOP_MIN_NEEDED_TO != 0))) +- || (!FROM_DIRECTION +- && (((data->__flags & __GCONV_IS_LAST) +- && (uintptr_t) outbuf % TO_LOOP_MIN_NEEDED_TO != 0) +- || (uintptr_t) inptr % TO_LOOP_MIN_NEEDED_FROM != 0))); +-#endif +- + while (1) + { + /* Remember the start value for this round. */ +@@ -535,34 +496,14 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + SAVE_RESET_STATE (1); + #endif + +- if (__glibc_likely (!unaligned)) +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend, +- lirreversiblep EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend, +- lirreversiblep EXTRA_LOOP_ARGS); +- } +-#if POSSIBLY_UNALIGNED ++ if (FROM_DIRECTION) ++ /* Run the conversion loop. */ ++ status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend, ++ lirreversiblep EXTRA_LOOP_ARGS); + else +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- status = GEN_unaligned (FROM_LOOP) (step, data, inptrp, inend, +- &outbuf, outend, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- status = GEN_unaligned (TO_LOOP) (step, data, inptrp, inend, +- &outbuf, outend, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-#endif ++ /* Run the conversion loop. */ ++ status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend, ++ lirreversiblep EXTRA_LOOP_ARGS); + + /* If we were called as part of an error handling module we + don't do anything else here. */ +@@ -619,41 +560,14 @@ FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data, + SAVE_RESET_STATE (0); + # endif + +- if (__glibc_likely (!unaligned)) +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- nstatus = FROM_LOOP (step, data, inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- nstatus = TO_LOOP (step, data, inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-# if POSSIBLY_UNALIGNED ++ if (FROM_DIRECTION) ++ /* Run the conversion loop. */ ++ nstatus = FROM_LOOP (step, data, inptrp, inend, ++ &outbuf, outerr, lirreversiblep EXTRA_LOOP_ARGS); + else +- { +- if (FROM_DIRECTION) +- /* Run the conversion loop. */ +- nstatus = GEN_unaligned (FROM_LOOP) (step, data, +- inptrp, inend, +- &outbuf, +- outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- else +- /* Run the conversion loop. */ +- nstatus = GEN_unaligned (TO_LOOP) (step, data, +- inptrp, inend, +- &outbuf, outerr, +- lirreversiblep +- EXTRA_LOOP_ARGS); +- } +-# endif ++ /* Run the conversion loop. */ ++ nstatus = TO_LOOP (step, data, inptrp, inend, &outbuf, ++ outerr, lirreversiblep EXTRA_LOOP_ARGS); + + /* We must run out of output buffer space in this + rerun. */ diff --git a/SOURCES/glibc-RHEL-162891-4.patch b/SOURCES/glibc-RHEL-162891-4.patch new file mode 100644 index 0000000..fc70d8f --- /dev/null +++ b/SOURCES/glibc-RHEL-162891-4.patch @@ -0,0 +1,325 @@ +commit d6f08d1cf027f4eb2ba289a6cc66853722d4badc +Author: Florian Weimer +Date: Thu Apr 16 19:13:43 2026 +0200 + + Use pending character state in IBM1390, IBM1399 character sets (CVE-2026-4046) + + Follow the example in iso-2022-jp-3.c and use the __count state + variable to store the pending character. This avoids restarting + the conversion if the output buffer ends between two 4-byte UCS-4 + code points, so that the assert reported in the bug can no longer + happen. + + Even though the fix is applied to ibm1364.c, the change is only + effective for the two HAS_COMBINED codecs for IBM1390, IBM1399. + + The test case was mostly auto-generated using + claude-4.6-opus-high-thinking, and composer-2-fast shows up in the + log as well. During review, gpt-5.4-xhigh flagged that the original + version of the test case was not exercising the new character + flush logic. + + This fixes bug 33980. + + Assisted-by: LLM + Reviewed-by: Carlos O'Donell + +Conflicts: + iconvdata/Makefile + (fixup context) + +diff --git a/iconvdata/Makefile b/iconvdata/Makefile +index c959758a90ed954f..c4a7159488bbfdf2 100644 +--- a/iconvdata/Makefile ++++ b/iconvdata/Makefile +@@ -75,7 +75,7 @@ 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-iconv13 bug-iconv14 \ +- bug-iconv15 tst-iconv-iso-2022-cn-ext ++ bug-iconv15 tst-iconv-iso-2022-cn-ext tst-bug33980 + ifeq ($(have-thread-library),yes) + tests += bug-iconv3 + endif +@@ -327,6 +327,8 @@ $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \ + $(addprefix $(objpfx),$(modules.so)) + $(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \ + $(addprefix $(objpfx),$(modules.so)) ++$(objpfx)tst-bug33980.out: $(addprefix $(objpfx), $(gconv-modules)) \ ++ $(addprefix $(objpfx),$(modules.so)) + + $(objpfx)iconv-test.out: run-iconv-test.sh \ + $(addprefix $(objpfx), $(gconv-modules)) \ +diff --git a/iconvdata/ibm1364.c b/iconvdata/ibm1364.c +index ecc3f8ddddbdbc8c..5edd9d01262a1c0d 100644 +--- a/iconvdata/ibm1364.c ++++ b/iconvdata/ibm1364.c +@@ -68,12 +68,29 @@ + + /* Since this is a stateful encoding we have to provide code which resets + the output state to the initial state. This has to be done during the +- flushing. */ ++ flushing. For the to-internal direction (FROM_DIRECTION is true), ++ there may be a pending character that needs flushing. */ + #define EMIT_SHIFT_TO_INIT \ + if ((data->__statep->__count & ~7) != sb) \ + { \ + if (FROM_DIRECTION) \ +- data->__statep->__count &= 7; \ ++ { \ ++ uint32_t ch = data->__statep->__count >> 7; \ ++ if (__glibc_unlikely (ch != 0)) \ ++ { \ ++ if (__glibc_unlikely (outend - outbuf < 4)) \ ++ status = __GCONV_FULL_OUTPUT; \ ++ else \ ++ { \ ++ put32 (outbuf, ch); \ ++ outbuf += 4; \ ++ /* Clear character and db bit. */ \ ++ data->__statep->__count &= 7; \ ++ } \ ++ } \ ++ else \ ++ data->__statep->__count &= 7; \ ++ } \ + else \ + { \ + /* We are not in the initial state. To switch back we have \ +@@ -100,11 +117,13 @@ + *curcsp = save_curcs + + +-/* Current codeset type. */ ++/* Current codeset type. The bit is stored in the __count variable of ++ the conversion state. If the db bit is set, bit 7 and above store ++ a pending UCS-4 code point if non-zero. */ + enum + { +- sb = 0, +- db = 64 ++ sb = 0, /* Single byte mode. */ ++ db = 64 /* Double byte mode. */ + }; + + +@@ -120,21 +139,29 @@ enum + } \ + else \ + { \ +- /* This is a combined character. Make sure we have room. */ \ +- if (__glibc_unlikely (outptr + 8 > outend)) \ +- { \ +- result = __GCONV_FULL_OUTPUT; \ +- break; \ +- } \ +- \ + const struct divide *cmbp \ + = &DB_TO_UCS4_COMB[ch - __TO_UCS4_COMBINED_MIN]; \ + assert (cmbp->res1 != 0 && cmbp->res2 != 0); \ + \ + put32 (outptr, cmbp->res1); \ + outptr += 4; \ +- put32 (outptr, cmbp->res2); \ +- outptr += 4; \ ++ \ ++ /* See whether we have room for the second character. */ \ ++ if (outend - outptr >= 4) \ ++ { \ ++ put32 (outptr, cmbp->res2); \ ++ outptr += 4; \ ++ } \ ++ else \ ++ { \ ++ /* Otherwise store only the first character now, and \ ++ put the second one into the queue. */ \ ++ curcs |= cmbp->res2 << 7; \ ++ inptr += 2; \ ++ /* Tell the caller why we terminate the loop. */ \ ++ result = __GCONV_FULL_OUTPUT; \ ++ break; \ ++ } \ + } \ + } + #else +@@ -154,7 +181,20 @@ enum + #define LOOPFCT FROM_LOOP + #define BODY \ + { \ +- uint32_t ch = *inptr; \ ++ uint32_t ch; \ ++ \ ++ ch = curcs >> 7; \ ++ if (__glibc_unlikely (ch != 0)) \ ++ { \ ++ put32 (outptr, ch); \ ++ outptr += 4; \ ++ /* Remove the pending character, but preserve state bits. */ \ ++ curcs &= (1 << 7) - 1; \ ++ continue; \ ++ } \ ++ \ ++ /* Otherwise read the next input byte. */ \ ++ ch = *inptr; \ + \ + if (__builtin_expect (ch, 0) == SO) \ + { \ +diff --git a/iconvdata/tst-bug33980.c b/iconvdata/tst-bug33980.c +new file mode 100644 +index 0000000000000000..c9693e0efebe4eae +--- /dev/null ++++ b/iconvdata/tst-bug33980.c +@@ -0,0 +1,153 @@ ++/* Test for bug 33980: combining characters in IBM1390/IBM1399. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes ++ starting at OUTBUF. OUTBUF should be right before an unmapped page ++ so that writing past the end will fault. Skip SHIFT bytes at the ++ start of the input and output, to exercise different buffer ++ alignment. TRUNCATE indicates skipped bytes at the end of ++ input (0 and 1 a valid). */ ++static void ++test_one (const char *encoding, unsigned int shift, unsigned int truncate, ++ char *outbuf, size_t outbufsize) ++{ ++ /* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two ++ Unicode code points when translated. */ ++ static char input[] = ++ { ++ /* 8 letters X. */ ++ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, ++ /* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */ ++ 0x0e, 0xec, 0xb5, 0x0f ++ }; ++ ++ /* Expected output after UTF-8 conversion. */ ++ static char expected[] = ++ { ++ 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', ++ /* U+304B (HIRAGANA LETTER KA). */ ++ 0xe3, 0x81, 0x8b, ++ /* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */ ++ 0xe3, 0x82, 0x9a ++ }; ++ ++ iconv_t cd = iconv_open ("UTF-8", encoding); ++ TEST_VERIFY_EXIT (cd != (iconv_t) -1); ++ ++ char result_storage[64]; ++ struct alloc_buffer result_buf ++ = alloc_buffer_create (result_storage, sizeof (result_storage)); ++ ++ char *inptr = &input[shift]; ++ size_t inleft = sizeof (input) - shift - truncate; ++ ++ while (inleft > 0) ++ { ++ char *outptr = outbuf; ++ size_t outleft = outbufsize; ++ size_t inleft_before = inleft; ++ ++ size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft); ++ size_t produced = outptr - outbuf; ++ alloc_buffer_copy_bytes (&result_buf, outbuf, produced); ++ ++ if (ret == (size_t) -1 && errno == E2BIG) ++ { ++ if (produced == 0 && inleft == inleft_before) ++ { ++ /* Output buffer too small to make progress. This is ++ expected for very small output buffer sizes. */ ++ TEST_VERIFY_EXIT (outbufsize < 3); ++ break; ++ } ++ continue; ++ } ++ if (ret == (size_t) -1) ++ FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize); ++ break; ++ } ++ ++ /* Flush any pending state (e.g. a buffered combined character). ++ With outbufsize < 3, we could not store the first character, so ++ the second character did not become pending, and there is nothing ++ to flush. */ ++ { ++ char *outptr = outbuf; ++ size_t outleft = outbufsize; ++ ++ size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft); ++ TEST_VERIFY_EXIT (ret == 0); ++ size_t produced = outptr - outbuf; ++ alloc_buffer_copy_bytes (&result_buf, outbuf, produced); ++ ++ /* Second flush does not provide more data. */ ++ outptr = outbuf; ++ outleft = outbufsize; ++ ret = iconv (cd, NULL, NULL, &outptr, &outleft); ++ TEST_VERIFY_EXIT (ret == 0); ++ TEST_VERIFY (outptr == outbuf); ++ } ++ ++ TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf)); ++ size_t result_used ++ = sizeof (result_storage) - alloc_buffer_size (&result_buf); ++ ++ if (outbufsize >= 3) ++ { ++ TEST_COMPARE (inleft, 0); ++ TEST_COMPARE (result_used, sizeof (expected) - shift); ++ TEST_COMPARE_BLOB (result_storage, result_used, ++ &expected[shift], sizeof (expected) - shift); ++ } ++ else ++ /* If the buffer is too small, only the leading X could be converted. */ ++ TEST_COMPARE (result_used, 8 - shift); ++ ++ TEST_VERIFY_EXIT (iconv_close (cd) == 0); ++} ++ ++static int ++do_test (void) ++{ ++ struct support_next_to_fault ntf ++ = support_next_to_fault_allocate (8); ++ ++ for (int shift = 0; shift <= 8; ++shift) ++ for (int truncate = 0; truncate < 2; ++truncate) ++ for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++) ++ { ++ char *outbuf = ntf.buffer + ntf.length - outbufsize; ++ test_one ("IBM1390", shift, truncate, outbuf, outbufsize); ++ test_one ("IBM1399", shift, truncate, outbuf, outbufsize); ++ } ++ ++ support_next_to_fault_free (&ntf); ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-168095.patch b/SOURCES/glibc-RHEL-168095.patch new file mode 100644 index 0000000..8954c25 --- /dev/null +++ b/SOURCES/glibc-RHEL-168095.patch @@ -0,0 +1,181 @@ +commit d8997716a1ca22cf038eac86ed286830ba9818cc +Author: DJ Delorie +Date: Wed Apr 1 17:52:25 2026 -0400 + + nss: fix __get_default_domain logic + + Fix logic bug in __nss_get_default_domain that prevents + proper initialization. + + Because this function is not exposed, the test case must link + against the object directly. + + Bug origin commit: 64d1e08ea822bf47cb2796ad0f727136227f983c + + Co-authored-by: Florian Weimer + Reviewed-by: Frédéric Bérat + +Conflicts: + nss/Makefile + (fixup context) + +diff --git a/nss/Makefile b/nss/Makefile +index 05fcadf60fd0d771..da0f0fdc0f70ff69 100644 +--- a/nss/Makefile ++++ b/nss/Makefile +@@ -55,6 +55,7 @@ extra-objs += $(makedb-modules:=.o) + tests-static = tst-field + tests-internal = tst-field + tests = test-netdb test-digits-dots tst-nss-getpwent bug17079 \ ++ tst-default-domain \ + tst-nss-test1 \ + tst-nss-test2 \ + tst-nss-test3 \ +@@ -212,3 +213,5 @@ $(objpfx)tst-nss-files-hosts-multi: $(libdl) + $(objpfx)tst-nss-files-hosts-getent: $(libdl) + $(objpfx)tst-nss-files-alias-leak: $(libdl) + $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)libnss_files.so ++ ++$(objpfx)tst-default-domain: $(objpfx)nisdomain.os +diff --git a/nss/nss_compat/nisdomain.c b/nss/nss_compat/nisdomain.c +index facce925b36d9418..def681af8f2363fb 100644 +--- a/nss/nss_compat/nisdomain.c ++++ b/nss/nss_compat/nisdomain.c +@@ -36,7 +36,7 @@ __nss_get_default_domain (char **outdomain) + + __libc_lock_lock (domainname_lock); + +- if (domainname[0] != '\0') ++ if (domainname[0] == '\0') + { + if (getdomainname (domainname, MAXDOMAINNAMELEN) < 0) + result = errno; +diff --git a/nss/tst-default-domain.c b/nss/tst-default-domain.c +new file mode 100644 +index 0000000000000000..2fa9153d8802d4c3 +--- /dev/null ++++ b/nss/tst-default-domain.c +@@ -0,0 +1,123 @@ ++/* Basic test of __nss_get_default_domain ++ 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 ++ . */ ++ ++#include ++#include ++#include ++ ++#include "nss_compat/nisdomain.h" ++ ++#include ++#include ++#include ++#include ++ ++char unset_domain[] = "unset_domain"; ++char new_domain[] = "new_domain"; ++ ++/* This function checks the __nss_get_default_domain() function in ++ nss_compat/nssdomain.c. Because this is an internal function to ++ libnss_compat.so, the Makefile will link that object to this test ++ case directly. */ ++ ++static int ++do_test (void) ++{ ++ char *domain_name; ++ char buf[1024]; ++ ++ /* We need to be in a network namespace so we can change the domain ++ name without interfering with the host system. */ ++ support_become_root (); ++ support_enter_network_namespace (); ++ if (!support_in_uts_namespace ()) ++ return EXIT_UNSUPPORTED; ++ ++ /* First pass: set an empty domain and make sure it's returned ++ correctly. This should not be cached. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname ("", 0) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, ""); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ /* Make sure the correct domain name is returned. */ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, ""); ++ ++ /* Second pass: set a non-empty domain and make sure it's returned ++ correctly. This works because the empty domain is not ++ cached. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname (new_domain, strlen (new_domain)) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, new_domain); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ /* Make sure the correct domain name is returned. */ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ /* The function caches the name, so check it twice. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ /* Third pass: set an empty domain again but expect the cached ++ value. */ ++ ++ /* Set the domain name to a known value. */ ++ TEST_VERIFY (setdomainname ("", 0) == 0); ++ ++ /* Make sure it got set. */ ++ TEST_VERIFY (getdomainname (buf, sizeof(buf)) == 0); ++ TEST_COMPARE_STRING (buf, ""); ++ ++ /* Set this to a known "unknown" value so we can detect if it's not ++ changed. */ ++ domain_name = unset_domain; ++ ++ /* This is the function we're testing. */ ++ TEST_VERIFY (__nss_get_default_domain (& domain_name) == 0); ++ ++ TEST_VERIFY (domain_name != NULL); ++ TEST_COMPARE_STRING (domain_name, new_domain); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-173358-1.patch b/SOURCES/glibc-RHEL-173358-1.patch new file mode 100644 index 0000000..548a9cc --- /dev/null +++ b/SOURCES/glibc-RHEL-173358-1.patch @@ -0,0 +1,224 @@ +Test-only backport of test for CVE-2026-4437 that does not affect +glibc-2.28. Test extracted from the following upstream commit: + +commit 5663ab0b833df187b15e7bb4b18173e22beb8bd1 +Author: Carlos O'Donell +Date: Fri Mar 20 16:43:33 2026 -0400 + + resolv: Count records correctly (CVE-2026-4437) + + The answer section boundary was previously ignored, and the code in + getanswer_ptr would iterate past the last resource record, but not + beyond the end of the returned data. This could lead to subsequent data + being interpreted as answer records, thus violating the DNS + specification. Such resource records could be maliciously crafted and + hidden from other tooling, but processed by the glibc stub resolver and + acted upon by the application. While we trust the data returned by the + configured recursive resolvers, we should not trust its format and + should validate it as required. It is a security issue to incorrectly + process the DNS protocol. + + A regression test is added for response section crossing. + + No regressions on x86_64-linux-gnu. + + Reviewed-by: Collin Funk + (cherry picked from commit 9f5f18aab40ec6b61fa49a007615e6077e9a979b) + +diff --git a/resolv/Makefile b/resolv/Makefile +index f3feaf072a3ec66d..9837a8cfa145e1da 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -55,6 +55,7 @@ tests += \ + tst-res_use_inet6 \ + tst-resolv-basic \ + tst-resolv-binary \ ++ tst-resolv-dns-section \ + tst-resolv-edns \ + tst-resolv-network \ + tst-resolv-noaaaa \ +@@ -65,6 +66,7 @@ tests += \ + tst-resolv-semi-failure \ + tst-resolv-short-response \ + tst-resolv-trailing \ ++ # tests + + # This test calls __res_context_send directly, which is not exported + # from libresolv. +@@ -197,6 +199,8 @@ $(objpfx)tst-resolv-ai_idn-nolibidn2.out: \ + $(gen-locales) $(objpfx)tst-no-libidn2.so + $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-dns-section: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-res_init: $(libdl) $(objpfx)libresolv.so +diff --git a/resolv/tst-resolv-dns-section.c b/resolv/tst-resolv-dns-section.c +new file mode 100644 +index 0000000000000000..1171baef51e3cc36 +--- /dev/null ++++ b/resolv/tst-resolv-dns-section.c +@@ -0,0 +1,162 @@ ++/* Test handling of invalid section transitions (bug 34014). ++ Copyright (C) 2022-2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Name of test, and the second section type. */ ++struct item { ++ const char *test; ++ int ns_section; ++}; ++ ++static const struct item test_items[] = ++ { ++ { "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar }, ++ { "Test crossing from ns_s_an to ns_s_an.", ns_s_ns }, ++ ++ { NULL, 0 }, ++ }; ++ ++/* The response is designed to contain the following: ++ - An Answer section with one T_PTR record that is skipped. ++ - A second section with a semantically invalid T_PTR record. ++ The original defect is that the response parsing would cross ++ section boundaries and handle the additional section T_PTR ++ as if it were an answer. A conforming implementation would ++ stop as soon as it reaches the end of the section. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count; ++ char *tail = NULL; ++ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%ms", &count, &tail) == 2) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* We have a bounded number of possible tests. */ ++ TEST_VERIFY (count >= 0); ++ TEST_VERIFY (count <= 15); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record, but the wrong name (skipped). */ ++ resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, "test.ptr.example.net"); ++ resolv_response_close_record (b); ++ ++ /* Add a second section to test section boundary crossing. */ ++ resolv_response_section (b, test_items[count].ns_section); ++ /* Semantically incorrect, but hide a T_PTR entry. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ resolv_response_add_name (b, "wrong.ptr.example.net"); ++ resolv_response_close_record (b); ++} ++ ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY (count < array_length (test_items)); ++ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ TEST_VERIFY (answer == NULL); ++ TEST_VERIFY (h_errno == NO_RECOVERY); ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; test_items[i].test != NULL; i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/SOURCES/glibc-RHEL-173358-2.patch b/SOURCES/glibc-RHEL-173358-2.patch new file mode 100644 index 0000000..76afef9 --- /dev/null +++ b/SOURCES/glibc-RHEL-173358-2.patch @@ -0,0 +1,304 @@ +Test-only backport of test for CVE-2026-4438 that does not affect +glibc-2.28. Test extracted from the following upstream commit: + +commit 9344c796f7a4ac8f2c59de63f3e1e936b51e817d +Author: Carlos O'Donell +Date: Fri Mar 20 17:14:33 2026 -0400 + + resolv: Check hostname for validity (CVE-2026-4438) + + The processed hostname in getanswer_ptr should be correctly checked to + avoid invalid characters from being allowed, including shell + metacharacters. It is a security issue to fail to check the returned + hostname for validity. + + A regression test is added for invalid metacharacters and other cases + of invalid or valid characters. + + No regressions on x86_64-linux-gnu. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit e10977481f4db4b2a3ce34fa4c3a1e26651ae312) + +diff --git a/resolv/Makefile b/resolv/Makefile +index 9837a8cfa145e1da..359e8a0a9b22aff9 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -57,6 +57,7 @@ tests += \ + tst-resolv-binary \ + tst-resolv-dns-section \ + tst-resolv-edns \ ++ tst-resolv-invalid-ptr \ + tst-resolv-network \ + tst-resolv-noaaaa \ + tst-resolv-noaaaa-vc \ +@@ -208,6 +209,8 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ + $(shared-thread-library) + $(objpfx)tst-resolv-res_init-thread: $(libdl) $(objpfx)libresolv.so \ + $(shared-thread-library) ++$(objpfx)tst-resolv-invalid-ptr: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) +diff --git a/resolv/tst-resolv-invalid-ptr.c b/resolv/tst-resolv-invalid-ptr.c +new file mode 100644 +index 0000000000000000..0c802ab96780efb0 +--- /dev/null ++++ b/resolv/tst-resolv-invalid-ptr.c +@@ -0,0 +1,255 @@ ++/* Test handling of invalid T_PTR results (bug 34015). ++ Copyright (C) 2022-2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Name of test, the answer, the expected error return, and if we ++ expect the call to fail. */ ++struct item { ++ const char *test; ++ const char *answer; ++ int expected; ++ bool fail; ++}; ++ ++static const struct item test_items[] = ++ { ++ /* Test for invalid characters. */ ++ { "Invalid use of \"|\"", ++ "test.|.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"&\"", ++ "test.&.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \";\"", ++ "test.;.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"<\"", ++ "test.<.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \">\"", ++ "test.>.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"(\"", ++ "test.(.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \")\"", ++ "test.).ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"$\"", ++ "test.$.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"`\"", ++ "test.`.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\\"", ++ "test.\\.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\'\"", ++ "test.'.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\"\"", ++ "test.\".ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \" \"", ++ "test. .ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\t\"", ++ "test.\t.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\n\"", ++ "test.\n.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\r\"", ++ "test.\r.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"*\"", ++ "test.*.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"?\"", ++ "test.?.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"[\"", ++ "test.[.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"]\"", ++ "test.].ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \",\"", ++ "test.,.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"~\"", ++ "test.~.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \":\"", ++ "test.:.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"!\"", ++ "test.!.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"@\"", ++ "test.@.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"#\"", ++ "test.#.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"%\"", ++ "test.%%.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"^\"", ++ "test.^.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */ ++ { "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)", ++ "ÁÂÃ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (4-byte, U+0750-U+0752)", ++ "ݐݑݒ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (6-byte, U+0904-U+0906)", ++ "ऄअआ.test.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for "-" which may be valid depending on position. */ ++ { "Invalid leading \"-\"", ++ "-test.ptr.example", NO_RECOVERY, true }, ++ { "Valid trailing \"-\"", ++ "test-.ptr.example", 0, false }, ++ { "Valid mid-label use of \"-\"", ++ "te-st.ptr.example", 0, false }, ++ ++ /* Test for "_" which is always valid in any position. */ ++ { "Valid leading use of \"_\"", ++ "_test.ptr.example", 0, false }, ++ { "Valid mid-label use of \"_\"", ++ "te_st.ptr.example", 0, false }, ++ { "Valid trailing use of \"_\"", ++ "test_.ptr.example", 0, false }, ++ ++ /* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */ ++ { "Valid \"[A-Z]\"", ++ "test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false }, ++ { "Valid \"[a-z]\"", ++ "test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false }, ++ { "Valid \"[0-9]\"", ++ "test.0123456789.ptr.example", 0, false }, ++ { "Valid mixed use of \"[A-Za-z0-9_-]\"", ++ "test.012abcABZ_-.ptr.example", 0, false }, ++ }; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count, count1; ++ char *tail = NULL; ++ ++ /* The test implementation can handle up to 255 tests. */ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ count |= count1 << 4; ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* Cross check. Count has a fixed bound (soft limit). */ ++ TEST_VERIFY (count >= 0 && count <= 255); ++ ++ /* We have a fixed number of tests (hard limit). */ ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, test_items[count].answer); ++ resolv_response_close_record (b); ++} ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ /* Generate an address to query for each test. */ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = (char) count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ ++ /* Verify h_errno is as expected. */ ++ TEST_COMPARE (h_errno, test_items[count].expected); ++ if (h_errno != test_items[count].expected) ++ /* And print more information if it's not. */ ++ printf ("INFO: %s\n", test_items[count].test); ++ ++ if (test_items[count].fail) ++ { ++ /* We expected a failure so verify answer is NULL. */ ++ TEST_VERIFY (answer == NULL); ++ /* If it's not NULL we should print out what we received. */ ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++ } ++ else ++ /* We don't expect a failure so answer must be valid. */ ++ TEST_COMPARE_STRING (answer->h_name, test_items[count].answer); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; i < array_length (test_items); i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index f2783fc..9b984ab 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -115,7 +115,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: %{glibcrelease}.34 +Release: %{glibcrelease}.37 # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1323,6 +1323,13 @@ Patch1088: glibc-RHEL-24169-21.patch Patch1089: glibc-RHEL-24169-22.patch Patch1090: glibc-RHEL-137185.patch Patch1091: glibc-RHEL-140104.patch +Patch1092: glibc-RHEL-162891-1.patch +Patch1093: glibc-RHEL-162891-2.patch +Patch1094: glibc-RHEL-162891-3.patch +Patch1095: glibc-RHEL-162891-4.patch +Patch1096: glibc-RHEL-168095.patch +Patch1097: glibc-RHEL-173358-1.patch +Patch1098: glibc-RHEL-173358-2.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2984,6 +2991,17 @@ fi %{_libdir}/libpthread_nonshared.a %changelog +* Mon May 11 2026 Arjun Shankar - 2.28-251.37 +- Add tests for CVE-2026-4437 and CVE-2026-4438 (RHEL-173358) + +* Mon May 11 2026 Frédéric Bérat - 2.28-251.36 +- Fix __nss_get_default_domain logic to restore netgroup user enumeration + (RHEL-168095) + +* Mon May 04 2026 Frédéric Bérat - 2.28-251.35 +- CVE-2026-4046: Fix assertion failure in IBM1390 and IBM1399 iconv modules + (RHEL-162891) + * Wed Apr 15 2026 Patsy Griffin - 2.28-251.34 - Update locales for Croatia to use EUR as currency symbol. (RHEL-140104)