[llvm] r262673 - [libfuzzer] arbitrary function adapter.

Mike Aizatsky via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 3 15:45:29 PST 2016


Author: aizatsky
Date: Thu Mar  3 17:45:29 2016
New Revision: 262673

URL: http://llvm.org/viewvc/llvm-project?rev=262673&view=rev
Log:
[libfuzzer] arbitrary function adapter.

The adapter automates converting sequence of bytes into arbitrary
arguments.

Differential Revision: http://reviews.llvm.org/D17829

Added:
    llvm/trunk/lib/Fuzzer/FuzzerFnAdapter.h
    llvm/trunk/lib/Fuzzer/test/FuzzerFnAdapterUnittest.cpp
    llvm/trunk/lib/Fuzzer/test/SimpleFnAdapterTest.cpp
    llvm/trunk/lib/Fuzzer/test/fuzzer-fn-adapter.test
Modified:
    llvm/trunk/lib/Fuzzer/test/CMakeLists.txt

Added: llvm/trunk/lib/Fuzzer/FuzzerFnAdapter.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/FuzzerFnAdapter.h?rev=262673&view=auto
==============================================================================
--- llvm/trunk/lib/Fuzzer/FuzzerFnAdapter.h (added)
+++ llvm/trunk/lib/Fuzzer/FuzzerFnAdapter.h Thu Mar  3 17:45:29 2016
@@ -0,0 +1,175 @@
+//===- FuzzerAdapter.h - Arbitrary function Fuzzer adapter -------*- C++ -*===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// W A R N I N G :  E X P E R I M E N T A L.
+//
+// Defines an adapter to fuzz functions with (almost) arbitrary signatures.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_ADAPTER_H
+#define LLVM_FUZZER_ADAPTER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <tuple>
+#include <vector>
+
+namespace fuzzer {
+
+/// Unpacks bytes from \p Data according to \p F argument types
+/// and calls the function.
+/// Use to automatically adapt LLVMFuzzerTestOneInput interface to
+/// a specific function.
+/// Supported argument types: primitive types, std::vector<uint8_t>.
+template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size);
+
+// The implementation performs several steps:
+// - function argument types are obtained (Args...)
+// - data is unpacked into std::tuple<Args...> one by one
+// - function is called with std::tuple<Args...> containing arguments.
+namespace impl {
+
+// Single argument unpacking.
+
+template <typename T>
+size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) {
+  if (Size < sizeof(T))
+    return Size;
+  *Value = *reinterpret_cast<const T *>(Data);
+  return Size - sizeof(T);
+}
+
+/// Unpacks into a given Value and returns the Size - num_consumed_bytes.
+/// Return value equal to Size signals inability to unpack the data (typically
+/// because there are not enough bytes).
+template <typename T>
+size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value);
+
+#define UNPACK_SINGLE_PRIMITIVE(Type)                                          \
+  template <>                                                                  \
+  size_t UnpackSingle<Type>(const uint8_t *Data, size_t Size, Type *Value) {   \
+    return UnpackPrimitive(Data, Size, Value);                                 \
+  }
+
+UNPACK_SINGLE_PRIMITIVE(char)
+UNPACK_SINGLE_PRIMITIVE(signed char)
+UNPACK_SINGLE_PRIMITIVE(unsigned char)
+
+UNPACK_SINGLE_PRIMITIVE(short int)
+UNPACK_SINGLE_PRIMITIVE(unsigned short int)
+
+UNPACK_SINGLE_PRIMITIVE(int)
+UNPACK_SINGLE_PRIMITIVE(unsigned int)
+
+UNPACK_SINGLE_PRIMITIVE(long int)
+UNPACK_SINGLE_PRIMITIVE(unsigned long int)
+
+UNPACK_SINGLE_PRIMITIVE(bool)
+UNPACK_SINGLE_PRIMITIVE(wchar_t)
+
+UNPACK_SINGLE_PRIMITIVE(float)
+UNPACK_SINGLE_PRIMITIVE(double)
+UNPACK_SINGLE_PRIMITIVE(long double)
+
+#undef UNPACK_SINGLE_PRIMITIVE
+
+template <>
+size_t UnpackSingle<std::vector<uint8_t>>(const uint8_t *Data, size_t Size,
+                                          std::vector<uint8_t> *Value) {
+  if (Size < 1)
+    return Size;
+  size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
+  std::vector<uint8_t> V(Data + 1, Data + 1 + Len);
+  Value->swap(V);
+  return Size - Len - 1;
+}
+
+// Unpacking into arbitrary tuple.
+
+// Recursion guard.
+template <int N, typename TupleT>
+typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type
+UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
+  return true;
+}
+
+// Unpack tuple elements starting from Nth.
+template <int N, typename TupleT>
+typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type
+UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
+  size_t NewSize = UnpackSingle(Data, Size, &std::get<N>(*Tuple));
+  if (NewSize == Size) {
+    return false;
+  }
+
+  return UnpackImpl<N + 1, TupleT>(Data + (Size - NewSize), NewSize, Tuple);
+}
+
+// Unpacks into arbitrary tuple and returns true if successful.
+template <typename... Args>
+bool Unpack(const uint8_t *Data, size_t Size, std::tuple<Args...> *Tuple) {
+  return UnpackImpl<0, std::tuple<Args...>>(Data, Size, Tuple);
+}
+
+// Helper integer sequence templates.
+
+template <int...> struct Seq {};
+
+template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
+
+// GenSeq<N>::type is Seq<0, 1, ..., N-1>
+template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; };
+
+// Function signature introspection.
+
+template <typename T> struct FnTraits {};
+
+template <typename ReturnType, typename... Args>
+struct FnTraits<ReturnType (*)(Args...)> {
+  enum { Arity = sizeof...(Args) };
+  typedef std::tuple<Args...> ArgsTupleT;
+};
+
+// Calling a function with arguments in a tuple.
+
+template <typename Fn, int... S>
+void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params,
+               Seq<S...>) {
+  F(std::get<S>(Params)...);
+}
+
+template <typename Fn>
+void Apply(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params) {
+  // S is Seq<0, ..., Arity-1>
+  auto S = typename GenSeq<FnTraits<Fn>::Arity>::type();
+  ApplyImpl(F, Params, S);
+}
+
+// Unpacking data into arguments tuple of correct type and calling the function.
+template <typename Fn>
+bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) {
+  typename FnTraits<Fn>::ArgsTupleT Tuple;
+  if (!Unpack(Data, Size, &Tuple))
+    return false;
+
+  Apply(F, Tuple);
+  return true;
+}
+
+} // namespace impl
+
+template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size) {
+  return impl::UnpackAndApply(F, Data, Size);
+}
+
+} // namespace fuzzer
+
+#endif

