• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- strstr_fuzz.cpp ---------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// Fuzzing test for llvm-libc strstr implementation.
10 ///
11 //===----------------------------------------------------------------------===//
12 
13 #include "src/string/strlen.h"
14 #include "src/string/strstr.h"
15 #include <stddef.h>
16 #include <stdint.h>
17 
18 // Simple loop to compare two strings up to a size n.
simple_memcmp(const char * left,const char * right,size_t n)19 static int simple_memcmp(const char *left, const char *right, size_t n) {
20   for (; n && *left == *right; ++left, ++right, --n)
21     ;
22   return n ? *left - *right : 0;
23 }
24 
25 // The general structure is to take the value of the first byte, set size1 to
26 // that value, and add the null terminator. size2 will then contain the rest of
27 // the bytes in data.
28 // For example, with inputs (data={2, 6, 4, 8, 0}, size=5):
29 //         size1: data[0] = 2
30 //         data1: {2, 6} + '\0' = {2, 6, '\0'}
31 //         size2: size - size1 = 3
32 //         data2: {4, 8, '\0'}
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)33 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
34   // Verify the size is at least 1 and the data is null terminated.
35   if (!size || data[size - 1] != '\0')
36     return 0;
37   const size_t size1 = (data[0] <= size ? data[0] : size);
38   // The first size will always be at least 1 since
39   // we need to append the null terminator. The second size
40   // needs to be checked since it must also contain the null
41   // terminator.
42   if (size - size1 == 0)
43     return 0;
44 
45   // Copy the data into a new container.
46   uint8_t *container = new uint8_t[size1 + 1];
47   if (!container)
48     __builtin_trap();
49 
50   size_t i;
51   for (i = 0; i < size1; ++i)
52     container[i] = data[i];
53   container[size1] = '\0'; // Add null terminator to container.
54 
55   const char *needle = reinterpret_cast<const char *>(container);
56   const char *haystack = reinterpret_cast<const char *>(data + i);
57   const char *result = __llvm_libc::strstr(haystack, needle);
58 
59   // A null terminator may exist earlier in each, so this needs to be recorded.
60   const size_t haystack_size = __llvm_libc::strlen(haystack);
61   const size_t needle_size = __llvm_libc::strlen(needle);
62 
63   if (result) {
64     // The needle is in the haystack.
65     // 1. Verify that the result matches the needle.
66     if (simple_memcmp(needle, result, needle_size) != 0)
67       __builtin_trap();
68 
69     const char *haystack_ptr = haystack;
70     // 2. Verify that the result is the first occurrence of the needle.
71     for (; haystack_ptr != result; ++haystack_ptr) {
72       if (simple_memcmp(needle, haystack_ptr, needle_size) == 0)
73         __builtin_trap(); // There was an earlier occurrence of the needle.
74     }
75   } else {
76     // No result was found. Verify that the needle doesn't exist within the
77     // haystack.
78     for (size_t i = 0; i + needle_size < haystack_size; ++i) {
79       if (simple_memcmp(needle, haystack + i, needle_size) == 0)
80         __builtin_trap(); // There was an earlier occurrence of the needle.
81     }
82   }
83   delete[] container;
84   return 0;
85 }
86