import CS git glibc-2.28-251.el8_10.37

This commit is contained in:
AlmaLinux RelEng Bot 2026-05-26 04:42:42 -04:00
parent 2662609aa5
commit 01d571523d
8 changed files with 2199 additions and 1 deletions

View File

@ -0,0 +1,77 @@
commit 7ac6fad987c28dc5e0e7964084260f163179be68
Author: Florian Weimer <fweimer@redhat.com>
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 <gconv.h>. It should have been added to the internal
<gconv_int.h> header.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
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 <assert.h>
#include <endian.h>
-#include <gconv.h>
+#include <iconv/gconv_int.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
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 <assert.h>
-#include <gconv.h>
+#include <iconv/gconv_int.h>
#include <string.h>
#define __need_size_t
#define __need_NULL

View File

@ -0,0 +1,369 @@
commit 5729e0e9af590807df66a3db688008f9547bce9f
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
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 <Wilco.Dijkstra@arm.com>
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 <stddef.h>
#include <libc-diag.h>
-/* 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; \
}

View File

@ -0,0 +1,700 @@
commit 3e20ddade31d9c392d8ccf7ec902172f4bb01c2b
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
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 <Wilco.Dijkstra@arm.com>
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 <libc-diag.h>
#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. */

View File

@ -0,0 +1,325 @@
commit d6f08d1cf027f4eb2ba289a6cc66853722d4badc
Author: Florian Weimer <fweimer@redhat.com>
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 <carlos@redhat.com>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <alloc_buffer.h>
+#include <errno.h>
+#include <iconv.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <support/check.h>
+#include <support/next_to_fault.h>
+#include <support/support.h>
+
+/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes
+ starting at OUTBUF. OUTBUF should be right before an unmapped page
+ so that writing past the end will fault. Skip SHIFT bytes at the
+ start of the input and output, to exercise different buffer
+ alignment. TRUNCATE indicates skipped bytes at the end of
+ input (0 and 1 a valid). */
+static void
+test_one (const char *encoding, unsigned int shift, unsigned int truncate,
+ char *outbuf, size_t outbufsize)
+{
+ /* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two
+ Unicode code points when translated. */
+ static char input[] =
+ {
+ /* 8 letters X. */
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ /* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */
+ 0x0e, 0xec, 0xb5, 0x0f
+ };
+
+ /* Expected output after UTF-8 conversion. */
+ static char expected[] =
+ {
+ 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
+ /* U+304B (HIRAGANA LETTER KA). */
+ 0xe3, 0x81, 0x8b,
+ /* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */
+ 0xe3, 0x82, 0x9a
+ };
+
+ iconv_t cd = iconv_open ("UTF-8", encoding);
+ TEST_VERIFY_EXIT (cd != (iconv_t) -1);
+
+ char result_storage[64];
+ struct alloc_buffer result_buf
+ = alloc_buffer_create (result_storage, sizeof (result_storage));
+
+ char *inptr = &input[shift];
+ size_t inleft = sizeof (input) - shift - truncate;
+
+ while (inleft > 0)
+ {
+ char *outptr = outbuf;
+ size_t outleft = outbufsize;
+ size_t inleft_before = inleft;
+
+ size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
+ size_t produced = outptr - outbuf;
+ alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
+
+ if (ret == (size_t) -1 && errno == E2BIG)
+ {
+ if (produced == 0 && inleft == inleft_before)
+ {
+ /* Output buffer too small to make progress. This is
+ expected for very small output buffer sizes. */
+ TEST_VERIFY_EXIT (outbufsize < 3);
+ break;
+ }
+ continue;
+ }
+ if (ret == (size_t) -1)
+ FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize);
+ break;
+ }
+
+ /* Flush any pending state (e.g. a buffered combined character).
+ With outbufsize < 3, we could not store the first character, so
+ the second character did not become pending, and there is nothing
+ to flush. */
+ {
+ char *outptr = outbuf;
+ size_t outleft = outbufsize;
+
+ size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
+ TEST_VERIFY_EXIT (ret == 0);
+ size_t produced = outptr - outbuf;
+ alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
+
+ /* Second flush does not provide more data. */
+ outptr = outbuf;
+ outleft = outbufsize;
+ ret = iconv (cd, NULL, NULL, &outptr, &outleft);
+ TEST_VERIFY_EXIT (ret == 0);
+ TEST_VERIFY (outptr == outbuf);
+ }
+
+ TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf));
+ size_t result_used
+ = sizeof (result_storage) - alloc_buffer_size (&result_buf);
+
+ if (outbufsize >= 3)
+ {
+ TEST_COMPARE (inleft, 0);
+ TEST_COMPARE (result_used, sizeof (expected) - shift);
+ TEST_COMPARE_BLOB (result_storage, result_used,
+ &expected[shift], sizeof (expected) - shift);
+ }
+ else
+ /* If the buffer is too small, only the leading X could be converted. */
+ TEST_COMPARE (result_used, 8 - shift);
+
+ TEST_VERIFY_EXIT (iconv_close (cd) == 0);
+}
+
+static int
+do_test (void)
+{
+ struct support_next_to_fault ntf
+ = support_next_to_fault_allocate (8);
+
+ for (int shift = 0; shift <= 8; ++shift)
+ for (int truncate = 0; truncate < 2; ++truncate)
+ for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++)
+ {
+ char *outbuf = ntf.buffer + ntf.length - outbufsize;
+ test_one ("IBM1390", shift, truncate, outbuf, outbufsize);
+ test_one ("IBM1399", shift, truncate, outbuf, outbufsize);
+ }
+
+ support_next_to_fault_free (&ntf);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,181 @@
commit d8997716a1ca22cf038eac86ed286830ba9818cc
Author: DJ Delorie <dj@redhat.com>
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 <fweimer@redhat.com>
Reviewed-by: Frédéric Bérat <fberat@redhat.com>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <unistd.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "nss_compat/nisdomain.h"
+
+#include <support/test-driver.h>
+#include <support/support.h>
+#include <support/namespace.h>
+#include <support/check.h>
+
+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 <support/test-driver.c>

