Add make_safe_url_new()
authorEmil Mikulic <emikulic@gmail.com>
Sun, 9 Dec 2018 12:13:28 +0000 (23:13 +1100)
committerEmil Mikulic <emikulic@gmail.com>
Sun, 9 Dec 2018 12:44:33 +0000 (23:44 +1100)
Contributed by: Bert Gijsbers

devel/fuzz_llvm_make_safe_uri.c

index a82de59..17fc800 100644 (file)
@@ -116,20 +116,92 @@ static char *make_safe_url(char *url) {
   return url;
 }
 
-int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+char *make_safe_url_new(char *const url) {
+    char *src = url, *dst;
+    #define ends(c) ((c) == '/' || (c) == '\0')
+
+    /* URLs not starting with a slash are illegal. */
+    if (*src != '/')
+        return NULL;
+
+    /* Fast case: skip until first double-slash or dot-dir. */
+    for ( ; *src && *src != '?'; ++src) {
+        if (*src == '/') {
+            if (src[1] == '/')
+                break;
+            else if (src[1] == '.') {
+                if (ends(src[2]))
+                    break;
+                else if (src[2] == '.' && ends(src[3]))
+                    break;
+            }
+        }
+    }
+
+    /* Copy to dst, while collapsing multi-slashes and handling dot-dirs. */
+    dst = src;
+    while (*src && *src != '?') {
+        if (*src != '/')
+            *dst++ = *src++;
+        else if (*++src == '/')
+            ;
+        else if (*src != '.')
+            *dst++ = '/';
+        else if (ends(src[1]))
+            /* Ignore single-dot component. */
+            ++src;
+        else if (src[1] == '.' && ends(src[2])) {
+            /* Double-dot component. */
+            src += 2;
+            if (dst == url)
+                return NULL; /* Illegal URL */
+            else
+                /* Backtrack to previous slash. */
+                while (*--dst != '/' && dst > url);
+        }
+        else
+            *dst++ = '/';
+    }
+
+    if (dst == url)
+        ++dst;
+    *dst = '\0';
+    return url;
+    #undef ends
+}
+
+char* dup(const uint8_t *data, size_t size) {
   char *buf = malloc(size + 1);
   memcpy(buf, data, size);
   buf[size] = 0;
+  return buf;
+}
+
+void check(char* safe) {
+  if (safe == NULL) return;
+  if (strstr(safe, "/../") != NULL) __builtin_trap();
+  if (strstr(safe, "/./") != NULL) __builtin_trap();
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  char *buf1 = dup(data, size);
+  char *buf2 = dup(data, size);
   // Enable this to make sure the fuzzer is working.
 #if 0
-  if (size > 2 && buf[2] == 'x') __builtin_trap();
+  if (size > 2 && buf1[2] == 'x') __builtin_trap();
 #endif
-  char *safe = make_safe_url(buf);
-  if (safe) {
-    if (strstr(safe, "/../") != NULL) __builtin_trap();
-    if (strstr(safe, "/./") != NULL) __builtin_trap();
+  char *safe1 = make_safe_url(buf1);
+  char *safe2 = make_safe_url(buf2);
+  check(safe1);
+  check(safe2);
+  if (safe1 == NULL) {
+    if (safe2 != NULL) __builtin_trap();
+  } else {
+    if (strcmp(safe1, safe2) != 0) __builtin_trap();
   }
-  free(buf);  // Don't leak memory.
+  // Don't leak memory.
+  free(buf1);
+  free(buf2);
   return 0;
 }