Index: tools/clang-fuzzer/CMakeLists.txt =================================================================== --- tools/clang-fuzzer/CMakeLists.txt +++ 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 @@ -107,6 +108,7 @@ endif() add_clang_subdirectory(handle-cxx) +add_clang_subdirectory(handle-objc) add_clang_subdirectory(handle-llvm) add_clang_executable(clang-fuzzer @@ -120,3 +122,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} + clangHandleObjC + ) Index: tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp =================================================================== --- /dev/null +++ tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp @@ -0,0 +1,25 @@ +//===-- 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-objc/handle_objc.h" + +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); + HandleObjC(s, {"-O2"}); + return 0; +} Index: tools/clang-fuzzer/Dockerfile =================================================================== --- tools/clang-fuzzer/Dockerfile +++ 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 Index: tools/clang-fuzzer/README.txt =================================================================== --- tools/clang-fuzzer/README.txt +++ 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) ======================================================= Index: tools/clang-fuzzer/corpus_examples/objc/BasicClass.m =================================================================== --- /dev/null +++ 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 + Index: tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m =================================================================== --- /dev/null +++ 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 + Index: tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m =================================================================== --- /dev/null +++ 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 + Index: tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m =================================================================== --- /dev/null +++ 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 + Index: tools/clang-fuzzer/handle-objc/CMakeLists.txt =================================================================== --- /dev/null +++ tools/clang-fuzzer/handle-objc/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support) + +add_clang_library(clangHandleObjC + handle_objc.cpp + + LINK_LIBS + clangBasic + clangCodeGen + clangFrontend + clangLex + clangSerialization + clangTooling + ) Index: tools/clang-fuzzer/handle-objc/handle_objc.h =================================================================== --- /dev/null +++ tools/clang-fuzzer/handle-objc/handle_objc.h @@ -0,0 +1,24 @@ +//==-- handle_objc.h - Helper function for Clang fuzzers -------------------==// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Defines HandleObjC for use by the Clang Objective-C fuzzers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_OBJC_HANDLEOBJC_H +#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_OBJC_HANDLEOBJC_H + +#include +#include + +namespace clang_fuzzer { +void HandleObjC(const std::string &S, + const std::vector &ExtraArgs); +} // namespace clang_fuzzer + +#endif Index: tools/clang-fuzzer/handle-objc/handle_objc.cpp =================================================================== --- /dev/null +++ tools/clang-fuzzer/handle-objc/handle_objc.cpp @@ -0,0 +1,51 @@ +//==-- handle_objc.cpp - Helper function for Clang fuzzers -----------------==// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implements HandleObjC for use by the Clang Objective-C fuzzers. +// +//===----------------------------------------------------------------------===// + +#include "handle_objc.h" + +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Option/Option.h" + +using namespace clang; + +void clang_fuzzer::HandleObjC(const std::string &S, + const std::vector &ExtraArgs) { + llvm::opt::ArgStringList CC1Args; + CC1Args.push_back("-cc1"); + for (auto &A : ExtraArgs) + CC1Args.push_back(A); + CC1Args.push_back("./test.m"); + + llvm::IntrusiveRefCntPtr Files( + new FileManager(FileSystemOptions())); + IgnoringDiagConsumer Diags; + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, + &Diags, false); + std::unique_ptr Invocation( + tooling::newInvocation(&Diagnostics, CC1Args)); + std::unique_ptr Input = + llvm::MemoryBuffer::getMemBuffer(S); + Invocation->getPreprocessorOpts().addRemappedFile("./test.m", + Input.release()); + std::unique_ptr action( + tooling::newFrontendActionFactory()); + std::shared_ptr PCHContainerOps = + std::make_shared(); + action->runInvocation(std::move(Invocation), Files.get(), PCHContainerOps, + &Diags); +} +