From 849e7603e9004fd292a93df64dd3524025f2987a Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Sun, 16 Sep 2018 13:46:46 -0400 Subject: [PATCH] fix null pointer subtraction and comparison in stdio morally, for null pointers a and b, a-b, ab should all be defined as 0; however, C does not define any of them. the stdio implementation makes heavy use of such pointer comparison and subtraction for buffer logic, and also uses null pos/base/end pointers to indicate that the FILE is not in the corresponding (read or write) mode ready for accesses through the buffer. all of the comparisons are fixed trivially by using != in place of the relational operators, since the opposite relation (e.g. pos>end) is logically impossible. the subtractions have been reviewed to check that they are conditional the stream being in the appropriate reading- or writing-through-buffer mode, with checks added where needed. in fgets and getdelim, the checks added should improve performance for unbuffered streams by avoiding a do-nothing call to memchr, and should be negligible for buffered streams. --- src/internal/stdio_impl.h | 4 ++-- src/stdio/__overflow.c | 2 +- src/stdio/__stdio_exit.c | 4 ++-- src/stdio/__toread.c | 2 +- src/stdio/ext2.c | 7 +++---- src/stdio/fflush.c | 6 +++--- src/stdio/fgetln.c | 2 +- src/stdio/fgets.c | 18 ++++++++++-------- src/stdio/fgetwc.c | 2 +- src/stdio/fread.c | 2 +- src/stdio/fseek.c | 4 ++-- src/stdio/ftell.c | 8 ++++++-- src/stdio/getdelim.c | 9 +++++++-- src/stdio/vfwscanf.c | 2 +- 14 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/internal/stdio_impl.h b/src/internal/stdio_impl.h index 4afb7ea2..8c81fd53 100644 --- a/src/internal/stdio_impl.h +++ b/src/internal/stdio_impl.h @@ -100,10 +100,10 @@ hidden void __getopt_msg(const char *, const char *, const char *, size_t); #define ferror(f) ((f)->flags & F_ERR) #define getc_unlocked(f) \ - ( ((f)->rpos < (f)->rend) ? *(f)->rpos++ : __uflow((f)) ) + ( ((f)->rpos != (f)->rend) ? *(f)->rpos++ : __uflow((f)) ) #define putc_unlocked(c, f) \ - ( ((unsigned char)(c)!=(f)->lbf && (f)->wpos<(f)->wend) \ + ( ((unsigned char)(c)!=(f)->lbf && (f)->wpos!=(f)->wend) \ ? *(f)->wpos++ = (c) : __overflow((f),(c)) ) /* Caller-allocated FILE * operations */ diff --git a/src/stdio/__overflow.c b/src/stdio/__overflow.c index 3bb37923..e65a594d 100644 --- a/src/stdio/__overflow.c +++ b/src/stdio/__overflow.c @@ -4,7 +4,7 @@ int __overflow(FILE *f, int _c) { unsigned char c = _c; if (!f->wend && __towrite(f)) return EOF; - if (f->wpos < f->wend && c != f->lbf) return *f->wpos++ = c; + if (f->wpos != f->wend && c != f->lbf) return *f->wpos++ = c; if (f->write(f, &c, 1)!=1) return EOF; return c; } diff --git a/src/stdio/__stdio_exit.c b/src/stdio/__stdio_exit.c index 5741070f..a5e42c67 100644 --- a/src/stdio/__stdio_exit.c +++ b/src/stdio/__stdio_exit.c @@ -9,8 +9,8 @@ static void close_file(FILE *f) { if (!f) return; FFINALLOCK(f); - if (f->wpos > f->wbase) f->write(f, 0, 0); - if (f->rpos < f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); + if (f->wpos != f->wbase) f->write(f, 0, 0); + if (f->rpos != f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); } void __stdio_exit(void) diff --git a/src/stdio/__toread.c b/src/stdio/__toread.c index 309ee6e8..f142ff09 100644 --- a/src/stdio/__toread.c +++ b/src/stdio/__toread.c @@ -3,7 +3,7 @@ int __toread(FILE *f) { f->mode |= f->mode-1; - if (f->wpos > f->wbase) f->write(f, 0, 0); + if (f->wpos != f->wbase) f->write(f, 0, 0); f->wpos = f->wbase = f->wend = 0; if (f->flags & F_NORD) { f->flags |= F_ERR; diff --git a/src/stdio/ext2.c b/src/stdio/ext2.c index afd8b34e..34162780 100644 --- a/src/stdio/ext2.c +++ b/src/stdio/ext2.c @@ -3,14 +3,13 @@ size_t __freadahead(FILE *f) { - return f->rend - f->rpos; + return f->rend ? f->rend - f->rpos : 0; } const char *__freadptr(FILE *f, size_t *sizep) { - size_t size = f->rend - f->rpos; - if (!size) return 0; - *sizep = size; + if (f->rpos == f->rend) return 0; + *sizep = f->rend - f->rpos; return (const char *)f->rpos; } diff --git a/src/stdio/fflush.c b/src/stdio/fflush.c index bf1e8437..02dae27a 100644 --- a/src/stdio/fflush.c +++ b/src/stdio/fflush.c @@ -11,7 +11,7 @@ int fflush(FILE *f) for (f=*__ofl_lock(); f; f=f->next) { FLOCK(f); - if (f->wpos > f->wbase) r |= fflush(f); + if (f->wpos != f->wbase) r |= fflush(f); FUNLOCK(f); } __ofl_unlock(); @@ -22,7 +22,7 @@ int fflush(FILE *f) FLOCK(f); /* If writing, flush output */ - if (f->wpos > f->wbase) { + if (f->wpos != f->wbase) { f->write(f, 0, 0); if (!f->wpos) { FUNLOCK(f); @@ -31,7 +31,7 @@ int fflush(FILE *f) } /* If reading, sync position, per POSIX */ - if (f->rpos < f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); + if (f->rpos != f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); /* Clear read and write modes */ f->wpos = f->wbase = f->wend = 0; diff --git a/src/stdio/fgetln.c b/src/stdio/fgetln.c index afe12b5d..5748435d 100644 --- a/src/stdio/fgetln.c +++ b/src/stdio/fgetln.c @@ -8,7 +8,7 @@ char *fgetln(FILE *f, size_t *plen) ssize_t l; FLOCK(f); ungetc(getc_unlocked(f), f); - if ((z=memchr(f->rpos, '\n', f->rend - f->rpos))) { + if (f->rend && (z=memchr(f->rpos, '\n', f->rend - f->rpos))) { ret = (char *)f->rpos; *plen = ++z - ret; f->rpos = (void *)z; diff --git a/src/stdio/fgets.c b/src/stdio/fgets.c index d3f9819e..6171f398 100644 --- a/src/stdio/fgets.c +++ b/src/stdio/fgets.c @@ -21,14 +21,16 @@ char *fgets(char *restrict s, int n, FILE *restrict f) } while (n) { - z = memchr(f->rpos, '\n', f->rend - f->rpos); - k = z ? z - f->rpos + 1 : f->rend - f->rpos; - k = MIN(k, n); - memcpy(p, f->rpos, k); - f->rpos += k; - p += k; - n -= k; - if (z || !n) break; + if (f->rpos != f->rend) { + z = memchr(f->rpos, '\n', f->rend - f->rpos); + k = z ? z - f->rpos + 1 : f->rend - f->rpos; + k = MIN(k, n); + memcpy(p, f->rpos, k); + f->rpos += k; + p += k; + n -= k; + if (z || !n) break; + } if ((c = getc_unlocked(f)) < 0) { if (p==s || !feof(f)) s = 0; break; diff --git a/src/stdio/fgetwc.c b/src/stdio/fgetwc.c index 07fb6d7c..0801e28f 100644 --- a/src/stdio/fgetwc.c +++ b/src/stdio/fgetwc.c @@ -10,7 +10,7 @@ static wint_t __fgetwc_unlocked_internal(FILE *f) size_t l; /* Convert character from buffer if possible */ - if (f->rpos < f->rend) { + if (f->rpos != f->rend) { l = mbtowc(&wc, (void *)f->rpos, f->rend - f->rpos); if (l+1 >= 1) { f->rpos += l + !l; /* l==0 means 1 byte, null */ diff --git a/src/stdio/fread.c b/src/stdio/fread.c index 733d3716..a2116da6 100644 --- a/src/stdio/fread.c +++ b/src/stdio/fread.c @@ -13,7 +13,7 @@ size_t fread(void *restrict destv, size_t size, size_t nmemb, FILE *restrict f) f->mode |= f->mode-1; - if (f->rend - f->rpos > 0) { + if (f->rpos != f->rend) { /* First exhaust the buffer. */ k = MIN(f->rend - f->rpos, l); memcpy(dest, f->rpos, k); diff --git a/src/stdio/fseek.c b/src/stdio/fseek.c index 67d75f7a..439308f7 100644 --- a/src/stdio/fseek.c +++ b/src/stdio/fseek.c @@ -3,10 +3,10 @@ int __fseeko_unlocked(FILE *f, off_t off, int whence) { /* Adjust relative offset for unread data in buffer, if any. */ - if (whence == SEEK_CUR) off -= f->rend - f->rpos; + if (whence == SEEK_CUR && f->rend) off -= f->rend - f->rpos; /* Flush write buffer, and report error on failure. */ - if (f->wpos > f->wbase) { + if (f->wpos != f->wbase) { f->write(f, 0, 0); if (!f->wpos) return -1; } diff --git a/src/stdio/ftell.c b/src/stdio/ftell.c index 5ca41654..1a2afbbc 100644 --- a/src/stdio/ftell.c +++ b/src/stdio/ftell.c @@ -5,12 +5,16 @@ off_t __ftello_unlocked(FILE *f) { off_t pos = f->seek(f, 0, - (f->flags & F_APP) && f->wpos > f->wbase + (f->flags & F_APP) && f->wpos != f->wbase ? SEEK_END : SEEK_CUR); if (pos < 0) return pos; /* Adjust for data in buffer. */ - return pos - (f->rend - f->rpos) + (f->wpos - f->wbase); + if (f->rend) + pos += f->rpos - f->rend; + else if (f->wbase) + pos += f->wpos - f->wbase; + return pos; } off_t __ftello(FILE *f) diff --git a/src/stdio/getdelim.c b/src/stdio/getdelim.c index 60c6cc18..c313775d 100644 --- a/src/stdio/getdelim.c +++ b/src/stdio/getdelim.c @@ -25,8 +25,13 @@ ssize_t getdelim(char **restrict s, size_t *restrict n, int delim, FILE *restric if (!*s) *n=0; for (;;) { - z = memchr(f->rpos, delim, f->rend - f->rpos); - k = z ? z - f->rpos + 1 : f->rend - f->rpos; + if (f->rpos != f->rend) { + z = memchr(f->rpos, delim, f->rend - f->rpos); + k = z ? z - f->rpos + 1 : f->rend - f->rpos; + } else { + z = 0; + k = 0; + } if (i+k+1 >= *n) { if (k >= SIZE_MAX/2-i) goto oom; size_t m = i+k+2; diff --git a/src/stdio/vfwscanf.c b/src/stdio/vfwscanf.c index 7be66344..82f48604 100644 --- a/src/stdio/vfwscanf.c +++ b/src/stdio/vfwscanf.c @@ -76,7 +76,7 @@ static int in_set(const wchar_t *set, int c) #if 1 #undef getwc #define getwc(f) \ - ((f)->rpos < (f)->rend && *(f)->rpos < 128 ? *(f)->rpos++ : (getwc)(f)) + ((f)->rpos != (f)->rend && *(f)->rpos < 128 ? *(f)->rpos++ : (getwc)(f)) #undef ungetwc #define ungetwc(c,f) \ -- 2.25.1