Modified: llvm/trunk/lib/Fuzzer/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/test/CMakeLists.txt?rev=262673&r1=262672&r2=262673&view=diff
==============================================================================
--- llvm/trunk/lib/Fuzzer/test/CMakeLists.txt (original)
+++ llvm/trunk/lib/Fuzzer/test/CMakeLists.txt Thu Mar  3 17:45:29 2016
@@ -27,6 +27,7 @@ set(Tests
   RepeatedMemcmp
   SimpleCmpTest
   SimpleDictionaryTest
+  SimpleFnAdapterTest
   SimpleHashTest
   SimpleTest
   StrcmpTest
@@ -90,6 +91,7 @@ include_directories(${LLVM_MAIN_SRC_DIR}
 
 add_executable(LLVMFuzzer-Unittest
   FuzzerUnittest.cpp
+  FuzzerFnAdapterUnittest.cpp
   $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
   )
 

Added: llvm/trunk/lib/Fuzzer/test/FuzzerFnAdapterUnittest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/test/FuzzerFnAdapterUnittest.cpp?rev=262673&view=auto
==============================================================================
--- llvm/trunk/lib/Fuzzer/test/FuzzerFnAdapterUnittest.cpp (added)
+++ llvm/trunk/lib/Fuzzer/test/FuzzerFnAdapterUnittest.cpp Thu Mar  3 17:45:29 2016
@@ -0,0 +1,98 @@
+#include "FuzzerFnAdapter.h"
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
+
+namespace fuzzer {
+namespace impl {
+
+template <typename... Args>
+bool Unpack(std::tuple<Args...> *Tuple, std::initializer_list<uint8_t> data) {
+  std::vector<uint8_t> V(data);
+  return Unpack(V.data(), V.size(), Tuple);
+}
+
+TEST(Unpack, Bool) {
+  std::tuple<bool> T;
+  EXPECT_TRUE(Unpack(&T, {1}));
+  EXPECT_TRUE(std::get<0>(T));
+
+  EXPECT_TRUE(Unpack(&T, {0}));
+  EXPECT_FALSE(std::get<0>(T));
+
+  EXPECT_FALSE(Unpack(&T, {}));
+}
+
+TEST(Unpack, BoolBool) {
+  std::tuple<bool, bool> T;
+  EXPECT_TRUE(Unpack(&T, {1, 0}));
+  EXPECT_TRUE(std::get<0>(T));
+  EXPECT_FALSE(std::get<1>(T));
+
+  EXPECT_TRUE(Unpack(&T, {0, 1}));
+  EXPECT_FALSE(std::get<0>(T));
+  EXPECT_TRUE(std::get<1>(T));
+
+  EXPECT_FALSE(Unpack(&T, {}));
+  EXPECT_FALSE(Unpack(&T, {10}));
+}
+
+TEST(Unpack, BoolInt) {
+  std::tuple<bool, int> T;
+  EXPECT_TRUE(Unpack(&T, {1, 16, 2, 0, 0}));
+  EXPECT_TRUE(std::get<0>(T));
+  EXPECT_EQ(528, std::get<1>(T));
+
+  EXPECT_FALSE(Unpack(&T, {1, 2}));
+}
+
+TEST(Unpack, Vector) {
+  std::tuple<std::vector<uint8_t>> T;
+  const auto &V = std::get<0>(T);
+
+  EXPECT_FALSE(Unpack(&T, {}));
+
+  EXPECT_TRUE(Unpack(&T, {0}));
+  EXPECT_EQ(0ul, V.size());
+
+  EXPECT_TRUE(Unpack(&T, {0, 1, 2, 3}));
+  EXPECT_EQ(0ul, V.size());
+
+  EXPECT_TRUE(Unpack(&T, {2}));
+  EXPECT_EQ(0ul, V.size());
+
+  EXPECT_TRUE(Unpack(&T, {2, 3}));
+  EXPECT_EQ(1ul, V.size());
+  EXPECT_EQ(3, V[0]);
+
+  EXPECT_TRUE(Unpack(&T, {2, 9, 8}));
+  EXPECT_EQ(2ul, V.size());
+  EXPECT_EQ(9, V[0]);
+  EXPECT_EQ(8, V[1]);
+}
+
+template <typename Fn>
+bool UnpackAndApply(Fn F, std::initializer_list<uint8_t> Data) {
+  std::vector<uint8_t> V(Data);
+  return UnpackAndApply(F, V.data(), V.size());
+}
+
+static void fnBool(bool b) { EXPECT_TRUE(b); }
+
+TEST(Apply, Bool) {
+  EXPECT_FALSE(UnpackAndApply(fnBool, {}));
+  EXPECT_TRUE(UnpackAndApply(fnBool, {1}));
+  EXPECT_NONFATAL_FAILURE(UnpackAndApply(fnBool, {0}),
+                          "Actual: false\nExpected: true");
+}
+
+static void fnInt(int i) { EXPECT_EQ(42, i); }
+
+TEST(Apply, Int) {
+  EXPECT_FALSE(UnpackAndApply(fnInt, {}));
+  EXPECT_TRUE(UnpackAndApply(fnInt, {42, 0, 0, 0}));
+  EXPECT_NONFATAL_FAILURE(UnpackAndApply(fnInt, {10, 0, 0, 0}),
+                          "Actual: 10\nExpected: 42");
+}
+
+} // namespace impl
+} // namespace fuzzer

Added: llvm/trunk/lib/Fuzzer/test/SimpleFnAdapterTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/test/SimpleFnAdapterTest.cpp?rev=262673&view=auto
==============================================================================
--- llvm/trunk/lib/Fuzzer/test/SimpleFnAdapterTest.cpp (added)
+++ llvm/trunk/lib/Fuzzer/test/SimpleFnAdapterTest.cpp Thu Mar  3 17:45:29 2016
@@ -0,0 +1,21 @@
+// Simple test for a fuzzer Fn adapter. The fuzzer has to find two non-empty
+// vectors with the same content.
+
+#include <iostream>
+#include <vector>
+
+#include "FuzzerFnAdapter.h"
+
+static void TestFn(std::vector<uint8_t> V1, std::vector<uint8_t> V2) {
+  if (V1.size() > 0 && V1 == V2) {
+    std::cout << "BINGO; Found the target, exiting\n";
+    exit(0);
+  }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+  fuzzer::Adapt(TestFn, Data, Size);
+  return 0;
+}
+
+

Added: llvm/trunk/lib/Fuzzer/test/fuzzer-fn-adapter.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Fuzzer/test/fuzzer-fn-adapter.test?rev=262673&view=auto
==============================================================================
--- llvm/trunk/lib/Fuzzer/test/fuzzer-fn-adapter.test (added)
+++ llvm/trunk/lib/Fuzzer/test/fuzzer-fn-adapter.test Thu Mar  3 17:45:29 2016
@@ -0,0 +1,3 @@
+RUN: LLVMFuzzer-SimpleFnAdapterTest 2>&1 | FileCheck %s
+
+CHECK: BINGO




More information about the llvm-commits mailing list