shithub: riscv

Download patch

ref: 6d424674113a6a6dd2566d82ba15f20d2d3090e7
parent: 0edcb33ca1f01f8e3c18f5ad99442da8aad2091f
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Nov 27 16:20:27 EST 2016

stdio: fix sclose() buffer overrun when terminating string, realloc() error handling (thanks porlock)

theres a bug is in sclose() where it doesnt check if wp is beyond
the buffer. also wp was not updated after realloc().

bug was reported by porlock on 9fans:

Plan 9's implementation of the standard C functions snprintf and
vsnprintf have a buffer overrun bug.

If the buffer length equals the output length (without the terminating
null), then one too many characters is written to the buffer.

For example,
              snprintf(buf, 4, "ABCD");

will write 5 characters to buf.

--- a/sys/src/ape/lib/ap/stdio/_IO_putc.c
+++ b/sys/src/ape/lib/ap/stdio/_IO_putc.c
@@ -40,18 +40,20 @@
 	if(f->flags&STRING){
 		f->rp=f->buf+f->bufl;
 		if(f->wp==f->rp){
-			if(f->flags&BALLOC)
-				f->buf=realloc(f->buf, f->bufl+BUFSIZ);
-			else{
+			if(f->flags&BALLOC){
+				char *t = realloc(f->buf, f->bufl+BUFSIZ);
+				if(t==NULL){
+					f->state=ERR;
+					return EOF;
+				}
+				f->buf=t;
+				f->wp=t+f->bufl;
+				f->bufl+=BUFSIZ;
+				f->rp=t+f->bufl;
+			}else{
 				f->state=ERR;
 				return EOF;
 			}
-			if(f->buf==NULL){
-				f->state=ERR;
-				return EOF;
-			}
-			f->rp=f->buf+f->bufl;
-			f->bufl+=BUFSIZ;
 		}
 		*f->wp++=c;
 	}
--- a/sys/src/ape/lib/ap/stdio/sclose.c
+++ b/sys/src/ape/lib/ap/stdio/sclose.c
@@ -7,11 +7,11 @@
 char *_IO_sclose(FILE *f){
 	switch(f->state){
 	default:	/* ERR CLOSED */
+	Error:
 		if(f->buf && f->flags&BALLOC)
 			free(f->buf);
-		f->state=CLOSED;
-		f->flags=0;
-		return NULL;
+		f->buf=0;
+		break;
 	case OPEN:
 		f->buf=malloc(1);
 		f->buf[0]='\0';
@@ -18,17 +18,24 @@
 		break;
 	case RD:
 	case END:
-		f->flags=0;
 		break;
 	case RDWR:
 	case WR:
+		f->rp=f->buf+f->bufl;
 		if(f->wp==f->rp){
-			if(f->flags&BALLOC)
-				f->buf=realloc(f->buf, f->bufl+1);
-			if(f->buf==NULL) return NULL;
+			if(f->flags&BALLOC){
+				char *t = realloc(f->buf, f->bufl+1);
+				if(t==NULL)
+					goto Error;
+				f->buf=t;
+				f->wp=t+f->bufl;
+			} else {
+				if(f->wp > f->buf)
+					*(f->wp-1) = '\0';
+				goto Error;
+			}
 		}
 		*f->wp='\0';
-		f->flags=0;
 		break;
 	}
 	f->state=CLOSED;
--- a/sys/src/libstdio/_IO_putc.c
+++ b/sys/src/libstdio/_IO_putc.c
@@ -18,7 +18,8 @@
 	case CLOSED:
 		return EOF;
 	case OPEN:
-		_IO_setvbuf(f);
+		if(_IO_setvbuf(f)!=0)
+			return EOF;
 		/* fall through */
 	case RDWR:
 	case END:
@@ -38,18 +39,20 @@
 	if(f->flags&STRING){
 		f->rp=f->buf+f->bufl;
 		if(f->wp==f->rp){
-			if(f->flags&BALLOC)
-				f->buf=realloc(f->buf, f->bufl+BUFSIZ);
-			else{
+			if(f->flags&BALLOC){
+				char *t = realloc(f->buf, f->bufl+BUFSIZ);
+				if(t==NULL){
+					f->state=ERR;
+					return EOF;
+				}
+				f->buf=t;
+				f->wp=t+f->bufl;
+				f->bufl+=BUFSIZ;
+				f->rp=t+f->bufl;
+			}else{
 				f->state=ERR;
 				return EOF;
 			}
-			if(f->buf==NULL){
-				f->state=ERR;
-				return EOF;
-			}
-			f->rp=f->buf+f->bufl;
-			f->bufl+=BUFSIZ;
 		}
 		*f->wp++=c;
 	}
--- a/sys/src/libstdio/sclose.c
+++ b/sys/src/libstdio/sclose.c
@@ -5,10 +5,11 @@
 char *sclose(FILE *f){
 	switch(f->state){
 	default:	/* ERR CLOSED */
+	Error:
 		if(f->buf && f->flags&BALLOC)
 			free(f->buf);
-		f->flags=0;
-		return NULL;
+		f->buf=0;
+		break;
 	case OPEN:
 		f->buf=malloc(1);
 		f->buf[0]='\0';
@@ -15,17 +16,24 @@
 		break;
 	case RD:
 	case END:
-		f->flags=0;
 		break;
 	case RDWR:
 	case WR:
+		f->rp=f->buf+f->bufl;
 		if(f->wp==f->rp){
-			if(f->flags&BALLOC)
-				f->buf=realloc(f->buf, f->bufl+1);
-			if(f->buf==NULL) return NULL;
+			if(f->flags&BALLOC){
+				char *t = realloc(f->buf, f->bufl+1);
+				if(t==NULL)
+					goto Error;
+				f->buf=t;
+				f->wp=t+f->bufl;
+			} else {
+				if(f->wp > f->buf)
+					*(f->wp-1) = '\0';
+				goto Error;
+			}
 		}
 		*f->wp='\0';
-		f->flags=0;
 		break;
 	}
 	f->state=CLOSED;