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

Collin Funk collin.funk1@gmail.com
Tue Nov 18 05:26:32 GMT 2025


Eric Blake <eblake@redhat.com> writes:

> On Fri, Nov 14, 2025 at 09:25:36PM -0800, 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.
>> ---
>
> Looks like it matches my idea.

Yes, that is what it was based on. I should have added a
"Suggested-by:".

>> +++ 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;
>
> Does linelen have to be initialized to any paticular value when
> lineptr is passed in as NULL?

For glibc it doesn't matter since we check if *LINEPTR == NULL first:

    if (*lineptr == NULL || *n == 0)
      {
      [...]
      }

I would be worried about passing it in uninitialized since an
implementation might access it.

Based on my reading of the standard the allocation is done if
*LINEPTR == NULL and then *n will be updated. So initializing it to
anything should be fine. Setting it to something other than 0 would look
strange, in my opinion.

>>    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
>
> duplicate "the"

Oops, I will remove that locally.

Thanks,
Collin

[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html


More information about the Libc-alpha mailing list