| //===----------------------------------------------------------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Differential fuzz test for llvm-libc inet_ntop implementation. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "src/arpa/inet/inet_ntop.h" | |
| #include <arpa/inet.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| if (size < 1) | |
| return 0; | |
| uint8_t af_selector = data[0]; | |
| int af = (af_selector & 0x80) ? AF_INET : AF_INET6; | |
| socklen_t dst_size = data[0] % 64; | |
| // Extract address bytes. We allocate 16 bytes for IPv6, IPv4 will only use 4. | |
| uint8_t address_bytes[16] = {0}; | |
| size_t payload_size = size - 1; | |
| size_t copy_size = payload_size > 16 ? 16 : payload_size; | |
| memcpy(address_bytes, data + 1, copy_size); | |
| // Setup buffers for dst | |
| constexpr size_t BUFFER_SIZE = 128; | |
| char ref_dst[BUFFER_SIZE]; | |
| char impl_dst[BUFFER_SIZE]; | |
| constexpr uint8_t SENTINEL = 0x5a; | |
| memset(ref_dst, SENTINEL, BUFFER_SIZE); | |
| memset(impl_dst, SENTINEL, BUFFER_SIZE); | |
| // Call reference implementation | |
| const char *ref_res = ::inet_ntop(af, address_bytes, ref_dst, dst_size); | |
| // Call our implementation | |
| const char *impl_res = | |
| LIBC_NAMESPACE::inet_ntop(af, address_bytes, impl_dst, dst_size); | |
| auto print_details = [&]() { | |
| fprintf(stderr, | |
| "Details:\n" | |
| " af: %d\n" | |
| " dst_size: %lu\n" | |
| " address_bytes: %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x " | |
| "%02x%02x %02x%02x %02x%02x\n" | |
| " ref_res: %s\n" | |
| " impl_res: %s\n", | |
| af, static_cast<unsigned long>(dst_size), address_bytes[0], | |
| address_bytes[1], address_bytes[2], address_bytes[3], | |
| address_bytes[4], address_bytes[5], address_bytes[6], | |
| address_bytes[7], address_bytes[8], address_bytes[9], | |
| address_bytes[10], address_bytes[11], address_bytes[12], | |
| address_bytes[13], address_bytes[14], address_bytes[15], | |
| ref_res ? ref_res : "nullptr", impl_res ? impl_res : "nullptr"); | |
| }; | |
| // Compare results | |
| if ((ref_res == nullptr) != (impl_res == nullptr)) { | |
| fprintf(stderr, "Success/failure mismatch!\n"); | |
| print_details(); | |
| __builtin_trap(); | |
| } | |
| if (ref_res != nullptr) { | |
| // Both succeeded. | |
| // Check that returned pointers are correct | |
| if (ref_res != ref_dst || impl_res != impl_dst) { | |
| fprintf(stderr, "Returned pointer does not match destination buffer!\n"); | |
| print_details(); | |
| __builtin_trap(); | |
| } | |
| // Check that strings match | |
| if (strcmp(ref_res, impl_res) != 0) { | |
| fprintf(stderr, "Output string mismatch!\n"); | |
| print_details(); | |
| __builtin_trap(); | |
| } | |
| } | |
| // Check for out-of-bounds writes | |
| for (size_t i = dst_size; i < BUFFER_SIZE; ++i) { | |
| if (impl_dst[i] != SENTINEL) { | |
| fprintf(stderr, | |
| "Out-of-bounds write detected at index %zu (expected 0x%02x, got " | |
| "0x%02x)!\n", | |
| i, SENTINEL, static_cast<uint8_t>(impl_dst[i])); | |
| print_details(); | |
| __builtin_trap(); | |
| } | |
| } | |
| return 0; | |
| } |