1 //
2 //
3 // Copyright 2016 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include <dirent.h>
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28
29 #include "absl/flags/flag.h"
30 #include "absl/types/optional.h"
31 #include "gtest/gtest.h"
32
33 #include <grpc/slice.h>
34 #include <grpc/support/alloc.h>
35 #include <grpc/support/log.h>
36
37 #include "src/core/lib/gprpp/env.h"
38 #include "src/core/lib/iomgr/error.h"
39 #include "test/core/util/test_config.h"
40 #include "test/core/util/tls_utils.h"
41 #include "test/cpp/util/test_config.h"
42
43 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
44 extern bool squelch;
45
46 ABSL_FLAG(std::string, file, "", "Use this file as test data");
47 ABSL_FLAG(std::string, directory, "", "Use this directory as test data");
48
49 class FuzzerCorpusTest : public ::testing::TestWithParam<std::string> {};
50
TEST_P(FuzzerCorpusTest,RunOneExample)51 TEST_P(FuzzerCorpusTest, RunOneExample) {
52 // Need to call grpc_init() here to use a slice, but need to shut it
53 // down before calling LLVMFuzzerTestOneInput(), because most
54 // implementations of that function will initialize and shutdown gRPC
55 // internally.
56 fprintf(stderr, "Example file: %s\n", GetParam().c_str());
57 squelch = false;
58 std::string buffer = grpc_core::testing::GetFileContents(GetParam());
59 size_t length = buffer.size();
60 void* data = gpr_malloc(length);
61 if (length > 0) {
62 memcpy(data, buffer.data(), length);
63 }
64 LLVMFuzzerTestOneInput(static_cast<uint8_t*>(data), length);
65 gpr_free(data);
66 }
67
68 class ExampleGenerator
69 : public ::testing::internal::ParamGeneratorInterface<std::string> {
70 public:
71 ::testing::internal::ParamIteratorInterface<std::string>* Begin()
72 const override;
73 ::testing::internal::ParamIteratorInterface<std::string>* End()
74 const override;
75
76 private:
Materialize() const77 void Materialize() const {
78 if (examples_.empty()) {
79 if (!absl::GetFlag(FLAGS_file).empty()) {
80 examples_.push_back(absl::GetFlag(FLAGS_file));
81 }
82 if (!absl::GetFlag(FLAGS_directory).empty()) {
83 auto test_srcdir = grpc_core::GetEnv("TEST_SRCDIR");
84 gpr_log(GPR_DEBUG, "test_srcdir=\"%s\"",
85 test_srcdir.has_value() ? test_srcdir->c_str() : "(null)");
86 std::string directory = absl::GetFlag(FLAGS_directory);
87 if (test_srcdir.has_value()) {
88 directory =
89 *test_srcdir + std::string("/com_github_grpc_grpc/") + directory;
90 }
91 gpr_log(GPR_DEBUG, "Using corpus directory: %s", directory.c_str());
92 DIR* dp;
93 struct dirent* ep;
94 dp = opendir(directory.c_str());
95
96 if (dp != nullptr) {
97 while ((ep = readdir(dp)) != nullptr) {
98 if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) {
99 examples_.push_back(directory + "/" + ep->d_name);
100 }
101 }
102
103 (void)closedir(dp);
104 } else {
105 perror("Couldn't open the directory");
106 abort();
107 }
108 }
109 }
110 // Make sure we don't succeed without doing anything, which caused
111 // us to be blind to our fuzzers not running for 9 months.
112 GPR_ASSERT(!examples_.empty());
113 // Get a consistent ordering of examples so problems don't just show up on
114 // CI
115 std::sort(examples_.begin(), examples_.end());
116 }
117
118 mutable std::vector<std::string> examples_;
119 };
120
121 class ExampleIterator
122 : public ::testing::internal::ParamIteratorInterface<std::string> {
123 public:
ExampleIterator(const ExampleGenerator & base_,std::vector<std::string>::const_iterator begin)124 ExampleIterator(const ExampleGenerator& base_,
125 std::vector<std::string>::const_iterator begin)
126 : base_(base_), begin_(begin), current_(begin) {}
127
BaseGenerator() const128 const ExampleGenerator* BaseGenerator() const override { return &base_; }
129
Advance()130 void Advance() override { current_++; }
Clone() const131 ExampleIterator* Clone() const override { return new ExampleIterator(*this); }
Current() const132 const std::string* Current() const override { return &*current_; }
133
Equals(const ParamIteratorInterface<std::string> & other) const134 bool Equals(const ParamIteratorInterface<std::string>& other) const override {
135 return &base_ == other.BaseGenerator() &&
136 current_ == dynamic_cast<const ExampleIterator*>(&other)->current_;
137 }
138
139 private:
ExampleIterator(const ExampleIterator & other)140 ExampleIterator(const ExampleIterator& other)
141 : base_(other.base_), begin_(other.begin_), current_(other.current_) {}
142
143 const ExampleGenerator& base_;
144 const std::vector<std::string>::const_iterator begin_;
145 std::vector<std::string>::const_iterator current_;
146 };
147
148 ::testing::internal::ParamIteratorInterface<std::string>*
Begin() const149 ExampleGenerator::Begin() const {
150 Materialize();
151 return new ExampleIterator(*this, examples_.begin());
152 }
153
154 ::testing::internal::ParamIteratorInterface<std::string>*
End() const155 ExampleGenerator::End() const {
156 Materialize();
157 return new ExampleIterator(*this, examples_.end());
158 }
159
160 INSTANTIATE_TEST_SUITE_P(
161 CorpusExamples, FuzzerCorpusTest,
162 ::testing::internal::ParamGenerator<std::string>(new ExampleGenerator));
163
main(int argc,char ** argv)164 int main(int argc, char** argv) {
165 grpc::testing::TestEnvironment env(&argc, argv);
166 grpc::testing::InitTest(&argc, &argv, true);
167 ::testing::InitGoogleTest(&argc, argv);
168
169 return RUN_ALL_TESTS();
170 }
171