[PATCH] libio: null terminate the buffer upon initial allocation in getdelim
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu Nov 27 20:14:21 GMT 2025
On 15/11/25 02:25, Collin Funk wrote:
> 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");
This tests with an empty file, should we also check with non-empty file to
ensure that getline returns the last line from the file?
> 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})
More information about the Libc-alpha
mailing list