From: David Goldman Date: Mon, 21 Oct 2019 20:45:02 +0000 (+0000) Subject: [clang-fuzzer] Add new fuzzer target for Objective-C X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=73e476646bdaeb2ad2d40254ce64c2dfe8c78873;p=clang [clang-fuzzer] Add new fuzzer target for Objective-C Summary: - Similar to that of `clang-fuzzer` itself but instead only targets Objective-C source files via cc1 - Also adds an example corpus directory containing some input for Objective-C Subscribers: mgorny, jfb, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D69171 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@375453 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/tools/clang-fuzzer/CMakeLists.txt b/tools/clang-fuzzer/CMakeLists.txt index 5f07e66fe6..4b2243c5ce 100644 --- a/tools/clang-fuzzer/CMakeLists.txt +++ b/tools/clang-fuzzer/CMakeLists.txt @@ -12,6 +12,7 @@ endif() # Needed by LLVM's CMake checks because this file defines multiple targets. set(LLVM_OPTIONAL_SOURCES ClangFuzzer.cpp + ClangObjectiveCFuzzer.cpp DummyClangFuzzer.cpp ExampleClangProtoFuzzer.cpp ExampleClangLoopProtoFuzzer.cpp @@ -120,3 +121,15 @@ target_link_libraries(clang-fuzzer ${LLVM_LIB_FUZZING_ENGINE} clangHandleCXX ) + +add_clang_executable(clang-objc-fuzzer + EXCLUDE_FROM_ALL + ${DUMMY_MAIN} + ClangObjectiveCFuzzer.cpp + ) + +target_link_libraries(clang-objc-fuzzer + PRIVATE + ${LLVM_LIB_FUZZING_ENGINE} + clangHandleCXX + ) diff --git a/tools/clang-fuzzer/ClangFuzzer.cpp b/tools/clang-fuzzer/ClangFuzzer.cpp index 3a14f4949c..3acdec6447 100644 --- a/tools/clang-fuzzer/ClangFuzzer.cpp +++ b/tools/clang-fuzzer/ClangFuzzer.cpp @@ -20,6 +20,6 @@ extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { return 0; } extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { std::string s((const char *)data, size); - HandleCXX(s, {"-O2"}); + HandleCXX(s, "./test.cc", {"-O2"}); return 0; } diff --git a/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp b/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp new file mode 100644 index 0000000000..908778f3d7 --- /dev/null +++ b/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp @@ -0,0 +1,24 @@ +//===-- ClangObjectiveCFuzzer.cpp - Fuzz Clang ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang on a single Objective-C +/// input. This function is then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "handle-cxx/handle_cxx.h" + +using namespace clang_fuzzer; + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + std::string s(reinterpret_cast(data), size); + HandleCXX(s, "./test.m", {"-O2"}); + return 0; +} + diff --git a/tools/clang-fuzzer/Dockerfile b/tools/clang-fuzzer/Dockerfile index a0c361dd96..1ddf82954e 100644 --- a/tools/clang-fuzzer/Dockerfile +++ b/tools/clang-fuzzer/Dockerfile @@ -32,6 +32,7 @@ RUN mkdir build1 && cd build1 && cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../llv -DLLVM_USE_SANITIZER=Address -DCLANG_ENABLE_PROTO_FUZZER=ON # Build the fuzzers RUN cd build1 && ninja clang-fuzzer +RUN cd build1 && ninja clang-objc-fuzzer RUN cd build1 && ninja clang-proto-fuzzer RUN cd build1 && ninja clang-proto-to-cxx RUN cd build1 && ninja clang-loop-proto-to-cxx diff --git a/tools/clang-fuzzer/README.txt b/tools/clang-fuzzer/README.txt index 9c25afdb22..eec4a9efdf 100644 --- a/tools/clang-fuzzer/README.txt +++ b/tools/clang-fuzzer/README.txt @@ -1,15 +1,21 @@ -This directory contains two utilities for fuzzing Clang: clang-fuzzer and -clang-proto-fuzzer. Both use libFuzzer to generate inputs to clang via -coverage-guided mutation. +This directory contains three utilities for fuzzing Clang: clang-fuzzer, +clang-objc-fuzzer, and clang-proto-fuzzer. All use libFuzzer to generate inputs +to clang via coverage-guided mutation. -The two utilities differ, however, in how they structure inputs to Clang. +The three utilities differ, however, in how they structure inputs to Clang. clang-fuzzer makes no attempt to generate valid C++ programs and is therefore primarily useful for stressing the surface layers of Clang (i.e. lexer, parser). + +clang-objc-fuzzer is similar but for Objective-C: it makes no attempt to +generate a valid Objective-C program. + clang-proto-fuzzer uses a protobuf class to describe a subset of the C++ language and then uses libprotobuf-mutator to mutate instantiations of that class, producing valid C++ programs in the process. As a result, clang-proto-fuzzer is better at stressing deeper layers of Clang and LLVM. +Some of the fuzzers have example corpuses inside the corpus_examples directory. + =================================== Building clang-fuzzer =================================== @@ -35,6 +41,35 @@ Example: bin/clang-fuzzer CORPUS_DIR +=================================== + Building clang-objc-fuzzer +=================================== +Within your LLVM build directory, run CMake with the following variable +definitions: +- CMAKE_C_COMPILER=clang +- CMAKE_CXX_COMPILER=clang++ +- LLVM_USE_SANITIZE_COVERAGE=YES +- LLVM_USE_SANITIZER=Address + +Then build the clang-objc-fuzzer target. + +Example: + cd $LLVM_SOURCE_DIR + mkdir build && cd build + cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address + ninja clang-objc-fuzzer + +====================== + Running clang-objc-fuzzer +====================== + bin/clang-objc-fuzzer CORPUS_DIR + +e.g. using the example objc corpus, + + bin/clang-objc-fuzzer + + ======================================================= Building clang-proto-fuzzer (Linux-only instructions) ======================================================= diff --git a/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m b/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m new file mode 100644 index 0000000000..600feb59f5 --- /dev/null +++ b/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m @@ -0,0 +1,29 @@ +@interface RootObject +@end + +@interface BasicClass : RootObject { + int _foo; + char _boolean; +} + +@property(nonatomic, assign) int bar; +@property(atomic, retain) id objectField; +@property(nonatomic, assign) id delegate; + +- (void)someMethod; +@end + +@implementation BasicClass + +@synthesize bar = _bar; +@synthesize objectField = _objectField; +@synthesize delegate = _delegate; + +- (void)someMethod { + int value = self.bar; + _foo = (_boolean != 0) ? self.bar : [self.objectField bar]; + [self setBar:value]; + id obj = self.objectField; +} +@end + diff --git a/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m b/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m new file mode 100644 index 0000000000..b235adfc22 --- /dev/null +++ b/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m @@ -0,0 +1,20 @@ +@interface RootObject +@end + +@interface BaseClass : RootObject +@property(atomic, assign, readonly) int field; +@end + +@interface BaseClass(Private) +@property(atomic, assign, readwrite) int field; + +- (int)something; +@end + +@implementation BaseClass +- (int)something { + self.field = self.field + 1; + return self.field; +} +@end + diff --git a/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m b/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m new file mode 100644 index 0000000000..d1d3c95607 --- /dev/null +++ b/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m @@ -0,0 +1,20 @@ +@interface RootObject +@end + +@interface BaseClass : RootObject +@end + +@interface BaseClass() { + int _field1; +} +@property(atomic, assign, readonly) int field2; + +- (int)addFields; +@end + +@implementation BaseClass +- (int)addFields { + return self->_field1 + [self field2]; +} +@end + diff --git a/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m b/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m new file mode 100644 index 0000000000..282baffc58 --- /dev/null +++ b/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m @@ -0,0 +1,34 @@ +@interface RootObject ++ (instancetype)alloc; + +- (instancetype)init; +@end + +@interface BaseClass : RootObject ++ (instancetype)sharedInstance; + +- (instancetype)initWithFoo:(int)foo; +@end + +static BaseClass *sharedInstance = (void *)0; +static int counter = 0; + +@implementation BaseClass ++ (instancetype)sharedInstance { + if (sharedInstance) { + return sharedInstance; + } + sharedInstance = [[BaseClass alloc] initWithFoo:3]; + return sharedInstance; +} + + +- (instancetype)initWithFoo:(int)foo { + self = [super init]; + if (self) { + counter += foo; + } + return self; +} +@end + diff --git a/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp index bc172f2373..32d351f4c3 100644 --- a/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp +++ b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp @@ -21,12 +21,13 @@ using namespace clang; void clang_fuzzer::HandleCXX(const std::string &S, + const char *FileName, const std::vector &ExtraArgs) { llvm::opt::ArgStringList CC1Args; CC1Args.push_back("-cc1"); for (auto &A : ExtraArgs) CC1Args.push_back(A); - CC1Args.push_back("./test.cc"); + CC1Args.push_back(FileName); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions())); @@ -39,7 +40,7 @@ void clang_fuzzer::HandleCXX(const std::string &S, tooling::newInvocation(&Diagnostics, CC1Args)); std::unique_ptr Input = llvm::MemoryBuffer::getMemBuffer(S); - Invocation->getPreprocessorOpts().addRemappedFile("./test.cc", + Invocation->getPreprocessorOpts().addRemappedFile(FileName, Input.release()); std::unique_ptr action( tooling::newFrontendActionFactory()); diff --git a/tools/clang-fuzzer/handle-cxx/handle_cxx.h b/tools/clang-fuzzer/handle-cxx/handle_cxx.h index 8359bea969..2126ae9ea3 100644 --- a/tools/clang-fuzzer/handle-cxx/handle_cxx.h +++ b/tools/clang-fuzzer/handle-cxx/handle_cxx.h @@ -18,6 +18,7 @@ namespace clang_fuzzer { void HandleCXX(const std::string &S, + const char *FileName, const std::vector &ExtraArgs); } // namespace clang_fuzzer