about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-06-22 17:11:17 -0400
committerRich Felker <dalias@aerifal.cx>2013-06-22 17:11:17 -0400
commitc20804500deebaabc56f383d48dd1ac77dce8349 (patch)
tree8123af90cd923e12dc864fcd291926ef85aa9f83
parenta494171a5a2778fc7b4d24d673d950f3e9864063 (diff)
downloadmusl-c20804500deebaabc56f383d48dd1ac77dce8349.tar.gz
musl-c20804500deebaabc56f383d48dd1ac77dce8349.tar.xz
musl-c20804500deebaabc56f383d48dd1ac77dce8349.zip
fix major scanf breakage with unbuffered streams, fmemopen, etc.
the shgetc api, used internally in scanf and int/float scanning code
to handle field width limiting and pushback, was designed assuming
that pushback could be achieved via a simple decrement on the file
buffer pointer. this only worked by chance for regular FILE streams,
due to the linux readv bug workaround in __stdio_read which moves the
last requested byte through the buffer rather than directly back to
the caller. for unbuffered streams and streams not using __stdio_read
but some other underlying read function, the first character read
could be completely lost, and replaced by whatever junk happened to be
in the unget buffer.

to fix this, simply have shgetc, when it performs an underlying read
operation on the stream, store the character read at the -1 offset
from the read buffer pointer. this is valid even for unbuffered
streams, as they have an unget buffer located just below the start of
the zero-length buffer. the check to avoid storing the character when
it is already there is to handle the possibility of read-only buffers.
no application-exposed FILE types are allowed to use read-only
buffers, but sscanf and strto* may use them internally when calling
functions which use the shgetc api.
-rw-r--r--src/internal/shgetc.c1
1 files changed, 1 insertions, 0 deletions
diff --git a/src/internal/shgetc.c b/src/internal/shgetc.c
index 96f72b6a..e878b00a 100644
--- a/src/internal/shgetc.c
+++ b/src/internal/shgetc.c
@@ -22,5 +22,6 @@ int __shgetc(FILE *f)
 	else
 		f->shend = f->rend;
 	if (f->rend) f->shcnt += f->rend - f->rpos + 1;
+	if (f->rpos[-1] != c) f->rpos[-1] = c;
 	return c;
 }