Procházet zdrojové kódy

fix null pointer subtraction and comparison in stdio

morally, for null pointers a and b, a-b, a<b, and a>b 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.
Rich Felker před 6 roky
rodič
revize
849e7603e9

+ 2 - 2
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 ferror(f) ((f)->flags & F_ERR)
 
 
 #define getc_unlocked(f) \
 #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) \
 #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)) )
 	? *(f)->wpos++ = (c) : __overflow((f),(c)) )
 
 
 /* Caller-allocated FILE * operations */
 /* Caller-allocated FILE * operations */

+ 1 - 1
src/stdio/__overflow.c

@@ -4,7 +4,7 @@ int __overflow(FILE *f, int _c)
 {
 {
 	unsigned char c = _c;
 	unsigned char c = _c;
 	if (!f->wend && __towrite(f)) return EOF;
 	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;
 	if (f->write(f, &c, 1)!=1) return EOF;
 	return c;
 	return c;
 }
 }

+ 2 - 2
src/stdio/__stdio_exit.c

@@ -9,8 +9,8 @@ static void close_file(FILE *f)
 {
 {
 	if (!f) return;
 	if (!f) return;
 	FFINALLOCK(f);
 	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)
 void __stdio_exit(void)

+ 1 - 1
src/stdio/__toread.c

@@ -3,7 +3,7 @@
 int __toread(FILE *f)
 int __toread(FILE *f)
 {
 {
 	f->mode |= f->mode-1;
 	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;
 	f->wpos = f->wbase = f->wend = 0;
 	if (f->flags & F_NORD) {
 	if (f->flags & F_NORD) {
 		f->flags |= F_ERR;
 		f->flags |= F_ERR;

+ 3 - 4
src/stdio/ext2.c

@@ -3,14 +3,13 @@
 
 
 size_t __freadahead(FILE *f)
 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)
 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;
 	return (const char *)f->rpos;
 }
 }
 
 

+ 3 - 3
src/stdio/fflush.c

@@ -11,7 +11,7 @@ int fflush(FILE *f)
 
 
 		for (f=*__ofl_lock(); f; f=f->next) {
 		for (f=*__ofl_lock(); f; f=f->next) {
 			FLOCK(f);
 			FLOCK(f);
-			if (f->wpos > f->wbase) r |= fflush(f);
+			if (f->wpos != f->wbase) r |= fflush(f);
 			FUNLOCK(f);
 			FUNLOCK(f);
 		}
 		}
 		__ofl_unlock();
 		__ofl_unlock();
@@ -22,7 +22,7 @@ int fflush(FILE *f)
 	FLOCK(f);
 	FLOCK(f);
 
 
 	/* If writing, flush output */
 	/* If writing, flush output */
-	if (f->wpos > f->wbase) {
+	if (f->wpos != f->wbase) {
 		f->write(f, 0, 0);
 		f->write(f, 0, 0);
 		if (!f->wpos) {
 		if (!f->wpos) {
 			FUNLOCK(f);
 			FUNLOCK(f);
@@ -31,7 +31,7 @@ int fflush(FILE *f)
 	}
 	}
 
 
 	/* If reading, sync position, per POSIX */
 	/* 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 */
 	/* Clear read and write modes */
 	f->wpos = f->wbase = f->wend = 0;
 	f->wpos = f->wbase = f->wend = 0;

+ 1 - 1
src/stdio/fgetln.c

@@ -8,7 +8,7 @@ char *fgetln(FILE *f, size_t *plen)
 	ssize_t l;
 	ssize_t l;
 	FLOCK(f);
 	FLOCK(f);
 	ungetc(getc_unlocked(f), 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;
 		ret = (char *)f->rpos;
 		*plen = ++z - ret;
 		*plen = ++z - ret;
 		f->rpos = (void *)z;
 		f->rpos = (void *)z;

+ 10 - 8
src/stdio/fgets.c

@@ -21,14 +21,16 @@ char *fgets(char *restrict s, int n, FILE *restrict f)
 	}
 	}
 
 
 	while (n) {
 	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 ((c = getc_unlocked(f)) < 0) {
 			if (p==s || !feof(f)) s = 0;
 			if (p==s || !feof(f)) s = 0;
 			break;
 			break;

+ 1 - 1
src/stdio/fgetwc.c

@@ -10,7 +10,7 @@ static wint_t __fgetwc_unlocked_internal(FILE *f)
 	size_t l;
 	size_t l;
 
 
 	/* Convert character from buffer if possible */
 	/* 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);
 		l = mbtowc(&wc, (void *)f->rpos, f->rend - f->rpos);
 		if (l+1 >= 1) {
 		if (l+1 >= 1) {
 			f->rpos += l + !l; /* l==0 means 1 byte, null */
 			f->rpos += l + !l; /* l==0 means 1 byte, null */

+ 1 - 1
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;
 	f->mode |= f->mode-1;
 
 
-	if (f->rend - f->rpos > 0) {
+	if (f->rpos != f->rend) {
 		/* First exhaust the buffer. */
 		/* First exhaust the buffer. */
 		k = MIN(f->rend - f->rpos, l);
 		k = MIN(f->rend - f->rpos, l);
 		memcpy(dest, f->rpos, k);
 		memcpy(dest, f->rpos, k);

+ 2 - 2
src/stdio/fseek.c

@@ -3,10 +3,10 @@
 int __fseeko_unlocked(FILE *f, off_t off, int whence)
 int __fseeko_unlocked(FILE *f, off_t off, int whence)
 {
 {
 	/* Adjust relative offset for unread data in buffer, if any. */
 	/* 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. */
 	/* Flush write buffer, and report error on failure. */
-	if (f->wpos > f->wbase) {
+	if (f->wpos != f->wbase) {
 		f->write(f, 0, 0);
 		f->write(f, 0, 0);
 		if (!f->wpos) return -1;
 		if (!f->wpos) return -1;
 	}
 	}

+ 6 - 2
src/stdio/ftell.c

@@ -5,12 +5,16 @@
 off_t __ftello_unlocked(FILE *f)
 off_t __ftello_unlocked(FILE *f)
 {
 {
 	off_t pos = f->seek(f, 0,
 	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);
 		? SEEK_END : SEEK_CUR);
 	if (pos < 0) return pos;
 	if (pos < 0) return pos;
 
 
 	/* Adjust for data in buffer. */
 	/* 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)
 off_t __ftello(FILE *f)

+ 7 - 2
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;
 	if (!*s) *n=0;
 
 
 	for (;;) {
 	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 (i+k+1 >= *n) {
 			if (k >= SIZE_MAX/2-i) goto oom;
 			if (k >= SIZE_MAX/2-i) goto oom;
 			size_t m = i+k+2;
 			size_t m = i+k+2;

+ 1 - 1
src/stdio/vfwscanf.c

@@ -76,7 +76,7 @@ static int in_set(const wchar_t *set, int c)
 #if 1
 #if 1
 #undef getwc
 #undef getwc
 #define getwc(f) \
 #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
 #undef ungetwc
 #define ungetwc(c,f) \
 #define ungetwc(c,f) \