On all versions of glibc, getline and getdelim do not NUL-terminate the
line buffer if the first read character is EOF as required by POSIX
[1]. The easiest way to demonstrate this is with this test program and
an empty file:
$ cat main.c
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#ifdef USE_GETLINE
# define GETLINE getline (&line, &line_size, fp)
#else
# define GETLINE getdelim (&line, &line_size, '\n', fp)
#endif
int
main (void)
{
FILE *fp = fopen ("file", "r");
if (fp == NULL)
abort ();
char *line = malloc (1);
size_t line_size = 1;
if (line == NULL)
abort ();
line[0] = 'A';
if (GETLINE != -1 || line[0] != '\0')
{
fprintf (stderr, "Expected first character to be NUL; got %c\n",
line[0]);
abort ();
}
return 0;
}
$ touch file
$ gcc main.c
$ ./a.out
Expected first character to be NUL; got A
Aborted (core dumped)
$ gcc -DUSE_GETLINE main.c
$ ./a.out
Expected first character to be NUL; got A
Aborted (core dumped)
I pushed a fix for this for glibc 2.43 [2].
Will push the attatched patches tomorrow, unless they need changes. :)
Collin
[1] https://sourceware.org/bugzilla/show_bug.cgi?id=28038
[2]
https://forge.sourceware.org/glibc/glibc-mirror/commit/33eff78c8b28adc4963987880e10d96761f2a167
>From 8510e0f28f65d42f8d4d68efd3447125aecd1d26 Mon Sep 17 00:00:00 2001
Message-ID: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 9 Oct 2025 20:36:15 -0700
Subject: [PATCH 1/4] getdelim: Work around a glibc bug.
* m4/getdelim.m4 (gl_FUNC_GETDELIM): Check that the buffer is terminated
with a NUL character when the first character read is EOF. Guess that
the function does not work on glibc.
* doc/posix-functions/getdelim.texi: Mention the bug.
---
ChangeLog | 8 +++++
doc/posix-functions/getdelim.texi | 5 ++++
m4/getdelim.m4 | 50 +++++++++++++++++--------------
3 files changed, 41 insertions(+), 22 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index ec2d9651a5..d5d2799955 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-10-09 Collin Funk <[email protected]>
+
+ getdelim: Work around a glibc bug.
+ * m4/getdelim.m4 (gl_FUNC_GETDELIM): Check that the buffer is terminated
+ with a NUL character when the first character read is EOF. Guess that
+ the function does not work on glibc.
+ * doc/posix-functions/getdelim.texi: Mention the bug.
+
2025-10-08 Bruno Haible <[email protected]>
stdcountof-h tests: Test more kinds of string literals.
diff --git a/doc/posix-functions/getdelim.texi b/doc/posix-functions/getdelim.texi
index d166e61e3b..cb16922259 100644
--- a/doc/posix-functions/getdelim.texi
+++ b/doc/posix-functions/getdelim.texi
@@ -19,6 +19,11 @@ @node getdelim
This function crashes when passed a pointer to a NULL buffer together with a
pointer to a non-zero buffer size on some platforms:
FreeBSD 8.0.
+@item
+This function does not null terminate the buffer when the first
+character read is EOF on some platforms:
+@c https://sourceware.org/PR28038
+glibc 2.42.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/m4/getdelim.m4 b/m4/getdelim.m4
index 63d8830649..35734aca84 100644
--- a/m4/getdelim.m4
+++ b/m4/getdelim.m4
@@ -1,5 +1,5 @@
# getdelim.m4
-# serial 19
+# serial 20
dnl Copyright (C) 2005-2007, 2009-2025 Free Software Foundation, Inc.
dnl
@@ -37,6 +37,7 @@ AC_DEFUN([gl_FUNC_GETDELIM]
gl_cv_func_working_getdelim=no ;;
*)
echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+ touch conftest.empty
AC_RUN_IFELSE([AC_LANG_SOURCE([[
# include <stdio.h>
# include <stdlib.h>
@@ -44,6 +45,7 @@ AC_DEFUN([gl_FUNC_GETDELIM]
int main ()
{
FILE *in = fopen ("./conftest.data", "r");
+ int result = 0;
if (!in)
return 1;
{
@@ -53,7 +55,7 @@ AC_DEFUN([gl_FUNC_GETDELIM]
size_t siz = 0;
int len = getdelim (&line, &siz, '\n', in);
if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
- { free (line); fclose (in); return 2; }
+ result |= 2;
free (line);
}
{
@@ -62,36 +64,40 @@ AC_DEFUN([gl_FUNC_GETDELIM]
char *line = NULL;
size_t siz = (size_t)(~0) / 4;
if (getdelim (&line, &siz, '\n', in) == -1)
- { fclose (in); return 3; }
+ result |= 4;
free (line);
}
fclose (in);
- return 0;
+ {
+ /* Test that reading EOF as the first character sets the first byte
+ in the buffer to NUL. This fails on glibc 2.42 and earlier. */
+ in = fopen ("./conftest.empty", "r");
+ if (!in)
+ return 1;
+ char *line = malloc (1);
+ line[0] = 'A';
+ size_t siz = 1;
+ if (getdelim (&line, &siz, '\n', in) != -1 || line[0] != '\0')
+ result |= 8;
+ free (line);
+ }
+ fclose (in);
+ return result;
}
]])],
[gl_cv_func_working_getdelim=yes],
[gl_cv_func_working_getdelim=no],
- [dnl We're cross compiling.
- dnl Guess it works on glibc2 systems and musl systems.
- AC_EGREP_CPP([Lucky GNU user],
- [
-#include <features.h>
-#ifdef __GNU_LIBRARY__
- #if (__GLIBC__ >= 2) && !defined __UCLIBC__
- Lucky GNU user
- #endif
-#endif
- ],
- [gl_cv_func_working_getdelim="guessing yes"],
- [case "$host_os" in
- *-musl* | midipix*) gl_cv_func_working_getdelim="guessing yes" ;;
- *) gl_cv_func_working_getdelim="$gl_cross_guess_normal" ;;
- esac
- ])
+ [case "$host_os" in
+ # Guess yes on musl.
+ *-musl* | midipix*) gl_cv_func_working_getdelim="guessing yes" ;;
+ # Guess no on glibc.
+ *-gnu* | gnu*) gl_cv_func_working_getdelim="guessing no" ;;
+ *) gl_cv_func_working_getdelim="$gl_cross_guess_normal" ;;
+ esac
])
;;
esac
- ])
+ rm -f conftest.data conftest.empty])
case "$gl_cv_func_working_getdelim" in
*yes) ;;
*) REPLACE_GETDELIM=1 ;;
--
2.51.0
>From 3a51be8d9ef54457a09e9f48fdb8460eb5890bf7 Mon Sep 17 00:00:00 2001
Message-ID: <3a51be8d9ef54457a09e9f48fdb8460eb5890bf7.1760068419.git.collin.fu...@gmail.com>
In-Reply-To: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
References: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 9 Oct 2025 20:40:05 -0700
Subject: [PATCH 2/4] getdelim tests: Add a test for the glibc bug.
* tests/test-getdelim.c (main): Check that the buffer is terminated with
a NUL character when the first character read is EOF.
---
ChangeLog | 4 ++++
tests/test-getdelim.c | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/ChangeLog b/ChangeLog
index d5d2799955..301976a136 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2025-10-09 Collin Funk <[email protected]>
+ getdelim tests: Add a test for the glibc bug.
+ * tests/test-getdelim.c (main): Check that the buffer is terminated with
+ a NUL character when the first character read is EOF.
+
getdelim: Work around a glibc bug.
* m4/getdelim.m4 (gl_FUNC_GETDELIM): Check that the buffer is terminated
with a NUL character when the first character read is EOF. Guess that
diff --git a/tests/test-getdelim.c b/tests/test-getdelim.c
index b7911464e3..3696d0f5ab 100644
--- a/tests/test-getdelim.c
+++ b/tests/test-getdelim.c
@@ -84,7 +84,11 @@ main (void)
ASSERT (memcmp (line, "d\0f", 4) == 0);
ASSERT (3 < len);
+ /* Test that reading an EOF will terminate the buffer with a NUL
+ character. */
result = getdelim (&line, &len, 'n', f);
+ ASSERT (0 < len);
+ ASSERT (line[0] == '\0');
ASSERT (result == -1);
free (line);
--
2.51.0
>From 2822c9a80c47a39856d95abd29dd113053600411 Mon Sep 17 00:00:00 2001
Message-ID: <2822c9a80c47a39856d95abd29dd113053600411.1760068419.git.collin.fu...@gmail.com>
In-Reply-To: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
References: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 9 Oct 2025 20:41:09 -0700
Subject: [PATCH 3/4] getline: Work around a glibc bug.
* m4/getline.m4 (gl_FUNC_GETLINE): Check that the buffer is terminated
with a NUL character when the first character read is EOF. Guess that
the function does not work on glibc.
* doc/posix-functions/getline.texi: Mention the bug.
---
ChangeLog | 6 ++++
doc/posix-functions/getline.texi | 5 ++++
m4/getline.m4 | 50 ++++++++++++++++++--------------
3 files changed, 39 insertions(+), 22 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 301976a136..204b8f2de9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2025-10-09 Collin Funk <[email protected]>
+ getline: Work around a glibc bug.
+ * m4/getline.m4 (gl_FUNC_GETLINE): Check that the buffer is terminated
+ with a NUL character when the first character read is EOF. Guess that
+ the function does not work on glibc.
+ * doc/posix-functions/getline.texi: Mention the bug.
+
getdelim tests: Add a test for the glibc bug.
* tests/test-getdelim.c (main): Check that the buffer is terminated with
a NUL character when the first character read is EOF.
diff --git a/doc/posix-functions/getline.texi b/doc/posix-functions/getline.texi
index 6edc3a56ef..ed67385694 100644
--- a/doc/posix-functions/getline.texi
+++ b/doc/posix-functions/getline.texi
@@ -22,6 +22,11 @@ @node getline
This function crashes when passed a pointer to a NULL buffer together with a
pointer to a non-zero buffer size on some platforms:
FreeBSD 8.0.
+@item
+This function does not null terminate the buffer when the first
+character read is EOF on some platforms:
+@c https://sourceware.org/PR28038
+glibc 2.42.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/m4/getline.m4 b/m4/getline.m4
index b97b801124..9ef0810c5f 100644
--- a/m4/getline.m4
+++ b/m4/getline.m4
@@ -1,5 +1,5 @@
# getline.m4
-# serial 33
+# serial 34
dnl Copyright (C) 1998-2003, 2005-2007, 2009-2025 Free Software Foundation,
dnl Inc.
@@ -31,6 +31,7 @@ AC_DEFUN([gl_FUNC_GETLINE]
AC_CACHE_CHECK([for working getline function],
[am_cv_func_working_getline],
[echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+ touch conftest.empty
AC_RUN_IFELSE([AC_LANG_SOURCE([[
# include <stdio.h>
# include <stdlib.h>
@@ -38,6 +39,7 @@ AC_DEFUN([gl_FUNC_GETLINE]
int main ()
{
FILE *in = fopen ("./conftest.data", "r");
+ int result = 0;
if (!in)
return 1;
{
@@ -47,7 +49,7 @@ AC_DEFUN([gl_FUNC_GETLINE]
size_t siz = 0;
int len = getline (&line, &siz, in);
if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
- { free (line); fclose (in); return 2; }
+ result |= 2;
free (line);
}
{
@@ -56,34 +58,38 @@ AC_DEFUN([gl_FUNC_GETLINE]
char *line = NULL;
size_t siz = (size_t)(~0) / 4;
if (getline (&line, &siz, in) == -1)
- { fclose (in); return 3; }
+ result |= 4;
free (line);
}
fclose (in);
- return 0;
+ {
+ /* Test that reading EOF as the first character sets the first byte
+ in the buffer to NUL. This fails on glibc 2.42 and earlier. */
+ in = fopen ("./conftest.empty", "r");
+ if (!in)
+ return 1;
+ char *line = malloc (1);
+ line[0] = 'A';
+ size_t siz = 1;
+ if (getline (&line, &siz, in) != -1 || line[0] != '\0')
+ result |= 8;
+ free (line);
+ }
+ fclose (in);
+ return result;
}
]])],
[am_cv_func_working_getline=yes],
[am_cv_func_working_getline=no],
- [dnl We're cross compiling.
- dnl Guess it works on glibc2 systems and musl systems.
- AC_EGREP_CPP([Lucky GNU user],
- [
-#include <features.h>
-#ifdef __GNU_LIBRARY__
- #if (__GLIBC__ >= 2) && !defined __UCLIBC__
- Lucky GNU user
- #endif
-#endif
- ],
- [am_cv_func_working_getline="guessing yes"],
- [case "$host_os" in
- *-musl* | midipix*) am_cv_func_working_getline="guessing yes" ;;
- *) am_cv_func_working_getline="$gl_cross_guess_normal" ;;
- esac
- ])
+ [case "$host_os" in
+ # Guess yes on musl.
+ *-musl* | midipix*) am_cv_func_working_getline="guessing yes" ;;
+ # Guess no on glibc.
+ *-gnu* | gnu*) am_cv_func_working_getline="guessing no" ;;
+ *) am_cv_func_working_getline="$gl_cross_guess_normal" ;;
+ esac
])
- ])
+ rm -f conftest.data conftest.empty])
else
am_cv_func_working_getline=no
case "$gl_cv_onwards_func_getline" in
--
2.51.0
>From fc1a9c75e2ed1589ec94d752b275b1f301c31864 Mon Sep 17 00:00:00 2001
Message-ID: <fc1a9c75e2ed1589ec94d752b275b1f301c31864.1760068419.git.collin.fu...@gmail.com>
In-Reply-To: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
References: <8510e0f28f65d42f8d4d68efd3447125aecd1d26.1760068419.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Thu, 9 Oct 2025 20:42:05 -0700
Subject: [PATCH 4/4] getline tests: Add a test for the glibc bug.
* tests/test-getline.c (main): Check that the buffer is terminated with
a NUL character when the first character read is EOF.
---
ChangeLog | 4 ++++
tests/test-getline.c | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/ChangeLog b/ChangeLog
index 204b8f2de9..63fd83a1e9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2025-10-09 Collin Funk <[email protected]>
+ getline tests: Add a test for the glibc bug.
+ * tests/test-getline.c (main): Check that the buffer is terminated with
+ a NUL character when the first character read is EOF.
+
getline: Work around a glibc bug.
* m4/getline.m4 (gl_FUNC_GETLINE): Check that the buffer is terminated
with a NUL character when the first character read is EOF. Guess that
diff --git a/tests/test-getline.c b/tests/test-getline.c
index 66c44d5dc9..bc559c6dc5 100644
--- a/tests/test-getline.c
+++ b/tests/test-getline.c
@@ -84,7 +84,11 @@ main (void)
ASSERT (memcmp (line, "d\0f", 4) == 0);
ASSERT (3 < len);
+ /* Test that reading an EOF will terminate the buffer with a NUL
+ character. */
result = getline (&line, &len, f);
+ ASSERT (0 < len);
+ ASSERT (line[0] == '\0');
ASSERT (result == -1);
free (line);
--
2.51.0