[libc-commits] [libc] e7866ea - [libc] Add fuzzing for printf floats

Michael Jones via libc-commits libc-commits at lists.llvm.org
Tue Aug 15 16:23:35 PDT 2023


Author: Michael Jones
Date: 2023-08-15T16:23:24-07:00
New Revision: e7866ea2125efe4baed98f6f9545966acf1ebad0

URL: https://github.com/llvm/llvm-project/commit/e7866ea2125efe4baed98f6f9545966acf1ebad0
DIFF: https://github.com/llvm/llvm-project/commit/e7866ea2125efe4baed98f6f9545966acf1ebad0.diff

LOG: [libc] Add fuzzing for printf floats

To guarantee accuracy for all potential float values, this patch adds a
fuzzer to compare the results for float conversions from our printf
against MPFR's.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D156495

Added: 
    libc/fuzzing/stdio/printf_float_conv_fuzz.cpp

Modified: 
    libc/fuzzing/stdio/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/fuzzing/stdio/CMakeLists.txt b/libc/fuzzing/stdio/CMakeLists.txt
index fac982c5b61730..bd7e38bc1401e5 100644
--- a/libc/fuzzing/stdio/CMakeLists.txt
+++ b/libc/fuzzing/stdio/CMakeLists.txt
@@ -7,3 +7,13 @@ add_libc_fuzzer(
   COMPILE_OPTIONS
     -DLIBC_COPT_MOCK_ARG_LIST
 )
+
+add_libc_fuzzer(
+  printf_float_conv_fuzz
+  NEED_MPFR
+  SRCS
+    printf_float_conv_fuzz.cpp
+  DEPENDS
+    libc.src.stdio.snprintf
+    libc.src.__support.FPUtil.fp_bits
+)

diff  --git a/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp b/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp
new file mode 100644
index 00000000000000..7d8335b817085e
--- /dev/null
+++ b/libc/fuzzing/stdio/printf_float_conv_fuzz.cpp
@@ -0,0 +1,123 @@
+//===-- printf_float_conv_fuzz.cpp ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Fuzzing test for llvm-libc printf %f/e/g/a implementations.
+///
+//===----------------------------------------------------------------------===//
+#include "src/stdio/snprintf.h"
+
+#include "src/__support/FPUtil/FPBits.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "utils/MPFRWrapper/mpfr_inc.h"
+
+constexpr int MAX_SIZE = 10000;
+
+inline bool simple_streq(char *first, char *second, int length) {
+  for (int i = 0; i < length; ++i) {
+    if (first[i] != second[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+enum class TestResult {
+  Success,
+  BufferSizeFailed,
+  LengthsDiffer,
+  StringsNotEqual,
+};
+
+inline TestResult test_vals(const char *fmt, double num, int prec, int width) {
+  // Call snprintf on a nullptr to get the buffer size.
+  int buffer_size = __llvm_libc::snprintf(nullptr, 0, fmt, width, prec, num);
+
+  if (buffer_size < 0) {
+    return TestResult::BufferSizeFailed;
+  }
+
+  char *test_buff = new char[buffer_size + 1];
+  char *reference_buff = new char[buffer_size + 1];
+
+  int test_result = 0;
+  int reference_result = 0;
+
+  test_result =
+      __llvm_libc::snprintf(test_buff, buffer_size + 1, fmt, width, prec, num);
+  reference_result =
+      mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);
+
+  // All of these calls should return that they wrote the same amount.
+  if (test_result != reference_result || test_result != buffer_size) {
+    return TestResult::LengthsDiffer;
+  }
+
+  if (!simple_streq(test_buff, reference_buff, buffer_size)) {
+    return TestResult::StringsNotEqual;
+  }
+
+  delete[] test_buff;
+  delete[] reference_buff;
+  return TestResult::Success;
+}
+
+constexpr char const *fmt_arr[] = {
+    "%*.*f",
+    "%*.*e",
+    "%*.*g",
+    "%*.*a",
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};
+  // data = raw_data;
+  // size = sizeof(raw_data);
+  double num = 0.0;
+  int prec = 0;
+  int width = 0;
+
+  __llvm_libc::fputil::FPBits<double>::UIntType raw_num = 0;
+
+  // Copy as many bytes of data as will fit into num, prec, and with. Any extras
+  // are ignored.
+  for (size_t cur = 0; cur < size; ++cur) {
+    if (cur < sizeof(raw_num)) {
+      raw_num = (raw_num << 8) + data[cur];
+    } else if (cur < sizeof(raw_num) + sizeof(prec)) {
+      prec = (prec << 8) + data[cur];
+    } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {
+      width = (width << 8) + data[cur];
+    }
+  }
+
+  num = __llvm_libc::fputil::FPBits<double>(raw_num).get_val();
+
+  if (width > MAX_SIZE) {
+    width = MAX_SIZE;
+  } else if (width < -MAX_SIZE) {
+    width = -MAX_SIZE;
+  }
+
+  if (prec > MAX_SIZE) {
+    prec = MAX_SIZE;
+  } else if (prec < -MAX_SIZE) {
+    prec = -MAX_SIZE;
+  }
+
+  for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);
+       ++cur_fmt) {
+    TestResult result = test_vals(fmt_arr[cur_fmt], num, prec, width);
+    if (result != TestResult::Success) {
+      __builtin_trap();
+    }
+  }
+  return 0;
+}


        


More information about the libc-commits mailing list