From: Kostya Serebryany Date: Thu, 14 May 2015 22:41:49 +0000 (+0000) Subject: [lib/Fuzzer] Add SHA1 implementation from public domain. X-Git-Tag: llvmorg-3.7.0-rc1~4557 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=96eab65d81db648d30c819dfa86e3af9c29e0566;p=platform%2Fupstream%2Fllvm.git [lib/Fuzzer] Add SHA1 implementation from public domain. Summary: This adds a SHA1 implementation taken from public domain code. The change is trivial, but as it involves third-party code I'd like a second pair of eyes before commit. LibFuzzer can not use SHA1 from openssl because openssl may not be available and because we may be fuzzing openssl itself. Using sha1sum via a pipe is too slow. Test Plan: n/a Reviewers: chandlerc Reviewed By: chandlerc Subscribers: majnemer, llvm-commits Differential Revision: http://reviews.llvm.org/D9733 llvm-svn: 237400 --- diff --git a/llvm/lib/Fuzzer/CMakeLists.txt b/llvm/lib/Fuzzer/CMakeLists.txt index 736950d..49d7f8f 100644 --- a/llvm/lib/Fuzzer/CMakeLists.txt +++ b/llvm/lib/Fuzzer/CMakeLists.txt @@ -10,6 +10,7 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerLoop.cpp FuzzerMutate.cpp FuzzerSanitizerOptions.cpp + FuzzerSHA1.cpp FuzzerUtil.cpp ) add_library(LLVMFuzzer STATIC diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 496b6db..8926189 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -44,6 +44,11 @@ std::string Hash(const Unit &U); void SetTimer(int Seconds); void PrintFileAsBase64(const std::string &Path); +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + int NumberOfCpuCores(); class Fuzzer { diff --git a/llvm/lib/Fuzzer/FuzzerSHA1.cpp b/llvm/lib/Fuzzer/FuzzerSHA1.cpp new file mode 100644 index 0000000..b42a0485 --- /dev/null +++ b/llvm/lib/Fuzzer/FuzzerSHA1.cpp @@ -0,0 +1,202 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerInternal.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +#elif defined __LITTLE_ENDIAN__ +/* override */ +#elif defined __BYTE_ORDER +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#else // ! defined __LITTLE_ENDIAN__ +# include // machine/endian.h +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SHA_BIG_ENDIAN +# endif +#endif + + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t* sha1_result(sha1nfo *s); + + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); +} + +void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); +} + +void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); +} + +uint8_t* sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } +#endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; +} + +} // namespace; Added for LibFuzzer + +// The rest is added for LibFuzzer +void fuzzer::ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char*)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); +} diff --git a/llvm/lib/Fuzzer/FuzzerUtil.cpp b/llvm/lib/Fuzzer/FuzzerUtil.cpp index 2fb6e05..c4b0afa 100644 --- a/llvm/lib/Fuzzer/FuzzerUtil.cpp +++ b/llvm/lib/Fuzzer/FuzzerUtil.cpp @@ -10,6 +10,8 @@ //===----------------------------------------------------------------------===// #include "FuzzerInternal.h" +#include +#include #include #include #include @@ -35,44 +37,13 @@ void PrintASCII(const Unit &U, const char *PrintAfter) { std::cerr << PrintAfter; } -// Try to compute a SHA1 sum of this Unit using an external 'sha1sum' command. -// We can not use the SHA1 function from openssl directly because -// a) openssl may not be available, -// b) we may be fuzzing openssl itself. -// This is all very sad, suggestions are welcome. -static std::string TrySha1(const Unit &in) { - char TempPath[] = "/tmp/fuzzer-tmp-XXXXXX"; - int FD = mkstemp(TempPath); - if (FD < 0) return ""; - ssize_t Written = write(FD, in.data(), in.size()); - close(FD); - if (static_cast(Written) != in.size()) return ""; - - std::string Cmd = "sha1sum < "; - Cmd += TempPath; - FILE *F = popen(Cmd.c_str(), "r"); - if (!F) return ""; - char Sha1[41]; - fgets(Sha1, sizeof(Sha1), F); - fclose(F); - - unlink(TempPath); - return Sha1; -} - -std::string Hash(const Unit &in) { - std::string Sha1 = TrySha1(in); - if (!Sha1.empty()) - return Sha1; - - size_t h1 = 0, h2 = 0; - for (auto x : in) { - h1 += x; - h1 *= 5; - h2 += x; - h2 *= 7; - } - return std::to_string(h1) + std::to_string(h2); +std::string Hash(const Unit &U) { + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Hash[i]; + return SS.str(); } static void AlarmHandler(int, siginfo_t *, void *) { diff --git a/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp b/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp index e337ca8..7b429b7 100644 --- a/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp +++ b/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp @@ -60,3 +60,11 @@ TEST(Fuzzer, CrossOver) { EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits); } } + +TEST(Fuzzer, Hash) { + uint8_t A[] = {'a', 'b', 'c'}; + fuzzer::Unit U(A, A + sizeof(A)); + EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U)); + U.push_back('d'); + EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U)); +}