fix multiple stdio functions' behavior on zero-length operations
authorRich Felker <dalias@aerifal.cx>
Fri, 5 Sep 2014 02:21:17 +0000 (22:21 -0400)
committerRich Felker <dalias@aerifal.cx>
Mon, 30 Mar 2015 05:15:44 +0000 (01:15 -0400)
previously, fgets, fputs, fread, and fwrite completely omitted locking
and access to the FILE object when their arguments yielded a zero
length read or write operation independent of the FILE state. this
optimization was invalid; it wrongly skipped marking the stream as
byte-oriented (a C conformance bug) and exposed observably missing
synchronization (a POSIX conformance bug) where one of these functions
could wrongly complete despite another thread provably holding the
lock.

(cherry picked from commit 6e2bb7acf42589fb7130b039d0623e2ca42503dd)

src/stdio/fgets.c
src/stdio/fputs.c
src/stdio/fread.c
src/stdio/fwrite.c

index cf5b1039b50e7c6eafb6d792f605f91836d3b118..d3f9819e82eec3a5707805d5ae5b661a78adec30 100644 (file)
@@ -10,14 +10,16 @@ char *fgets(char *restrict s, int n, FILE *restrict f)
        size_t k;
        int c;
 
+       FLOCK(f);
+
        if (n--<=1) {
+               f->mode |= f->mode-1;
+               FUNLOCK(f);
                if (n) return 0;
                *s = 0;
                return s;
        }
 
-       FLOCK(f);
-
        while (n) {
                z = memchr(f->rpos, '\n', f->rend - f->rpos);
                k = z ? z - f->rpos + 1 : f->rend - f->rpos;
index 1112b192c06b49528862fec0168f5af2527d1886..4737f448e934ac18465c4f827b479edabcc180d4 100644 (file)
@@ -3,9 +3,7 @@
 
 int fputs(const char *restrict s, FILE *restrict f)
 {
-       size_t l = strlen(s);
-       if (!l) return 0;
-       return (int)fwrite(s, l, 1, f) - 1;
+       return (int)fwrite(s, strlen(s), 1, f) - 1;
 }
 
 weak_alias(fputs, fputs_unlocked);
index c461256c3b0de4d6af84c0df426710fa121ddc06..33a65f586ead4649ef537ce53780ea7e12fd0e8e 100644 (file)
@@ -8,11 +8,10 @@ size_t fread(void *restrict destv, size_t size, size_t nmemb, FILE *restrict f)
        unsigned char *dest = destv;
        size_t len = size*nmemb, l = len, k;
 
-       /* Never touch the file if length is zero.. */
-       if (!l) return 0;
-
        FLOCK(f);
 
+       f->mode |= f->mode-1;
+
        if (f->rend - f->rpos > 0) {
                /* First exhaust the buffer. */
                k = MIN(f->rend - f->rpos, l);
index d5f6542d6bebe32ec6dac9ac319e8eaf430dfcd4..81ec271e1bcb7ba477f92c8af74e4cc159deb032 100644 (file)
@@ -28,7 +28,6 @@ size_t __fwritex(const unsigned char *restrict s, size_t l, FILE *restrict f)
 size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f)
 {
        size_t k, l = size*nmemb;
-       if (!l) return l;
        FLOCK(f);
        k = __fwritex(src, l, f);
        FUNLOCK(f);