Faster fuzz test: teach the fuzz test programs to handle directories
authorRichard Levitte <levitte@openssl.org>
Wed, 28 Mar 2018 13:46:28 +0000 (15:46 +0200)
committerRichard Levitte <levitte@openssl.org>
Thu, 29 Mar 2018 07:24:56 +0000 (09:24 +0200)
Instead of invoking the fuzz test programs once for every corpora
file, we invoke them once for each directory of corpora files.  This
dramatically reduces the number of program invikations, as well as the
time 99-test_fuzz.t takes to complete.

fuzz/test-corpus.c was enhanced to handle directories as well as
regular files.

Reviewed-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5776)

fuzz/test-corpus.c
test/recipes/99-test_fuzz.t

index 9cef01f86d9afaa3591b0bb093613142a25013c5..95ffcf2d42ce4721a49c27ee34dd4b778a5f8e12 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <openssl/crypto.h>
 #include "fuzzer.h"
+#include "internal/o_dir.h"
 
-int main(int argc, char **argv) {
-    int n;
+#if defined(_WIN32) && defined(_MAX_PATH)
+# define PATH_MAX _MAX_PATH
+#endif
 
-    FuzzerInitialize(&argc, &argv);
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
 
-    for (n = 1; n < argc; ++n) {
-        struct stat st;
-        FILE *f;
-        unsigned char *buf;
-        size_t s;
-
-        stat(argv[n], &st);
-        f = fopen(argv[n], "rb");
-        if (f == NULL)
-            continue;
-        buf = malloc(st.st_size);
+# if !defined(S_ISREG)
+#   define S_ISREG(m) ((m) & S_IFREG)
+# endif
+
+static void testfile(const char *pathname)
+{
+    struct stat st;
+    FILE *f;
+    unsigned char *buf;
+    size_t s;
+
+    if (stat(pathname, &st) < 0 || !S_ISREG(st.st_mode))
+        return;
+    printf("# %s\n", pathname);
+    fflush(stdout);
+    f = fopen(pathname, "rb");
+    if (f == NULL)
+        return;
+    buf = malloc(st.st_size);
+    if (buf != NULL) {
         s = fread(buf, 1, st.st_size, f);
         OPENSSL_assert(s == (size_t)st.st_size);
         FuzzerTestOneInput(buf, s);
         free(buf);
-        fclose(f);
+    }
+    fclose(f);
+}
+
+int main(int argc, char **argv) {
+    int n;
+
+    FuzzerInitialize(&argc, &argv);
+
+    for (n = 1; n < argc; ++n) {
+        size_t dirname_len = strlen(argv[n]);
+        const char *filename = NULL;
+        char *pathname = NULL;
+        OPENSSL_DIR_CTX *ctx = NULL;
+        int wasdir = 0;
+
+        /*
+         * We start with trying to read the given path as a directory.
+         */
+        while ((filename = OPENSSL_DIR_read(&ctx, argv[n])) != NULL) {
+            wasdir = 1;
+            if (pathname == NULL) {
+                pathname = malloc(PATH_MAX);
+                if (pathname == NULL)
+                    break;
+                strcpy(pathname, argv[n]);
+#ifdef __VMS
+                if (strchr(":<]", pathname[dirname_len - 1]) == NULL)
+#endif
+                    pathname[dirname_len++] = '/';
+                pathname[dirname_len] = '\0';
+            }
+            strcpy(pathname + dirname_len, filename);
+            testfile(pathname);
+        }
+        OPENSSL_DIR_end(&ctx);
+
+        /* If it wasn't a directory, treat it as a file instead */
+        if (!wasdir)
+            testfile(argv[n]);
+
+        free(pathname);
     }
 
     FuzzerCleanup();
index 9322ff7790843994c909a67a8182e89725245f16..2c45fecc9489d8c1297a7ebd9f91b064bf0bbc4d 100644 (file)
@@ -26,14 +26,14 @@ plan tests => scalar @fuzzers;
 
 foreach my $f (@fuzzers) {
     subtest "Fuzzing $f" => sub {
-        my @files = glob(srctop_file('fuzz', 'corpora', $f, '*'));
-        push @files, glob(srctop_file('fuzz', 'corpora', "$f-*", '*'));
+        my @dirs = glob(srctop_file('fuzz', 'corpora', $f));
+        push @dirs, glob(srctop_file('fuzz', 'corpora', "$f-*"));
 
-        plan skip_all => "No corpora for $f-test" unless @files;
+        plan skip_all => "No corpora for $f-test" unless @dirs;
 
-        plan tests => scalar @files;
+        plan tests => scalar @dirs;
 
-        foreach (@files) {
+        foreach (@dirs) {
             ok(run(fuzz(["$f-test", $_])));
         }
     }