[PATCH] libio: null terminate the buffer upon initial allocation in getdelim

Collin Funk collin.funk1@gmail.com
Sat Nov 15 05:25:36 GMT 2025


Commit 33eff78c8b28adc4963987880e10d96761f2a167 caused issues in nbdkit
which had code similar to this to get the last line of the file:

    while (getline (&line, &len, fp) != -1)
      ;
    /* Process LINE.  */

After that commit, line[0] would be equal to '\0' instead of containing
the last line of the file like before that commit.

This patch null terminates the buffer upon getdelim/getline's initial
allocation. This is compatible with previous glibc versions, while also
protecting the caller from reading uninitialized memory if the file is
empty, as long as getline/getdelim does the initial allocation.
---
 libio/iogetdelim.c   | 7 +++++--
 libio/tst-getdelim.c | 8 +++-----
 manual/stdio.texi    | 7 ++++++-
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/libio/iogetdelim.c b/libio/iogetdelim.c
index 1d89757352..8528e2a01e 100644
--- a/libio/iogetdelim.c
+++ b/libio/iogetdelim.c
@@ -63,7 +63,11 @@ __getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
     {
       *n = 120;
       *lineptr = (char *) malloc (*n);
-      if (*lineptr == NULL)
+      /* Null terminate the buffer upon allocation otherwise it will not be
+         null-terminated upon reading from an empty file.  */
+      if (*lineptr != NULL)
+        (*lineptr)[0] = '\0';
+      else
 	{
 	  fseterr_unlocked (fp);
 	  result = -1;
@@ -77,7 +81,6 @@ __getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
       if (__underflow (fp) == EOF)
 	{
 	  result = -1;
-	  (*lineptr)[0] = '\0';
 	  goto unlock_return;
 	}
       len = fp->_IO_read_end - fp->_IO_read_ptr;
diff --git a/libio/tst-getdelim.c b/libio/tst-getdelim.c
index 556697453c..0120761ac3 100644
--- a/libio/tst-getdelim.c
+++ b/libio/tst-getdelim.c
@@ -51,11 +51,9 @@ do_test (void)
   xfclose (memstream);
   free (lineptr);
 
-  /* Test that getdelim NUL terminates upon reading an EOF from an empty
-     file (BZ #28038).  This test fails on glibc 2.42 and earlier.  */
-  lineptr = xmalloc (1);
-  lineptr[0] = 'A';
-  linelen = 1;
+  /* Test that we null-terminate the buffer upon allocating it (BZ #28038).  */
+  lineptr = NULL;
+  linelen = 0;
   char *file_name;
   TEST_VERIFY_EXIT (create_temp_file ("tst-getdelim.", &file_name) != -1);
   FILE *fp = fopen (file_name, "r");
diff --git a/manual/stdio.texi b/manual/stdio.texi
index e8f60b09c1..4e5b890cf0 100644
--- a/manual/stdio.texi
+++ b/manual/stdio.texi
@@ -1279,7 +1279,12 @@ @node Line Input
 POSIX.1-2008.
 
 If an error occurs or end of file is reached without any bytes read,
-@code{getline} returns @code{-1}.
+@code{getline} returns @code{-1}.  POSIX leaves the contents of
+@code{*@var{lineptr}} undefined when @code{getline} returns @code{-1}.
+If the the @glibcadj{} implementation of @code{getline} allocates the
+initial buffer it will null terminate it to prevent the caller from
+reading uninitialized memory if no characters can be read from
+@code{stream}.
 @end deftypefun
 
 @deftypefun ssize_t getdelim (char **restrict @var{lineptr}, size_t *restrict @var{n}, int @var{delimiter}, FILE *restrict @var{stream})
-- 
2.51.1



More information about the Libc-alpha mailing list