fix flockfile-list regression test
authorSzabolcs Nagy <nsz@port70.net>
Sat, 25 Jul 2020 00:37:21 +0000 (00:37 +0000)
committerSzabolcs Nagy <nsz@port70.net>
Sat, 25 Jul 2020 00:42:50 +0000 (00:42 +0000)
this test should check if freed stdio memory is clobbered
after an funlockfile.

the old method was very fragile: it tried to reuse the
freed memory via a malloc and check if that allocation is
clobbered. now musl supports malloc interposition so we
can use that to directly check the required property.

src/regression/flockfile-list.c

index 09f18ff..a269cb8 100644 (file)
@@ -6,6 +6,84 @@
 #include <string.h>
 #include "test.h"
 
+#define t_fatal(...) (t_error(__VA_ARGS__), _Exit(t_status))
+#define length(a) (sizeof(a)/sizeof*(a))
+
+// interpose malloc functions
+// freed memory is not reused, it is checked for clobber.
+
+static unsigned char buf[1<<20];
+static size_t pos;
+static struct {
+       size_t pos;
+       size_t n;
+       int freed;
+} alloc[100];
+static int idx;
+
+void *malloc(size_t n)
+{
+       if (n == 0) n++;
+       if (n > sizeof buf - pos)
+               t_fatal("test buffer is small, pos: %zu, need: %zu\n", pos, n);
+       if (idx >= length(alloc))
+               t_fatal("test buffer is small, idx: %d\n", idx);
+       void *p = buf + pos;
+       alloc[idx].pos = pos;
+       alloc[idx].n = n;
+       pos += n;
+       idx++;
+       return p;
+}
+
+void *calloc(size_t n, size_t m)
+{
+       return memset(malloc(n*m), 0, n*m);
+}
+
+void *aligned_alloc(size_t a, size_t n)
+{
+       t_fatal("aligned_alloc is unsupported\n");
+}
+
+static int findidx(void *p)
+{
+       size_t pos = (unsigned char *)p - buf;
+       for (int i=0; i<idx; i++)
+               if (alloc[i].pos == pos)
+                       return i;
+       t_fatal("%p is not an allocated pointer\n", p);
+       return -1;
+}
+
+void *realloc(void *p, size_t n)
+{
+       void *q = malloc(n);
+       size_t m = alloc[findidx(p)].n;
+       memcpy(q, p, m < n ? m : n);
+       free(p);
+       return q;
+}
+
+void free(void *p)
+{
+       if (p == 0) return;
+       int i = findidx(p);
+       memset(p, 42, alloc[i].n);
+       alloc[i].freed = 1;
+}
+
+static void checkfreed(void)
+{
+       for (int i=0; i<idx; i++)
+               if (alloc[i].freed)
+                       for (size_t j=0; j<alloc[i].n; j++)
+                               if (buf[alloc[i].pos + j] != 42) {
+                                       t_error("freed allocation %d (pos: %zu, len: %zu) is clobbered\n", i, alloc[i].pos, alloc[i].n);
+                                       break;
+                               }
+}
+
 int main()
 {
        FILE *f = tmpfile();
@@ -14,29 +92,8 @@ int main()
        flockfile(f);
        funlockfile(g);
        fclose(g);
-
-       /* fill memory */
-       if (t_vmfill(0,0,0) < 0)
-               t_error("vmfill failed: %s\n", strerror(errno));
-       size_t i,n;
-       unsigned char *p;
-       for (n = 1; n < 10000; n++) {
-               if (!(p=malloc(n))) break;
-               free(p);
-       }
-       n--;
-       if (!(p=malloc(n))) {
-               t_error("bad malloc fragmentation\n");
-               return t_status;
-       }
-       memset(p, 0xff, n);
-
        /* may corrupt memory */
        funlockfile(f);
-       for (i=0; i<n; i++) {
-               if (p[i]!=0xff) {
-                       t_error("p[%zu] = %.2x\n", i, p[i]);
-               }
-       }
+       checkfreed();
        return t_status;
 }