1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <limits.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <string.h>
9 
10 #include <fbl/auto_call.h>
11 #include <fbl/string_printf.h>
12 #include <unittest/unittest.h>
13 
14 #include "fuzzer-fixture.h"
15 #include "test-fuzzer.h"
16 
17 namespace fuzzing {
18 namespace testing {
19 
20 #define ZXDEBUG 0
21 
22 // Public methods
23 
TestFuzzer()24 TestFuzzer::TestFuzzer()
25     : out_(nullptr), outbuf_(nullptr), outbuflen_(0), err_(nullptr), errbuf_(nullptr),
26       errbuflen_(0) {}
27 
~TestFuzzer()28 TestFuzzer::~TestFuzzer() {
29     Reset();
30 }
31 
Reset()32 void TestFuzzer::Reset() {
33     Fuzzer::Reset();
34     args_.clear();
35     executable_.clear();
36     manifest_.clear();
37     dictionary_.clear();
38     data_path_.Reset();
39 
40     if (out_) {
41         fclose(out_);
42 #if ZXDEBUG
43         fprintf(stdout, "%s", outbuf_);
44         fflush(stdout);
45 #endif
46         free(outbuf_);
47         outbuflen_ = 0;
48         outbuf_ = nullptr;
49         out_ = nullptr;
50     }
51 
52     if (err_) {
53         fclose(err_);
54 #if ZXDEBUG
55         fprintf(stderr, "%s", errbuf_);
56         fflush(stderr);
57 #endif
58         free(errbuf_);
59         errbuflen_ = 0;
60         errbuf_ = nullptr;
61         err_ = nullptr;
62     }
63 }
64 
InitZircon()65 bool TestFuzzer::InitZircon() {
66     BEGIN_HELPER;
67     ASSERT_TRUE(fixture_.CreateZircon());
68     ASSERT_TRUE(Init());
69     END_HELPER;
70 }
71 
InitFuchsia()72 bool TestFuzzer::InitFuchsia() {
73     BEGIN_HELPER;
74     ASSERT_TRUE(fixture_.CreateFuchsia());
75     ASSERT_TRUE(Init());
76     END_HELPER;
77 }
78 
Eval(const char * cmdline)79 zx_status_t TestFuzzer::Eval(const char* cmdline) {
80     BEGIN_HELPER;
81     ASSERT_TRUE(Init());
82 
83     char* buf = strdup(cmdline);
84     ASSERT_NONNULL(buf);
85     auto cleanup = fbl::MakeAutoCall([&buf]() { free(buf); });
86     char* ptr = buf;
87     char* arg;
88     while ((arg = strsep(&ptr, " "))) {
89         if (arg && *arg) {
90             args_.push_back(arg);
91         }
92     }
93 
94     END_HELPER;
95 }
96 
InStdOut(const char * needle)97 bool TestFuzzer::InStdOut(const char* needle) {
98     fflush(out_);
99     return strcasestr(outbuf_, needle) != nullptr;
100 }
101 
InStdErr(const char * needle)102 bool TestFuzzer::InStdErr(const char* needle) {
103     fflush(err_);
104     return strcasestr(errbuf_, needle) != nullptr;
105 }
106 
FindArg(const char * fmt,const fbl::String & arg)107 int TestFuzzer::FindArg(const char* fmt, const fbl::String& arg) {
108     fbl::StringBuffer<PATH_MAX> buffer;
109     buffer.AppendPrintf(fmt, arg.c_str());
110     int result = 0;
111     for (const char* arg = args_.first(); arg; arg = args_.next()) {
112         if (strcmp(arg, buffer.c_str()) == 0) {
113             return result;
114         }
115         ++result;
116     }
117     return -1;
118 }
119 
CheckProcess(zx_handle_t process,const char * target)120 bool TestFuzzer::CheckProcess(zx_handle_t process, const char* target) {
121     if (target) {
122         set_target(target);
123     }
124     return Fuzzer::CheckProcess(process);
125 }
126 
127 // Protected methods
128 
Execute()129 zx_status_t TestFuzzer::Execute() {
130     zx_status_t rc;
131 
132     GetArgs(&args_);
133 
134     fbl::String package, target;
135     const char* s = args_.first();
136     executable_.Set(s);
137     if (strcmp(s, "/bin/run") != 0) {
138         // BootFS path
139         // .../boot/test/fuzz/<target>
140         package.Set("zircon_fuzzers");
141         target.Set(s + fixture_.path("boot/test/fuzz/").length());
142     } else {
143         // PkgFS path
144         // fuchsia-pkg://fuchsia.com/<package>#meta/<target>.cmx
145         s = args_.next();
146         s += strlen("fuchsia-pkg://fuchsia.com/");
147         const char* t = strchr(s, '#');
148         package.Set(s, t - s);
149         s = t + strlen("#meta/");
150         t = strrchr(s, '.');
151         target.Set(s, t - s);
152     }
153     manifest_ = fbl::StringPrintf("fuchsia-pkg://fuchsia.com/%s#meta/%s.cmx", package.c_str(),
154                                   target.c_str());
155     dictionary_ = fixture_.path("pkgfs/packages/%s/%s/data/%s/dictionary", package.c_str(),
156                                 fixture_.max_version(package.c_str()), target.c_str());
157     data_path_.Reset();
158     if ((rc = data_path_.Push(fixture_.path("data/fuzzing"))) != ZX_OK ||
159         (rc = data_path_.Push(package)) != ZX_OK || (rc = data_path_.Push(target)) != ZX_OK) {
160         return rc;
161     }
162 
163     return ZX_OK;
164 }
165 
166 // Private methods
167 
Init()168 bool TestFuzzer::Init() {
169     BEGIN_HELPER;
170     Reset();
171 
172     out_ = open_memstream(&outbuf_, &outbuflen_);
173     ASSERT_NONNULL(out_);
174 
175     err_ = open_memstream(&errbuf_, &errbuflen_);
176     ASSERT_NONNULL(err_);
177 
178     // Configure base object
179     set_root(fixture_.path());
180     set_out(out_);
181     set_err(err_);
182 
183     END_HELPER;
184 }
185 
186 } // namespace testing
187 } // namespace fuzzing
188