Please try to run attached source under "strace -e trace=mmap2,munmap"
At the end there will be something like:
buf = 0x8049160, fp = 0x804a008, fp+delta = 0x804a04f
munmap(0x804a04f, 4096) = -1 EINVAL (Invalid argument)
On file close libio tries to free buffer, which have not been allocated by libio.
In first case it is internal 1byte buffer, but it can be also user specified buffer,
just compile with -DUSER_BUF. It leads to crashes.
Created attachment 864 [details]
Bug 2337 - libio in wide mode deallocates user supplied buffer,
same behaviour also for post 2.3.91 CVS snapshot
Attached code snippet compiled with -DUSER_BUF still segfaults,
can also be used for testsuite.
libio deallocates user supplied buffer - same behaviour also for glibc-20060501
I've identified two problems with the glibc src code:
1.) The first fwprintf() invocation automatically reorients the FILE stream as
'wide' using _IO_fwide(). The user provided buffer (_IO_FILE->_IO_buf_base) is
NOT USED as the wide character buffer(_IO_FILE->_wide_data->_IO_buf_base). This
causes vfprintf to detect an empty buffer and __woverflow allocates an internal
wide character buffer the size of the file system blk_size (i.e. 1024) to use
for wide character vfprintf. This is not directly related to the spurious
deallocation of the user supplied buffer.
2.) When fclose is called _IO_new_fclose() invokes INT_USE(_IO_file_close_it())
which zeros the _IO_FILE struct _flags field:
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
following which _IO_new_fclose() invokes _IO_FINISH(fp) which calls
_IO_new_file_finish() (the _IO_wfile_jumps entry for __finish) which detects an
unset _IO_USER_BUF and free's the buffer spuriously.
1.) When the stream is reoriented set _IO_FILE->_wide_data->_IO_buf_base =
_IO_FILE->_IO_buf_base; _IO_FILE->_IO_buf_base = NULL; This will cause wide
character printf to use the user supplied buffer.
2a.) Reset the _IO_USER_BUF bit flag to '1' after clearing _IO_FILE->_flags if
it was set before the clearing the _flags in _IO_file_close_it().
2b.) Provide a wide character centric 'finish' function and adjust the
_IO_wfile_jumps jump table entry to use the new function rather than reusing the
non-wide character centric version, i.e.:
instead of what currently exists:
Then, since the FILE stream has been reoriented to 'wide' the _IO_wfile_finish()
would properly only care about the wide character allocated buffer in the manner
I'll investigate the specifications to see if wide character usage is supposed
to use the user supplied buffer.
In the meantime I can provide a patch for solution 2a). It may not be the right
decision but we'll investigate.
Created attachment 1351 [details]
libio patch to prevent spurious deallocation of user supplied buffer.
Directing wide-character IO to use the user supplied buffer proved to be
problematic because the wide character operations make use of the non-wide
character buffer for write operations.
The least intrusive solution was to clean up the IO file finish path. The wide
character jump vtable is initialized such that wide-character IO uses the
default (non-wide) _IO_file_finish() function (probably an oversight) which
invokes _IO_default_finish(). This ends up checking and clearing the non-wide
user buffer spuriously in _IO_new_fclose().
I created a wide-character oriented file finish function _IO_wfile_finish()
which calls the already existing _IO_wdefault_finish() function and I added it
to the wide IO jump table as the default IO file finish function.
This solved the problem and wide character IO now finishes in a manner
consistent with the IO orientation.
I've only tested this on PowerPC thus far.
Per this thread on libc-alpha Ulrich has suggested that this bug be left to him:
Fixed in CVS.
Subject: Bug 2337
Module name: libc
Changes by: firstname.lastname@example.org 2007-01-12 17:25:39
. : ChangeLog
libio : Makefile fileops.c genops.c libio.h
wfiledoalloc.c wgenops.c wmemstream.c wstrops.c
libio : tst-setvbuf1.c
* libio/Makefile (tests): Add tst-setvbuf1.
* libio/tst-setvbuf1.c: New file.
* libio/genops.c (__uflow): Fix a typo.
* libio/wfiledoalloc.c (_IO_wfile_doallocate): Don't stat
nor set _IO_LINE_BUF bit here. Size the wide buffer based on
the narrow buffer size.
* libio/libio.h (_IO_FLAGS2_USER_WBUF): Define.
* libio/wgenops.c (_IO_wsetb, _IO_wdefault_finish): Test and set
_IO_FLAGS2_USER_WBUF bit in _flags2 instead of _IO_USER_BUF bit
* libio/wstrops.c (_IO_wstr_overflow, enlarge_userbuf,
* libio/wmemstream.c (open_wmemstream): Likewise.
* libio/fileops.c (_IO_new_file_close_it): Call _IO_set[bgp]
even for wide streams.
This reliably does not work (if an application does this, it will crash), so the bug (although it can lead to a dangling pointer or double-free) does not appear to be a security issue.