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){
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;
}

View File

@ -7,28 +7,35 @@
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';
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;

View File

@ -18,7 +18,8 @@ int _IO_putc(int c, FILE *f){
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 @@ int _IO_putc(int c, FILE *f){
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;
}

View File

@ -5,27 +5,35 @@
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';
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;