Index: lib/Fuzzer/CMakeLists.txt =================================================================== --- lib/Fuzzer/CMakeLists.txt +++ lib/Fuzzer/CMakeLists.txt @@ -2,9 +2,12 @@ # Disable the coverage and sanitizer instrumentation for the fuzzer itself. set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -mpopcnt -fno-sanitize=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters -Werror") if( LLVM_USE_SANITIZE_COVERAGE ) - if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") + if((NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address") AND + (NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address;Undefined") AND + (NOT "${LLVM_USE_SANITIZER}" STREQUAL "Undefined;Address")) message(FATAL_ERROR - "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and " + "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address " + "(or LLVM_USE_SANITIZER=Address;Undefined) and " "LLVM_USE_SANITIZE_COVERAGE=YES to be set." ) endif() Index: lib/Support/APInt.cpp =================================================================== --- lib/Support/APInt.cpp +++ lib/Support/APInt.cpp @@ -1042,7 +1042,11 @@ if (isSingleWord()) { if (shiftAmt == BitWidth) return APInt(BitWidth, 0); // undefined - return APInt(BitWidth, SignExtend64(VAL, BitWidth) >> shiftAmt); + else { + unsigned SignBit = APINT_BITS_PER_WORD - BitWidth; + return APInt(BitWidth, + (((int64_t(VAL) << SignBit) >> SignBit) >> shiftAmt)); + } } // If all the bits were shifted out, the result is, technically, undefined. Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -39,6 +39,7 @@ add_llvm_tool_subdirectory(llvm-config) add_llvm_tool_subdirectory(llvm-lto) add_llvm_tool_subdirectory(llvm-profdata) +add_llvm_tool_subdirectory(llvm-apint-fuzzer) # Projects supported via LLVM_EXTERNAL_*_SOURCE_DIR need to be explicitly # specified. Index: tools/llvm-apint-fuzzer/CMakeLists.txt =================================================================== --- tools/llvm-apint-fuzzer/CMakeLists.txt +++ tools/llvm-apint-fuzzer/CMakeLists.txt @@ -0,0 +1,10 @@ +if( LLVM_USE_SANITIZE_COVERAGE ) + set(LLVM_LINK_COMPONENTS + Support + ) + add_llvm_tool(llvm-apint-fuzzer + llvm-apint-fuzzer.cpp) + target_link_libraries(llvm-apint-fuzzer + LLVMFuzzer + ) +endif() Index: tools/llvm-apint-fuzzer/llvm-apint-fuzzer.cpp =================================================================== --- tools/llvm-apint-fuzzer/llvm-apint-fuzzer.cpp +++ tools/llvm-apint-fuzzer/llvm-apint-fuzzer.cpp @@ -0,0 +1,65 @@ +//===--- fuzz-llvm-as.cpp - Fuzzer for llvm-as using lib/Fuzzer -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +using namespace llvm; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) + return 0; + uint32_t ShiftAmount = llvm::support::endian::read32le(Data); + Data += 4; + Size -= 4; + + if (Size < 8) + return 0; + uint64_t Value = llvm::support::endian::read64be(Data); + Data += 8; + Size -= 8; + + if (Size < 1) + return 0; + uint32_t Bits = Data[0]; + Data += 1; + Size -= 1; + + // Limitation of APInt::ashr(): Bits can't be 0. + Bits = std::max(Bits, 1u); + + // Limitation of APInt::ashr(): ShiftAmount can't be > Bits. + ShiftAmount = std::min(ShiftAmount, Bits); + + APInt Input(Bits, Value, true); + + // Check that: + // (X >> ShiftAmount) >> 1 + // can be commuted to: + // (X >> 1) >> ShiftAmount + auto Fst = Input.ashr(ShiftAmount).ashr(1); + auto Snd = Input.ashr(1).ashr(ShiftAmount); + + if (Fst != Snd) + abort(); + + // Sanity check: this should crash when ubsan'd, assuming the fix to APInt + // isn't applied: + const APInt neg_one(64, static_cast(-1), true); + if (neg_one != neg_one.ashr(7)) + abort(); + + return 0; +}