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.
front
cinap_lenrek 2016-11-27 21:20:27 +01:00
parent 0edcb33ca1
commit 6d42467411
4 changed files with 54 additions and 34 deletions

View File

@ -40,18 +40,20 @@ int _IO_putc(int c, FILE *f){
if(f->flags&STRING){ if(f->flags&STRING){
f->rp=f->buf+f->bufl; f->rp=f->buf+f->bufl;
if(f->wp==f->rp){ if(f->wp==f->rp){
if(f->flags&BALLOC) if(f->flags&BALLOC){
f->buf=realloc(f->buf, f->bufl+BUFSIZ); char *t = realloc(f->buf, f->bufl+BUFSIZ);
else{ if(t==NULL){
f->state=ERR; f->state=ERR;
return EOF; return EOF;
} }
if(f->buf==NULL){ f->buf=t;
f->state=ERR; f->wp=t+f->bufl;
return EOF;
}
f->rp=f->buf+f->bufl;
f->bufl+=BUFSIZ; f->bufl+=BUFSIZ;
f->rp=t+f->bufl;
}else{
f->state=ERR;
return EOF;
}
} }
*f->wp++=c; *f->wp++=c;
} }

View File

@ -7,28 +7,35 @@
char *_IO_sclose(FILE *f){ char *_IO_sclose(FILE *f){
switch(f->state){ switch(f->state){
default: /* ERR CLOSED */ default: /* ERR CLOSED */
Error:
if(f->buf && f->flags&BALLOC) if(f->buf && f->flags&BALLOC)
free(f->buf); free(f->buf);
f->state=CLOSED; f->buf=0;
f->flags=0; break;
return NULL;
case OPEN: case OPEN:
f->buf=malloc(1); f->buf=malloc(1);
f->buf[0]='\0'; f->buf[0]='\0';
break; break;
case RD: case RD:
case END: case END:
f->flags=0;
break; break;
case RDWR: case RDWR:
case WR: case WR:
f->rp=f->buf+f->bufl;
if(f->wp==f->rp){ if(f->wp==f->rp){
if(f->flags&BALLOC) if(f->flags&BALLOC){
f->buf=realloc(f->buf, f->bufl+1); char *t = realloc(f->buf, f->bufl+1);
if(f->buf==NULL) return NULL; 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->wp='\0';
f->flags=0;
break; break;
} }
f->state=CLOSED; f->state=CLOSED;

View File

@ -18,7 +18,8 @@ int _IO_putc(int c, FILE *f){
case CLOSED: case CLOSED:
return EOF; return EOF;
case OPEN: case OPEN:
_IO_setvbuf(f); if(_IO_setvbuf(f)!=0)
return EOF;
/* fall through */ /* fall through */
case RDWR: case RDWR:
case END: case END:
@ -38,18 +39,20 @@ int _IO_putc(int c, FILE *f){
if(f->flags&STRING){ if(f->flags&STRING){
f->rp=f->buf+f->bufl; f->rp=f->buf+f->bufl;
if(f->wp==f->rp){ if(f->wp==f->rp){
if(f->flags&BALLOC) if(f->flags&BALLOC){
f->buf=realloc(f->buf, f->bufl+BUFSIZ); char *t = realloc(f->buf, f->bufl+BUFSIZ);
else{ if(t==NULL){
f->state=ERR; f->state=ERR;
return EOF; return EOF;
} }
if(f->buf==NULL){ f->buf=t;
f->state=ERR; f->wp=t+f->bufl;
return EOF;
}
f->rp=f->buf+f->bufl;
f->bufl+=BUFSIZ; f->bufl+=BUFSIZ;
f->rp=t+f->bufl;
}else{
f->state=ERR;
return EOF;
}
} }
*f->wp++=c; *f->wp++=c;
} }

View File

@ -5,27 +5,35 @@
char *sclose(FILE *f){ char *sclose(FILE *f){
switch(f->state){ switch(f->state){
default: /* ERR CLOSED */ default: /* ERR CLOSED */
Error:
if(f->buf && f->flags&BALLOC) if(f->buf && f->flags&BALLOC)
free(f->buf); free(f->buf);
f->flags=0; f->buf=0;
return NULL; break;
case OPEN: case OPEN:
f->buf=malloc(1); f->buf=malloc(1);
f->buf[0]='\0'; f->buf[0]='\0';
break; break;
case RD: case RD:
case END: case END:
f->flags=0;
break; break;
case RDWR: case RDWR:
case WR: case WR:
f->rp=f->buf+f->bufl;
if(f->wp==f->rp){ if(f->wp==f->rp){
if(f->flags&BALLOC) if(f->flags&BALLOC){
f->buf=realloc(f->buf, f->bufl+1); char *t = realloc(f->buf, f->bufl+1);
if(f->buf==NULL) return NULL; 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->wp='\0';
f->flags=0;
break; break;
} }
f->state=CLOSED; f->state=CLOSED;