brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.0 KiB · eefe78c Raw
142 lines · cpp
1//===-- printf_float_conv_fuzz.cpp ----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// Fuzzing test for llvm-libc printf %f/e/g/a implementations.10///11//===----------------------------------------------------------------------===//12#include "src/stdio/snprintf.h"13 14#include "src/__support/FPUtil/FPBits.h"15 16#include <stddef.h>17#include <stdint.h>18 19#include "utils/MPFRWrapper/mpfr_inc.h"20 21constexpr int MAX_SIZE = 10000;22 23inline bool simple_streq(char *first, char *second, int length) {24  for (int i = 0; i < length; ++i) {25    if (first[i] != second[i]) {26      return false;27    }28  }29  return true;30}31 32inline int simple_strlen(const char *str) {33  int i = 0;34  for (; *str; ++str, ++i) {35    ;36  }37  return i;38}39 40enum class TestResult {41  Success,42  BufferSizeFailed,43  LengthsDiffer,44  StringsNotEqual,45};46 47template <typename F>48inline TestResult test_vals(const char *fmt, F num, int prec, int width) {49  // Call snprintf on a nullptr to get the buffer size.50  int buffer_size = LIBC_NAMESPACE::snprintf(nullptr, 0, fmt, width, prec, num);51 52  if (buffer_size < 0) {53    return TestResult::BufferSizeFailed;54  }55 56  char *test_buff = new char[buffer_size + 1];57  char *reference_buff = new char[buffer_size + 1];58 59  int test_result = 0;60  int reference_result = 0;61 62  test_result = LIBC_NAMESPACE::snprintf(test_buff, buffer_size + 1, fmt, width,63                                         prec, num);64  reference_result =65      mpfr_snprintf(reference_buff, buffer_size + 1, fmt, width, prec, num);66 67  // All of these calls should return that they wrote the same amount.68  if (test_result != reference_result || test_result != buffer_size) {69    return TestResult::LengthsDiffer;70  }71 72  if (!simple_streq(test_buff, reference_buff, buffer_size)) {73    return TestResult::StringsNotEqual;74  }75 76  delete[] test_buff;77  delete[] reference_buff;78  return TestResult::Success;79}80 81constexpr char const *fmt_arr[] = {82    "%*.*f", "%*.*e", "%*.*g", "%*.*a", "%*.*Lf", "%*.*Le", "%*.*Lg", "%*.*La",83};84 85extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {86  // const uint8_t raw_data[] = {0x30,0x27,0x1,0x0,0x0,0x0,0x0,0x0,0x24};87  // data = raw_data;88  // size = sizeof(raw_data);89  double num = 0.0;90  int prec = 0;91  int width = 0;92 93  LIBC_NAMESPACE::fputil::FPBits<double>::StorageType raw_num = 0;94 95  // Copy as many bytes of data as will fit into num, prec, and with. Any extras96  // are ignored.97  for (size_t cur = 0; cur < size; ++cur) {98    if (cur < sizeof(raw_num)) {99      raw_num = (raw_num << 8) + data[cur];100    } else if (cur < sizeof(raw_num) + sizeof(prec)) {101      prec = (prec << 8) + data[cur];102    } else if (cur < sizeof(raw_num) + sizeof(prec) + sizeof(width)) {103      width = (width << 8) + data[cur];104    }105  }106 107  num = LIBC_NAMESPACE::fputil::FPBits<double>(raw_num).get_val();108 109  // While we could create a "ld_raw_num" from additional bytes, it's much110  // easier to stick with simply casting num to long double. This avoids the111  // issues around 80 bit long doubles, especially unnormal and pseudo-denormal112  // numbers, which MPFR doesn't handle well.113  long double ld_num = static_cast<long double>(num);114 115  if (width > MAX_SIZE) {116    width = MAX_SIZE;117  } else if (width < -MAX_SIZE) {118    width = -MAX_SIZE;119  }120 121  if (prec > MAX_SIZE) {122    prec = MAX_SIZE;123  } else if (prec < -MAX_SIZE) {124    prec = -MAX_SIZE;125  }126 127  for (size_t cur_fmt = 0; cur_fmt < sizeof(fmt_arr) / sizeof(char *);128       ++cur_fmt) {129    int fmt_len = simple_strlen(fmt_arr[cur_fmt]);130    TestResult result;131    if (fmt_arr[cur_fmt][fmt_len - 2] == 'L') {132      result = test_vals<long double>(fmt_arr[cur_fmt], ld_num, prec, width);133    } else {134      result = test_vals<double>(fmt_arr[cur_fmt], num, prec, width);135    }136    if (result != TestResult::Success) {137      __builtin_trap();138    }139  }140  return 0;141}142