Bug 10108 - setvbuf _IOFBF doesn't honor size correctly
Summary: setvbuf _IOFBF doesn't honor size correctly
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: stdio (show other bugs)
Version: unspecified
: P2 minor
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-04-27 21:54 UTC by Pádraig Brady
Modified: 2014-06-24 08:31 UTC (History)
5 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Pádraig Brady 2009-04-27 21:54:57 UTC
The following program shows some weird behaviour on fedora 8 (glibc-2.7-2).
Basically the printf is output immediately when it should be buffered.
Note the trailing \n is not output.

The conditions that this happens is:
* The first write only.
* and the write is > 1 byte.
* and specified buffer size is < 128.

Bug 6719 seems related but different I think.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int size=10; //128 required on glibc-2.7 for expected operation
    char* buf=malloc(size);
    setvbuf (stdout, buf, _IOFBF, size);
    printf("too soon\n");
    sleep(2);
}
Comment 1 Pádraig Brady 2009-04-29 09:42:42 UTC
Same thing happens on fedora 11 (glibc-2.9.90-22.i686)
Comment 2 Ondrej Bilka 2013-10-09 19:27:02 UTC
I am not sure about impact of this bug as this behaviour should not break anything.
Comment 3 Rich Felker 2013-10-10 16:53:27 UTC
The implementation is completely entitled not to use the buffer at all if it's too small (or for any other arbitrary reason), or to use it in odd ways, so I don't think this is a conformance bug. However it may be a bug versus the glibc-intended behavior.
Comment 4 paxdiablo 2014-01-31 04:48:53 UTC
The _first_ thing you should be doing is checking the return code from setvbuf(), it's allowed to return a non-zero value if the request cannot be honoured.

See http://pubs.opengroup.org/onlinepubs/009604499/functions/setvbuf.html for details.

Hence the line:

    setvbuf (stdout, buf, _IOFBF, size);
should be written as:

    int rc = setvbuf (stdout, buf, _IOFBF, size);
and later on after the sleep(), add

    printf ("setvbuf rc was %d\n", rc);
Comment 5 Frank Ch. Eigler 2014-06-22 04:10:54 UTC
The problem is this block in libio/iosetvbuf.c:

  switch (mode)
    {
    case _IOFBF:
      fp->_IO_file_flags &= ~(_IO_LINE_BUF|_IO_UNBUFFERED);
      if (buf == NULL)
        {
          if (fp->_IO_buf_base == NULL)
              if (_IO_DOALLOCATE (fp) < 0)
                {
                  result = EOF;
                  goto unlock_return;
                }
              fp->_IO_file_flags &= ~_IO_LINE_BUF;
            }
          result = 0;
          goto unlock_return;
        }
      break;

Note specifically how _IO_DOALLOCATE (fp) is called,
without any reference for the incoming size value.
(_IO_DOALLOCATE uses the block size of the underlying
filesystem.)
Comment 6 Andreas Schwab 2014-06-22 06:50:29 UTC
Since buf is non-NULL here this block of code isn't executed at all.
Comment 7 Andreas Schwab 2014-06-22 07:14:52 UTC
See _IO_new_file_xsputn:

      /* Try to maintain alignment: write a whole number of blocks. */
      block_size = f->_IO_buf_end - f->_IO_buf_base;
      do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);

      if (do_write)
	{
	  count = new_do_write (f, s, do_write);
	  to_do -= count;
	  if (count < do_write)
	    return n - to_do;
	}
Comment 8 Frank Ch. Eigler 2014-06-22 11:29:27 UTC
(In reply to Andreas Schwab from comment #6)
> Since buf is non-NULL here this block of code isn't executed at all.

OK, the original comment#0 has a non-zero buffer.  Should I file a
separate PR for this case:

    setvbuf(fp, NULL, _IOFBF, size);

expecting glibc to allocate a size-sized malloc buffer, being
disappointed.
Comment 9 paxdiablo 2014-06-23 02:02:59 UTC
I'm still not convinced that this should be considered a bug. The POSIX standard and ISO C11 both state quite clearly (C11 has two extra words, surrounded by [square brackets] below but the meaning is identical):
=====
If buf is not a null pointer, the array it points to MAY be used instead of a buffer allocated by [the] setvbuf() [function] and the argument size specifies the size of the array; otherwise, size MAY determine the size of a buffer allocated by the setvbuf() function.
=====

Note the use of the word "may" (capitalised in my quote above). "May" and "shall" have _very_ specific meanings in standards language, the former meaning that something may or may not be true, the latter being that it's _required_ to be true.

In other words, _nothing_ in POSIX or ISO (and the glibc manppage simply defers to those standards) requires an implementation to use the buffer you pass in, nor does it require an implementation to honor your size request.

The only way I can see this being an actual bug is if the return value is zero even though the request is not being honoured. OP should modify their code as per my previous comment to establish whether this is the case. However, even if it is the case, simply returning an error would be the easiest fix :-)
Comment 10 Pádraig Brady 2014-06-23 10:27:52 UTC
The standards give leeway for different implementations.
No harm in glibc having the best implementation.
For example alignment constraints could be handled separately to user specified size.

Also setvbuf returns 0 (success) in this case
Comment 11 Andreas Schwab 2014-06-23 10:49:24 UTC
This is not a conformance issue, but clearly the snippet in #c7 doesn't work as intented.