| //===-- weak_avl_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 weak AVL implementations. |
| /// |
| //===----------------------------------------------------------------------===// |
| #include "hdr/types/ENTRY.h" |
| #include "src/__support/CPP/bit.h" |
| #include "src/__support/CPP/optional.h" |
| #include "src/__support/macros/config.h" |
| #include "src/__support/weak_avl.h" |
| |
| namespace LIBC_NAMESPACE_DECL { |
| |
| // A sequence of actions: |
| // - Erase: a single byte valued (5, 6 mod 7) followed by an int |
| // - Find: a single byte valued (4 mod 7) followed by an int |
| // - FindOrInsert: a single byte valued (0,1,2,3 mod 7) followed by an int |
| extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size); |
| extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, |
| size_t max_size, unsigned int seed) { |
| size = LLVMFuzzerMutate(data, size, max_size); |
| return size / (1 + sizeof(int)) * (1 + sizeof(int)); |
| } |
| |
| class AVLTree { |
| using Node = WeakAVLNode<int>; |
| Node *root = nullptr; |
| bool reversed = false; |
| static int compare(int a, int b) { return (a > b) - (a < b); } |
| static int reverse_compare(int a, int b) { return (b > a) - (b < a); } |
| |
| public: |
| AVLTree(bool reversed = false) : reversed(reversed) {} |
| bool find(int key) { |
| return Node::find(root, key, reversed ? reverse_compare : compare) |
| .has_value(); |
| } |
| bool find_or_insert(int key) { |
| return Node::find_or_insert(root, key, reversed ? reverse_compare : compare) |
| .has_value(); |
| } |
| bool erase(int key) { |
| if (cpp::optional<Node *> node = |
| Node::find(root, key, reversed ? reverse_compare : compare)) { |
| Node::erase(root, node.value()); |
| return true; |
| } |
| return false; |
| } |
| ~AVLTree() { Node::destroy(root); } |
| }; |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| AVLTree tree1; |
| AVLTree tree2(true); |
| for (size_t i = 0; i + (1 + sizeof(int)) <= size; i += 1 + sizeof(int)) { |
| uint8_t action = data[i]; |
| int key; |
| __builtin_memcpy(&key, data + i + 1, sizeof(int)); |
| if (action % 7 == 4) { |
| // Find |
| bool res1 = tree1.find(key); |
| bool res2 = tree2.find(key); |
| if (res1 != res2) |
| __builtin_trap(); |
| |
| } else if (action % 7 == 5 || action % 7 == 6) { |
| // Erase |
| bool res1 = tree1.erase(key); |
| bool res2 = tree2.erase(key); |
| if (res1 != res2) |
| __builtin_trap(); |
| if (tree1.find(key)) |
| __builtin_trap(); |
| if (tree2.find(key)) |
| __builtin_trap(); |
| } else { |
| // FindOrInsert |
| bool res1 = tree1.find_or_insert(key); |
| bool res2 = tree2.find_or_insert(key); |
| if (res1 != res2) |
| __builtin_trap(); |
| if (!tree1.find(key)) |
| __builtin_trap(); |
| if (!tree2.find(key)) |
| __builtin_trap(); |
| } |
| } |
| return 0; |
| } |
| |
| } // namespace LIBC_NAMESPACE_DECL |