[PATCH] libio: Fix CVE-2026-18374 - fopen heap buffer overflow with empty ccs=
=?ks_c_5601-1987?B?vNW1v7HVL1Byb2Nlc3MgJiBJbmZyYSBMYWIoU1IpL7vvvLrA/MDa?= <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
Dear glibc maintainers and community,
I'm submitting a patch to fix CVE-2026-18374, a heap buffer overflow
vulnerability in the fopen() function when processing empty ,ccs= parameters.
## Vulnerability Summary
CVE-2026-18374: Heap buffer overflow in fopen() with empty ,ccs= parameter
- CVSS Score: 4.9 (AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L)
- Affected: glibc 2.45 and earlier
- Reported by: AISLE in partnership with Red Hat
- Advisory: GLIBC-SA-2026-0015
## Root Cause
When fopen("file", "r,ccs=") is called with an empty charset specification:
1. The code allocates a buffer of size (length + 3) bytes
2. For empty strings, this results in only 3 bytes being allocated
3. Subsequent charset conversion operations could overflow this minimal buffer
4. Leads to heap corruption, DoS, or potential code execution
## Fix
The patch adds input validation to reject empty ccs= strings before
buffer allocation:
1. Extract ccs= string length into a size_t variable for clarity
2. Validate that the length is not zero
3. Return EINVAL error for invalid (empty) charset specifications
4. Close file descriptor before returning error
This approach is minimal and focused, maintaining compatibility with valid
ccs= specifications while preventing the buffer overflow.
## Testing
- Regression test case added: do_cve_2026_18374()
- Test verifies fopen() correctly rejects empty ccs= with EINVAL
- Test follows existing CVE/BZ test patterns in libio/tst-fopenloc.c
- No regressions expected in existing tests
## Files Modified
- libio/fileops.c: Add input validation for empty ccs= strings
- libio/tst-fopenloc.c: Add regression test case
Total changes: 38 insertions(+), 2 deletions
---
libio: Fix CVE-2026-18374 - fopen heap buffer overflow with empty ccs=
Passing an empty string to the ,ccs= parameter in fopen() could cause a
heap buffer overflow. The vulnerable code allocated a buffer of size 3
bytes when endp - (cs + 5) = 0 (empty ccs value), which could be
insufficient for the conversion processing.
This patch adds validation to reject empty ccs= strings with EINVAL,
preventing the buffer overflow. Additionally, the ccs_len variable is
extracted to improve code clarity and safety.
CVE-2026-18374 - CVSS 4.9
Reported-by: AISLE in partnership with Red Hat
Signed-off-by: Dongkyun Son <[email protected]>
---
libio/fileops.c | 14 ++++++++++++--
libio/tst-fopenloc.c | 26 ++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/libio/fileops.c b/libio/fileops.c
index 4db4a76f75..b034f71c56 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -292,7 +292,17 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const
char *mode,
struct gconv_fcts fcts;
struct _IO_codecvt *cc;
char *endp = __strchrnul (cs + 5, ',');
- char *ccs = malloc (endp - (cs + 5) + 3);
+ size_t ccs_len = endp - (cs + 5);
+
+ /* Reject empty ccs= string to prevent heap buffer overflow. */
+ if (ccs_len == 0)
+ {
+ (void) _IO_file_close_it (fp);
+ __set_errno (EINVAL);
+ return NULL;
+ }
+
+ char *ccs = malloc (ccs_len + 3);
if (ccs == NULL)
{
@@ -302,7 +312,7 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const
char *mode,
return NULL;
}
- *((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
+ *((char *) __mempcpy (ccs, cs + 5, ccs_len)) = '\0';
strip (ccs, ccs);
if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0'
diff --git a/libio/tst-fopenloc.c b/libio/tst-fopenloc.c
index ac46f0ac60..42e6786df0 100644
--- a/libio/tst-fopenloc.c
+++ b/libio/tst-fopenloc.c
@@ -85,6 +85,31 @@ do_bz18906 (void)
return EXIT_SUCCESS;
}
+static int
+do_cve_2026_18374 (void)
+{
+ /* CVE-2026-18374 -- reject empty ccs= to prevent heap buffer overflow. */
+
+ const char *ccs = "r,ccs=";
+ FILE *fp = fopen (inputfile, ccs);
+
+ if (fp != NULL)
+ {
+ printf ("fopen with empty ccs= should have failed but succeeded\n");
+ fclose (fp);
+ return 1;
+ }
+
+ /* Check that errno is set to EINVAL for invalid ccs parameter. */
+ if (errno != EINVAL)
+ {
+ printf ("expected EINVAL, got %d\n", errno);
+ return 1;
+ }
+
+ return 0;
+}
+
static int
do_test (void)
{
@@ -110,6 +135,7 @@ do_test (void)
TEST_COMPARE (do_bz17916 (), 0);
TEST_COMPARE (do_bz18906 (), 0);
+ TEST_COMPARE (do_cve_2026_18374 (), 0);
return EXIT_SUCCESS;
}