View File

@ -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 <carlos@redhat.com>
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 <collin.funk1@gmail.com>
(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
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+/* Name of test, and the second section type. */
+struct item {
+ const char *test;
+ int ns_section;
+};
+
+static const struct item test_items[] =
+ {
+ { "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar },
+ { "Test crossing from ns_s_an to ns_s_an.", ns_s_ns },
+
+ { NULL, 0 },
+ };
+
+/* The response is designed to contain the following:
+ - An Answer section with one T_PTR record that is skipped.
+ - A second section with a semantically invalid T_PTR record.
+ The original defect is that the response parsing would cross
+ section boundaries and handle the additional section T_PTR
+ as if it were an answer. A conforming implementation would
+ stop as soon as it reaches the end of the section. */
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+
+ /* We only test PTR. */
+ TEST_COMPARE (qtype, T_PTR);
+
+ unsigned int count;
+ char *tail = NULL;
+
+ if (strstr (qname, "in-addr.arpa") != NULL
+ && sscanf (qname, "%u.%ms", &count, &tail) == 2)
+ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
+ else if (sscanf (qname, "%x.%ms", &count, &tail) == 2)
+ {
+ TEST_COMPARE_STRING (tail, "\
+0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
+ }
+ else
+ FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
+ free (tail);
+
+ /* We have a bounded number of possible tests. */
+ TEST_VERIFY (count >= 0);
+ TEST_VERIFY (count <= 15);
+
+ struct resolv_response_flags flags = {};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+
+ /* Actual answer record, but the wrong name (skipped). */
+ resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60);
+
+ /* Record the answer. */
+ resolv_response_add_name (b, "test.ptr.example.net");
+ resolv_response_close_record (b);
+
+ /* Add a second section to test section boundary crossing. */
+ resolv_response_section (b, test_items[count].ns_section);
+ /* Semantically incorrect, but hide a T_PTR entry. */
+ resolv_response_open_record (b, qname, qclass, qtype, 60);
+ resolv_response_add_name (b, "wrong.ptr.example.net");
+ resolv_response_close_record (b);
+}
+
+
+/* Perform one check using a reverse lookup. */
+static void
+check_reverse (int af, int count)
+{
+ TEST_VERIFY (af == AF_INET || af == AF_INET6);
+ TEST_VERIFY (count < array_length (test_items));
+
+ char addr[sizeof (struct in6_addr)] = { 0 };
+ socklen_t addrlen;
+ if (af == AF_INET)
+ {
+ addr[0] = (char) 192;
+ addr[1] = (char) 168;
+ addr[2] = (char) 0;
+ addr[3] = (char) count;
+ addrlen = 4;
+ }
+ else
+ {
+ addr[0] = 0x20;
+ addr[1] = 0x01;
+ addr[2] = 0x0d;
+ addr[3] = 0xb8;
+ addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
+ addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
+ addr[12] = 0x0;
+ addr[13] = 0x0;
+ addr[14] = 0x0;
+ addr[15] = count;
+ addrlen = 16;
+ }
+
+ h_errno = 0;
+ struct hostent *answer = gethostbyaddr (addr, addrlen, af);
+ TEST_VERIFY (answer == NULL);
+ TEST_VERIFY (h_errno == NO_RECOVERY);
+ if (answer != NULL)
+ printf ("error: unexpected success: %s\n",
+ support_format_hostent (answer));
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ for (int i = 0; test_items[i].test != NULL; i++)
+ {
+ check_reverse (AF_INET, i);
+ check_reverse (AF_INET6, i);
+ }
+
+ resolv_test_end (obj);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -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 <carlos@redhat.com>
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 <adhemerval.zanella@linaro.org>
(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
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/format_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+/* Name of test, the answer, the expected error return, and if we
+ expect the call to fail. */
+struct item {
+ const char *test;
+ const char *answer;
+ int expected;
+ bool fail;
+};
+
+static const struct item test_items[] =
+ {
+ /* Test for invalid characters. */
+ { "Invalid use of \"|\"",
+ "test.|.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"&\"",
+ "test.&.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \";\"",
+ "test.;.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"<\"",
+ "test.<.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \">\"",
+ "test.>.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"(\"",
+ "test.(.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \")\"",
+ "test.).ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"$\"",
+ "test.$.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"`\"",
+ "test.`.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\\\"",
+ "test.\\.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\'\"",
+ "test.'.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\"\"",
+ "test.\".ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \" \"",
+ "test. .ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\\t\"",
+ "test.\t.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\\n\"",
+ "test.\n.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"\\r\"",
+ "test.\r.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"*\"",
+ "test.*.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"?\"",
+ "test.?.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"[\"",
+ "test.[.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"]\"",
+ "test.].ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \",\"",
+ "test.,.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"~\"",
+ "test.~.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \":\"",
+ "test.:.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"!\"",
+ "test.!.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"@\"",
+ "test.@.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"#\"",
+ "test.#.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"%\"",
+ "test.%%.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"^\"",
+ "test.^.ptr.example", NO_RECOVERY, true },
+
+ /* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */
+ { "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)",
+ "ÁÂÃ.test.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of UTF-8 (4-byte, U+0750-U+0752)",
+ "ݐݑݒ.test.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of UTF-8 (6-byte, U+0904-U+0906)",
+ "ऄअआ.test.ptr.example", NO_RECOVERY, true },
+
+ /* Test for "-" which may be valid depending on position. */
+ { "Invalid leading \"-\"",
+ "-test.ptr.example", NO_RECOVERY, true },
+ { "Valid trailing \"-\"",
+ "test-.ptr.example", 0, false },
+ { "Valid mid-label use of \"-\"",
+ "te-st.ptr.example", 0, false },
+
+ /* Test for "_" which is always valid in any position. */
+ { "Valid leading use of \"_\"",
+ "_test.ptr.example", 0, false },
+ { "Valid mid-label use of \"_\"",
+ "te_st.ptr.example", 0, false },
+ { "Valid trailing use of \"_\"",
+ "test_.ptr.example", 0, false },
+
+ /* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */
+ { "Valid \"[A-Z]\"",
+ "test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false },
+ { "Valid \"[a-z]\"",
+ "test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false },
+ { "Valid \"[0-9]\"",
+ "test.0123456789.ptr.example", 0, false },
+ { "Valid mixed use of \"[A-Za-z0-9_-]\"",
+ "test.012abcABZ_-.ptr.example", 0, false },
+ };
+
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+
+ /* We only test PTR. */
+ TEST_COMPARE (qtype, T_PTR);
+
+ unsigned int count, count1;
+ char *tail = NULL;
+
+ /* The test implementation can handle up to 255 tests. */
+ if (strstr (qname, "in-addr.arpa") != NULL
+ && sscanf (qname, "%u.%ms", &count, &tail) == 2)
+ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
+ else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3)
+ {
+ TEST_COMPARE_STRING (tail, "\
+0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
+ count |= count1 << 4;
+ }
+ else
+ FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
+ free (tail);
+
+ /* Cross check. Count has a fixed bound (soft limit). */
+ TEST_VERIFY (count >= 0 && count <= 255);
+
+ /* We have a fixed number of tests (hard limit). */
+ TEST_VERIFY_EXIT (count < array_length (test_items));
+
+ struct resolv_response_flags flags = {};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+
+ /* Actual answer record. */
+ resolv_response_open_record (b, qname, qclass, qtype, 60);
+
+ /* Record the answer. */
+ resolv_response_add_name (b, test_items[count].answer);
+ resolv_response_close_record (b);
+}
+
+/* Perform one check using a reverse lookup. */
+static void
+check_reverse (int af, int count)
+{
+ TEST_VERIFY (af == AF_INET || af == AF_INET6);
+ TEST_VERIFY_EXIT (count < array_length (test_items));
+
+ /* Generate an address to query for each test. */
+ char addr[sizeof (struct in6_addr)] = { 0 };
+ socklen_t addrlen;
+ if (af == AF_INET)
+ {
+ addr[0] = (char) 192;
+ addr[1] = (char) 168;
+ addr[2] = (char) 0;
+ addr[3] = (char) count;
+ addrlen = 4;
+ }
+ else
+ {
+ addr[0] = 0x20;
+ addr[1] = 0x01;
+ addr[2] = 0x0d;
+ addr[3] = 0xb8;
+ addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
+ addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
+ addr[12] = 0x0;
+ addr[13] = 0x0;
+ addr[14] = 0x0;
+ addr[15] = (char) count;
+ addrlen = 16;
+ }
+
+ h_errno = 0;
+ struct hostent *answer = gethostbyaddr (addr, addrlen, af);
+
+ /* Verify h_errno is as expected. */
+ TEST_COMPARE (h_errno, test_items[count].expected);
+ if (h_errno != test_items[count].expected)
+ /* And print more information if it's not. */
+ printf ("INFO: %s\n", test_items[count].test);
+
+ if (test_items[count].fail)
+ {
+ /* We expected a failure so verify answer is NULL. */
+ TEST_VERIFY (answer == NULL);
+ /* If it's not NULL we should print out what we received. */
+ if (answer != NULL)
+ printf ("error: unexpected success: %s\n",
+ support_format_hostent (answer));
+ }
+ else
+ /* We don't expect a failure so answer must be valid. */
+ TEST_COMPARE_STRING (answer->h_name, test_items[count].answer);
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ for (int i = 0; i < array_length (test_items); i++)
+ {
+ check_reverse (AF_INET, i);
+ check_reverse (AF_INET6, i);
+ }
+ resolv_test_end (obj);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -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 <arjun@redhat.com> - 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 <fberat@redhat.com> - 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 <fberat@redhat.com> - 2.28-251.35
- CVE-2026-4046: Fix assertion failure in IBM1390 and IBM1399 iconv modules
(RHEL-162891)
* Wed Apr 15 2026 Patsy Griffin <patsy@redhat.com> - 2.28-251.34
- Update locales for Croatia to use EUR as currency symbol. (RHEL-140104)