about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2012-04-17 10:58:02 -0400
committerRich Felker <dalias@aerifal.cx>2012-04-17 10:58:02 -0400
commitb5a8b28915aad17b6f49ccacd6d3fef3890844d1 (patch)
tree4e99ccfebc80ef47789e1457fa675381f6514492
parentcc3a4466605fe8dfc31f3b75779110ac93055bc1 (diff)
downloadmusl-b5a8b28915aad17b6f49ccacd6d3fef3890844d1.tar.gz
musl-b5a8b28915aad17b6f49ccacd6d3fef3890844d1.tar.xz
musl-b5a8b28915aad17b6f49ccacd6d3fef3890844d1.zip
fix buffer overflow in vfprintf on long writes to unbuffered files
vfprintf temporarily swaps in a local buffer (for the duration of the
operation) when the target stream is unbuffered; this both simplifies
the implementation of functions like dprintf (they don't need their
own buffers) and eliminates the pathologically bad performance of
writing the formatted output with one or more write syscalls per
formatting field.

in cases like dprintf where we are dealing with a virgin FILE
structure, everything worked correctly. however for long-lived files
(like stderr), it's possible that the buffer bounds were already set
for the internal zero-size buffer. on the next write, __stdio_write
would pick up and use the new buffer provided by vfprintf, but the
bound (wend) field was still pointing at the internal zero-size
buffer's end. this in turn allowed unbounded writes to the temporary
buffer.
-rw-r--r--src/stdio/vfprintf.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c
index 928c8c16..91c6b93a 100644
--- a/src/stdio/vfprintf.c
+++ b/src/stdio/vfprintf.c
@@ -652,8 +652,9 @@ int vfprintf(FILE *f, const char *fmt, va_list ap)
 	FLOCK(f);
 	if (!f->buf_size) {
 		saved_buf = f->buf;
-		f->buf = internal_buf;
+		f->wpos = f->wbase = f->buf = internal_buf;
 		f->buf_size = sizeof internal_buf;
+		f->wend = internal_buf + sizeof internal_buf;
 	}
 	ret = printf_core(f, fmt, &ap2, nl_arg, nl_type);
 	if (saved_buf) {