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>
Fri, 5 Sep 2014 02:21:17 +0000 (22:21 -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.

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