diff --git a/clang/tools/clang-fuzzer/CMakeLists.txt b/clang/tools/clang-fuzzer/CMakeLists.txt --- a/clang/tools/clang-fuzzer/CMakeLists.txt +++ b/clang/tools/clang-fuzzer/CMakeLists.txt @@ -12,6 +12,7 @@ # 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 @@ ${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/clang/tools/clang-fuzzer/ClangFuzzer.cpp b/clang/tools/clang-fuzzer/ClangFuzzer.cpp --- a/clang/tools/clang-fuzzer/ClangFuzzer.cpp +++ b/clang/tools/clang-fuzzer/ClangFuzzer.cpp @@ -20,6 +20,6 @@ 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/clang/tools/clang-fuzzer/ClangFuzzer.cpp b/clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp copy from clang/tools/clang-fuzzer/ClangFuzzer.cpp copy to clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp --- a/clang/tools/clang-fuzzer/ClangFuzzer.cpp +++ b/clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp @@ -1,4 +1,4 @@ -//===-- ClangFuzzer.cpp - Fuzz Clang --------------------------------------===// +//===-- 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. @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// /// /// \file -/// This file implements a function that runs Clang on a single -/// input. This function is then linked into the Fuzzer library. +/// This file implements a function that runs Clang on a single Objective-C +/// input. This function is then linked into the Fuzzer library. /// //===----------------------------------------------------------------------===// @@ -16,10 +16,9 @@ using namespace clang_fuzzer; -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"}); + std::string s(reinterpret_cast(data), size); + HandleCXX(s, "./test.m", {"-O2"}); return 0; } + diff --git a/clang/tools/clang-fuzzer/Dockerfile b/clang/tools/clang-fuzzer/Dockerfile --- a/clang/tools/clang-fuzzer/Dockerfile +++ b/clang/tools/clang-fuzzer/Dockerfile @@ -32,6 +32,7 @@ -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/clang/tools/clang-fuzzer/README.txt b/clang/tools/clang-fuzzer/README.txt --- a/clang/tools/clang-fuzzer/README.txt +++ b/clang/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 @@ 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/clang/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m b/clang/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m new file mode 100644 --- /dev/null +++ b/clang/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/clang/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m b/clang/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m new file mode 100644 --- /dev/null +++ b/clang/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/clang/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m b/clang/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m new file mode 100644 --- /dev/null +++ b/clang/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/clang/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m b/clang/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m new file mode 100644 --- /dev/null +++ b/clang/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/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h b/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h --- a/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h +++ b/clang/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 diff --git a/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp b/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp --- a/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp +++ b/clang/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 @@ 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());