diff options
| author | Michael Ensing <michael.ensing@leviathansecurity.com> | 2020-05-01 16:00:17 -0700 |
|---|---|---|
| committer | Michael Ensing <michael.ensing@leviathansecurity.com> | 2020-05-13 22:30:13 -0700 |
| commit | f0b8122ab9b1696a80988e2b51d3da3d715f46a8 (patch) | |
| tree | 496eeceafd826f217a091d9cd0d48d7254526651 | |
| parent | c5ca7d3a766f84d58119d18d8df738975204c7a9 (diff) | |
Add fuzzers for 10 libosi components
Added fuzzers, most of which may be run either on host or on device.
Note that coverage will differ based on arch. Additionally, alarm
will not successfully run on host due to the reliance on hooking
system functions, and compat may not be useful on-device, as libosi's
compat.h function definitions, and therefore the fuzzer calls, are
wrapped in an '#if __GLIBC__'.
Test: Tested on a Pixel 3a with no starting corpus and a short run,
resulting in the following statistics:
- alarm 15% cov ~280 exec/s
- allocation_tracker: 61% cov ~2.5k exec/s
- allocator: 34% cov ~24k exec/s
- array: 22% cov ~16k exec/s
- buffer: 22% cov ~26k exec/s
- compat: N/A N/A
- fixed_queue: 45% cov ~8k exec/s
- future 22% cov ~83k exec/s
- list 46% cov ~21k exec/s
- ringbuffer 38% cov ~20k exec/s
Signed-off-by: Michael Ensing <michael.ensing@leviathansecurity.com>
Change-Id: I9203c804941beaa470940452f39bf352d725eb02
22 files changed, 1531 insertions, 0 deletions
diff --git a/system/osi/test/fuzzers/Android.bp b/system/osi/test/fuzzers/Android.bp new file mode 100644 index 0000000000..c39c0fd49b --- /dev/null +++ b/system/osi/test/fuzzers/Android.bp @@ -0,0 +1,8 @@ +cc_defaults { + name: "libosi_fuzz_defaults", + defaults: ["fluoride_osi_defaults"], + host_supported: true, + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/alarm/Android.bp b/system/osi/test/fuzzers/alarm/Android.bp new file mode 100644 index 0000000000..51b5e5601a --- /dev/null +++ b/system/osi/test/fuzzers/alarm/Android.bp @@ -0,0 +1,21 @@ +cc_fuzz { + name: "libosi_fuzz_alarm", + defaults: ["libosi_fuzz_defaults"], + host_supported: false, + srcs: [ + "fuzz_alarm.cc", + ], + shared_libs: [ + "liblog", + "libprotobuf-cpp-lite", + "libcutils", + "libcrypto", + ], + static_libs: [ + "libbt-common", + "libbt-protos-lite", + "libgmock", + "libosi", + ], + cflags: [ "-Wno-unused-function" ], +} diff --git a/system/osi/test/fuzzers/alarm/fuzz_alarm.cc b/system/osi/test/fuzzers/alarm/fuzz_alarm.cc new file mode 100644 index 0000000000..e4e6a252e3 --- /dev/null +++ b/system/osi/test/fuzzers/alarm/fuzz_alarm.cc @@ -0,0 +1,151 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/alarm.h" +#include "osi/include/semaphore.h" + +#include "common/message_loop_thread.h" + +using base::Closure; +using base::TimeDelta; +using bluetooth::common::MessageLoopThread; + +#define MAX_CONCURRENT_ALARMS 25 +#define MAX_BUFFER_LEN 4096 +#define MAX_ALARM_DURATION 25 + +static semaphore_t* semaphore; +static int cb_counter; +static base::MessageLoop* message_loop_; + +base::MessageLoop* get_main_message_loop() { return message_loop_; } + +static void cb(void* data) { + ++cb_counter; + semaphore_post(semaphore); +} + +void setup() { + cb_counter = 0; + semaphore = semaphore_new(0); +} +void teardown() { semaphore_free(semaphore); } + +alarm_t* fuzz_init_alarm(FuzzedDataProvider* dataProvider) { + size_t name_len = + dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_LEN); + std::vector<char> alarm_name_vect = + dataProvider->ConsumeBytesWithTerminator<char>(name_len, '\0'); + char* alarm_name = alarm_name_vect.data(); + + // Determine if our alarm will be periodic + if (dataProvider->ConsumeBool()) { + return alarm_new_periodic(alarm_name); + } else { + return alarm_new(alarm_name); + } +} + +bool fuzz_set_alarm(alarm_t* alarm, uint64_t interval, alarm_callback_t cb, + FuzzedDataProvider* dataProvider) { + // Generate a random buffer (or null) + void* data_buffer = nullptr; + size_t buff_len = + dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_LEN); + if (buff_len == 0) { + return false; + } + + // allocate our space + std::vector<uint8_t> data_vector = + dataProvider->ConsumeBytes<uint8_t>(buff_len); + data_buffer = data_vector.data(); + + // Make sure alarm is non-null + if (alarm) { + // Should this alarm be regular or on mloop? + if (dataProvider->ConsumeBool()) { + alarm_set_on_mloop(alarm, interval, cb, data_buffer); + } else { + alarm_set(alarm, interval, cb, data_buffer); + } + } + + return true; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Perform setup + setup(); + + alarm_t* alarm = nullptr; + // Should our alarm be valid or null? + if (dataProvider.ConsumeBool()) { + // Init our alarm + alarm = fuzz_init_alarm(&dataProvider); + } + + // Set up the alarm & cancel + // Alarm must be non-null, or set() will trigger assert + if (alarm) { + if (!fuzz_set_alarm(alarm, MAX_ALARM_DURATION, cb, &dataProvider)) { + return 0; + } + alarm_cancel(alarm); + } + + // Check if scheduled + alarm_is_scheduled(alarm); + + if (alarm) { + // Set up another set of alarms & let these ones run + int num_alarms = + dataProvider.ConsumeIntegralInRange<uint8_t>(0, MAX_CONCURRENT_ALARMS); + for (int i = 0; i < num_alarms; i++) { + uint64_t interval = + dataProvider.ConsumeIntegralInRange<uint64_t>(0, MAX_ALARM_DURATION); + if (fuzz_set_alarm(alarm, interval, cb, &dataProvider)) { + return 0; + } + alarm_get_remaining_ms(alarm); + } + + // Wait for them to complete + for (int i = 1; i <= num_alarms; i++) { + semaphore_wait(semaphore); + } + } + + // Free the alarm object + alarm_free(alarm); + + // dump debug data to /dev/null + int debug_fd = open("/dev/null", O_RDWR); + alarm_debug_dump(debug_fd); + + // Cleanup + alarm_cleanup(); + + // Perform teardown + teardown(); + + return 0; +} diff --git a/system/osi/test/fuzzers/allocation_tracker/Android.bp b/system/osi/test/fuzzers/allocation_tracker/Android.bp new file mode 100644 index 0000000000..83194daf38 --- /dev/null +++ b/system/osi/test/fuzzers/allocation_tracker/Android.bp @@ -0,0 +1,14 @@ +cc_fuzz { + name: "libosi_fuzz_allocation_tracker", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_allocation_tracker.cc", + ], + shared_libs: [ + "liblog", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc b/system/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc new file mode 100644 index 0000000000..69299d0ba5 --- /dev/null +++ b/system/osi/test/fuzzers/allocation_tracker/fuzz_allocation_tracker.cc @@ -0,0 +1,132 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/allocation_tracker.h" + +#define MAX_NUM_FUNCTIONS 512 +#define MAX_BUF_SIZE 256 + +struct alloc_struct { + allocator_id_t alloc_id; + void* ptr; +}; + +void freeAllocationVector(std::vector<alloc_struct>* alloc_vector) { + // Free our allocated buffers + for (const auto& alloc : *alloc_vector) { + void* real_ptr = allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr); + if (real_ptr) { + free(real_ptr); + } + } + alloc_vector->clear(); +} + +void callArbitraryFunction(std::vector<alloc_struct>* alloc_vector, + FuzzedDataProvider* dataProvider) { + // Get our function identifier + switch (dataProvider->ConsumeIntegralInRange<char>(0, 6)) { + // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer + // (This will likely bias whatever action is here to run more often) + case 0: + return; + // Init + case 1: + allocation_tracker_init(); + return; + case 2: + // NOTE: This will print to stderr if allocations exist. May clutter logs + allocation_tracker_expect_no_allocations(); + return; + case 3: { + alloc_struct alloc; + // Determine allocator ID & buffer size (without canaries) + alloc.alloc_id = dataProvider->ConsumeIntegral<allocator_id_t>(); + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE); + if (size == 0) { + return; + } + // Get our size with canaries & allocate + size_t real_size = allocation_tracker_resize_for_canary(size); + void* tmp_ptr = malloc(real_size); + if (tmp_ptr == nullptr) { + return; + } + alloc.ptr = + allocation_tracker_notify_alloc(alloc.alloc_id, tmp_ptr, size); + // Put our id/ptr pair in our tracking vector to be freed later + if (alloc.ptr) { + alloc_vector->push_back(alloc); + } + } + return; + case 4: { + // Grab a ptr from our tracking vector & free it + if (!alloc_vector->empty()) { + size_t index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, alloc_vector->size() - 1); + alloc_struct alloc = alloc_vector->at(index); + void* real_ptr = + allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr); + if (real_ptr) { + free(real_ptr); + } + alloc_vector->erase(alloc_vector->begin() + index); + } + } + return; + case 5: { + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE); + allocation_tracker_resize_for_canary(size); + } + return; + // Reset + // NOTE: Should this be exempted from fuzzing? Header says to not call this, + // but it's still exposed. It also doesn't perform a full reset. + case 6: + // Have to actually free the mem first as reset doesn't do it + freeAllocationVector(alloc_vector); + allocation_tracker_reset(); + return; + default: + return; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Keep a vector of our allocated pointers + std::vector<alloc_struct> alloc_vector; + + // How many functions are we going to call? + size_t num_functions = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS); + for (size_t i = 0; i < num_functions; i++) { + callArbitraryFunction(&alloc_vector, &dataProvider); + } + + // Free anything we've allocated over the course of the fuzzer loop + freeAllocationVector(&alloc_vector); + + // Reset our tracker for the next run + allocation_tracker_reset(); + return 0; +} diff --git a/system/osi/test/fuzzers/allocator/Android.bp b/system/osi/test/fuzzers/allocator/Android.bp new file mode 100644 index 0000000000..90db372852 --- /dev/null +++ b/system/osi/test/fuzzers/allocator/Android.bp @@ -0,0 +1,11 @@ +cc_fuzz { + name: "libosi_fuzz_allocator", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_allocator.cc", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/allocator/fuzz_allocator.cc b/system/osi/test/fuzzers/allocator/fuzz_allocator.cc new file mode 100644 index 0000000000..bdfd99a1d4 --- /dev/null +++ b/system/osi/test/fuzzers/allocator/fuzz_allocator.cc @@ -0,0 +1,119 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/allocator.h" +#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h" + +#define MAX_NUM_FUNCTIONS 512 +#define MAX_BUF_SIZE 256 + +void callArbitraryFunction(std::vector<void*>* alloc_vector, + FuzzedDataProvider* dataProvider) { + // Get our function identifier + char func_id = dataProvider->ConsumeIntegralInRange<char>(0, 6); + + switch (func_id) { + // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer + // (This will likely bias whatever action is here to run more often) + case 0: + return; + // Let case 1 be osi_malloc, and 2 be osi_calloc + case 1: + case 2: { + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE); + void* ptr = nullptr; + if (size == 0) { + return; + } + if (func_id == 1) { + ptr = osi_malloc(size); + } else { + ptr = osi_calloc(size); + } + if (ptr) { + alloc_vector->push_back(ptr); + } + } + return; + // Let case 3 be osi_free, and 4 be osi_free_and_reset + case 3: + case 4: { + if (alloc_vector->size() == 0) { + return; + } + size_t index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, alloc_vector->size() - 1); + void* ptr = alloc_vector->at(index); + if (ptr) { + if (func_id == 3) { + osi_free(ptr); + } else { + osi_free_and_reset(&ptr); + } + } + alloc_vector->erase(alloc_vector->begin() + index); + } + return; + // Let case 5 be osi_strdup, and 6 be osi_strdup + case 5: + case 6: { + // Make a src buffer + char* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, true); + char* str = nullptr; + if (buf == nullptr) { + return; + } + if (func_id == 5) { + str = osi_strdup(buf); + } else { + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE); + str = osi_strndup(buf, size); + } + free(buf); + if (str) { + alloc_vector->push_back(str); + } + } + return; + default: + return; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Keep a vector of our allocated objects for freeing later + std::vector<void*> alloc_vector; + // Call some functions, create some buffers + size_t num_functions = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS); + for (size_t i = 0; i < num_functions; i++) { + callArbitraryFunction(&alloc_vector, &dataProvider); + } + // Free anything we've allocated + for (const auto& alloc : alloc_vector) { + if (alloc != nullptr) { + osi_free(alloc); + } + } + alloc_vector.clear(); + return 0; +} diff --git a/system/osi/test/fuzzers/array/Android.bp b/system/osi/test/fuzzers/array/Android.bp new file mode 100644 index 0000000000..ffcaf8c614 --- /dev/null +++ b/system/osi/test/fuzzers/array/Android.bp @@ -0,0 +1,14 @@ +cc_fuzz { + name: "libosi_fuzz_array", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_array.cc", + ], + shared_libs: [ + "liblog", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/array/fuzz_array.cc b/system/osi/test/fuzzers/array/fuzz_array.cc new file mode 100644 index 0000000000..eeabf952d5 --- /dev/null +++ b/system/osi/test/fuzzers/array/fuzz_array.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/array.h" + +// Capping the element size at sizeof(uint32_t)+1 +// because it looks like there's a buffer overread +#define MAX_ELEMENT_SIZE sizeof(uint32_t) +#define MAX_ARRAY_LEN 1024 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Attempt to init an array + size_t element_size = + dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_ELEMENT_SIZE); + array_t* arr = array_new(element_size); + + // Functions can only be called on a non-null array_t, according to the .h + if (arr != nullptr) { + // How large do we want our array? + size_t arr_len = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_ARRAY_LEN); + if (arr_len > 0) { + for (size_t i = 0; i < arr_len; i++) { + uint32_t new_val = dataProvider.ConsumeIntegral<uint32_t>(); + // append_value() just derefs and calls append_ptr(), + // so no need to fuzz separately + array_append_value(arr, new_val); + } + + // Pull the ptr to an element in the array + size_t get_index = + dataProvider.ConsumeIntegralInRange<size_t>(0, array_length(arr) - 1); + array_at(arr, get_index); + + // Grab the array pointer + array_ptr(arr); + } + } + + // Free the array (this can be performed on a nullptr) + array_free(arr); + + return 0; +} diff --git a/system/osi/test/fuzzers/buffer/Android.bp b/system/osi/test/fuzzers/buffer/Android.bp new file mode 100644 index 0000000000..3467e0d890 --- /dev/null +++ b/system/osi/test/fuzzers/buffer/Android.bp @@ -0,0 +1,14 @@ +cc_fuzz { + name: "libosi_fuzz_buffer", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_buffer.cc", + ], + shared_libs: [ + "liblog", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/buffer/fuzz_buffer.cc b/system/osi/test/fuzzers/buffer/fuzz_buffer.cc new file mode 100644 index 0000000000..b781a3171c --- /dev/null +++ b/system/osi/test/fuzzers/buffer/fuzz_buffer.cc @@ -0,0 +1,70 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/buffer.h" + +#define MAX_BUFFER_SIZE 4096 +#define MAX_NUM_SLICES 100 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Create our buffer + size_t buf_size = + dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_SIZE); + buffer_t* buf = buffer_new(buf_size); + + // These functions require a non-null buffer, according to the header + // The size also needs to be over 1 to make slices + if (buf != nullptr && buf_size > 1) { + std::vector<buffer_t*> slices; + + // Make a bunch of refs to various slices of the buffer + size_t num_slices = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_SLICES); + for (size_t i = 0; i < num_slices; i++) { + // If slice_size is zero or GT buf_size, lib throws an exception + size_t slice_size = + dataProvider.ConsumeIntegralInRange<size_t>(1, buf_size - 1); + if (slice_size > 0) { + buffer_t* new_slice = nullptr; + if (slice_size == buf_size) { + new_slice = buffer_new_ref(buf); + } else { + new_slice = buffer_new_slice(buf, slice_size); + } + + // Add the slice to our vector so we can free it later + slices.push_back(new_slice); + } + } + + // Retrieve the buffer ptr + buffer_ptr(buf); + + // Free the slices + for (const auto& slice : slices) { + buffer_free(slice); + } + } + + // Free the root buffer + buffer_free(buf); + + return 0; +} diff --git a/system/osi/test/fuzzers/compat/Android.bp b/system/osi/test/fuzzers/compat/Android.bp new file mode 100644 index 0000000000..3d9178b1e7 --- /dev/null +++ b/system/osi/test/fuzzers/compat/Android.bp @@ -0,0 +1,15 @@ +cc_fuzz { + name: "libosi_fuzz_compat", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_compat.cc", + ], + shared_libs: [ + "liblog", + "libcutils", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/compat/fuzz_compat.cc b/system/osi/test/fuzzers/compat/fuzz_compat.cc new file mode 100644 index 0000000000..3feeaeb1cc --- /dev/null +++ b/system/osi/test/fuzzers/compat/fuzz_compat.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/compat.h" + +#define MAX_BUFFER_SIZE 4096 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { +// Our functions are only defined with __GLIBC__ +#if __GLIBC__ + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + size_t buf_size = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_SIZE); + if (buf_size == 0) { + return 0; + } + + // Set up our buffers + // NOTE: If the src buffer is not NULL-terminated, the strlcpy will + // overread regardless of the len arg. Force null-term for now. + std::vector<char> bytes = + dataProvider.ConsumeBytesWithTerminator<char>(buf_size, '\0'); + if (bytes.empty()) { + return 0; + } + buf_size = bytes.size(); + void* dst_buf = malloc(buf_size); + if (dst_buf == nullptr) { + return 0; + } + + // Call the getId fn just to ensure things don't crash + gettid(); + + // Copy, then concat + size_t len_to_cpy = dataProvider.ConsumeIntegralInRange<size_t>(0, buf_size); + strlcpy(reinterpret_cast<char*>(dst_buf), + reinterpret_cast<char*>(bytes.data()), len_to_cpy); + strlcat(reinterpret_cast<char*>(dst_buf), + reinterpret_cast<char*>(bytes.data()), len_to_cpy); + + // Clear out our dest buffer + free(dst_buf); +#endif + + return 0; +} diff --git a/system/osi/test/fuzzers/fixed_queue/Android.bp b/system/osi/test/fuzzers/fixed_queue/Android.bp new file mode 100644 index 0000000000..d21ee7ce13 --- /dev/null +++ b/system/osi/test/fuzzers/fixed_queue/Android.bp @@ -0,0 +1,15 @@ +cc_fuzz { + name: "libosi_fuzz_fixed_queue", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_fixed_queue.cc", + ], + shared_libs: [ + "liblog", + "libcutils", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc b/system/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc new file mode 100644 index 0000000000..f8a426a6a7 --- /dev/null +++ b/system/osi/test/fuzzers/fixed_queue/fuzz_fixed_queue.cc @@ -0,0 +1,240 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include <sys/select.h> +#include "osi/include/fixed_queue.h" +#include "osi/include/future.h" +#include "osi/include/thread.h" +#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h" + +#define MAX_START_SIZE 2048 +#define MAX_NUM_FUNCTIONS 512 +#define MAX_BUF_SIZE 512 + +static future_t* received_message_future = nullptr; + +// Empty callback function +void fqFreeCb(void* data) {} +void fqCb(fixed_queue_t* queue, void* data) { + void* msg = fixed_queue_try_dequeue(queue); + future_ready(received_message_future, msg); +} + +// Returns either a nullptr or a function ptr to the placeholder cb function +fixed_queue_free_cb cbOrNull(FuzzedDataProvider* dataProvider) { + bool null_cb = dataProvider->ConsumeBool(); + if (null_cb) { + return nullptr; + } else { + return fqFreeCb; + } +} + +bool fdIsAvailable(int fd) { + int nfds = 1; + fd_set readfds, writefds, exceptfds; + timeval timeout; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(fd, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 50; + + return select(nfds, &readfds, &writefds, &exceptfds, &timeout) > 0; +} + +void createNewFuture() { + // Free the existing future if it exists + if (received_message_future != nullptr) { + future_ready(received_message_future, nullptr); + future_await(received_message_future); + } + + // Create a new one + received_message_future = future_new(); +} + +void callArbitraryFunction(fixed_queue_t* fixed_queue, + std::vector<void*>* live_buffer_vector, + std::vector<thread_t*>* live_thread_vector, + FuzzedDataProvider* dataProvider) { + void* buf_ptr = nullptr; + size_t index = 0; + int fd = 0; + // Get our function identifier + switch (dataProvider->ConsumeIntegralInRange<char>(0, 17)) { + // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer + // (This will likely bias whatever action is here to run more often) + case 0: + return; + // Clear the queue + case 1: + fixed_queue_flush(fixed_queue, cbOrNull(dataProvider)); + return; + // Check if empty + case 2: + fixed_queue_is_empty(fixed_queue); + return; + // Check length + case 3: + fixed_queue_length(fixed_queue); + return; + // Check capacity (Cannot be null) + case 4: + if (fixed_queue) { + fixed_queue_capacity(fixed_queue); + } + return; + // Add to the queue (Cannot be null) + case 5: + if (fixed_queue) { + buf_ptr = generateBuffer(dataProvider, MAX_BUF_SIZE, false); + live_buffer_vector->push_back(buf_ptr); + if (buf_ptr) { + // Make sure we won't block + fd = fixed_queue_get_enqueue_fd(fixed_queue); + if (fdIsAvailable(fd)) { + fixed_queue_enqueue(fixed_queue, buf_ptr); + } + } + } + return; + case 6: + if (fixed_queue) { + buf_ptr = generateBuffer(dataProvider, MAX_BUF_SIZE, false); + live_buffer_vector->push_back(buf_ptr); + if (buf_ptr) { + fixed_queue_try_enqueue(fixed_queue, buf_ptr); + } + } + return; + // Remove from the queue (Cannot be null) + case 7: + if (fixed_queue && fixed_queue_length(fixed_queue) > 0) { + fixed_queue_dequeue(fixed_queue); + } + return; + case 8: + if (fixed_queue) { + fixed_queue_try_dequeue(fixed_queue); + } + return; + // Peeks + case 9: + fixed_queue_try_peek_first(fixed_queue); + return; + case 10: + fixed_queue_try_peek_last(fixed_queue); + return; + // Try to remove existing specific element + case 11: + if (live_buffer_vector->empty()) { + return; + } + // Grab an existing buffer + index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, live_buffer_vector->size() - 1); + buf_ptr = live_buffer_vector->at(index); + if (buf_ptr != nullptr) { + fixed_queue_try_remove_from_queue(fixed_queue, buf_ptr); + } + return; + // Try to remove nonexistant element + case 12: + buf_ptr = + reinterpret_cast<void*>(dataProvider->ConsumeIntegral<uint64_t>()); + if (buf_ptr != nullptr) { + fixed_queue_try_remove_from_queue(fixed_queue, buf_ptr); + } + return; + // Convert the queue to a list (Cannot be null) + case 13: + if (fixed_queue) { + fixed_queue_get_list(fixed_queue); + } + return; + // Check if enqueue is blocking + case 14: + fixed_queue_get_enqueue_fd(fixed_queue); + return; + // Check if dequeue is blocking + case 15: + fixed_queue_get_dequeue_fd(fixed_queue); + return; + // NOTE: thread appears to have a memleak, disabling this for now. + case 16: + // if (fixed_queue) { + // createNewFuture(); + // // Start up a thread and register with it. + // thread_t* tmp_thread = thread_new( + // dataProvider->ConsumeRandomLengthString().c_str()); + // if (tmp_thread == nullptr) { + // return; + // } + // live_thread_vector->push_back(tmp_thread); + // reactor_t* reactor = thread_get_reactor(tmp_thread); + // if (reactor == nullptr) { + // return; + // } + // fixed_queue_register_dequeue(fixed_queue, reactor, fqCb, nullptr); + // fixed_queue_enqueue(fixed_queue, (void*)"test"); + // future_await(received_message_future); + // } + return; + case 17: + fixed_queue_unregister_dequeue(fixed_queue); + return; + default: + return; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Make vectors to keep track of objects we generate, for freeing + std::vector<void*> live_buffer_vector; + std::vector<thread_t*> live_thread_vector; + + size_t start_capacity = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_START_SIZE); + fixed_queue_t* fixed_queue = fixed_queue_new(start_capacity); + + // How many functions are we going to call? + size_t num_functions = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS); + for (size_t i = 0; i < num_functions; i++) { + callArbitraryFunction(fixed_queue, &live_buffer_vector, &live_thread_vector, + &dataProvider); + } + + // Free our queue (with either a null or placeholder callback) + fixed_queue_free(fixed_queue, cbOrNull(&dataProvider)); + + // Free buffers we've created through fn calls during this fuzzer loop. + for (const auto& buffer : live_buffer_vector) { + free(buffer); + } + for (const auto& thread : live_thread_vector) { + thread_free(thread); + } + + return 0; +} diff --git a/system/osi/test/fuzzers/future/Android.bp b/system/osi/test/fuzzers/future/Android.bp new file mode 100644 index 0000000000..4fdbafe4ec --- /dev/null +++ b/system/osi/test/fuzzers/future/Android.bp @@ -0,0 +1,15 @@ +cc_fuzz { + name: "libosi_fuzz_future", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_future.cc", + ], + shared_libs: [ + "liblog", + "libcutils", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/future/fuzz_future.cc b/system/osi/test/fuzzers/future/fuzz_future.cc new file mode 100644 index 0000000000..1df3e7da0f --- /dev/null +++ b/system/osi/test/fuzzers/future/fuzz_future.cc @@ -0,0 +1,58 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/future.h" + +#define MAX_BUFFER_SIZE 8 + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // The value of this result ptr shouldn't matter, but make a buffer to be safe + size_t buf_size = + dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_BUFFER_SIZE); + void* buf = malloc(buf_size); + if (buf == nullptr) { + return 0; + } + std::vector<uint8_t> bytes = dataProvider.ConsumeBytes<uint8_t>(buf_size); + memcpy(buf, bytes.data(), bytes.size()); + + // Is our future an immediate? + future_t* future = nullptr; + bool is_immediate = dataProvider.ConsumeBool(); + if (is_immediate) { + future = future_new_immediate(buf); + } else { + future = future_new(); + } + + // These functions require a non-null object, according to the header + if (future != nullptr) { + // If we need to, specify that the future is ready + if (!is_immediate) { + future_ready(future, buf); + } + + // Free the object + future_await(future); + } + + free(buf); + return 0; +} diff --git a/system/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h b/system/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h new file mode 100644 index 0000000000..395b85ec77 --- /dev/null +++ b/system/osi/test/fuzzers/include/libosiFuzzHelperFunctions.h @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBOSI_FUZZ_HELPERS_H_ +#define LIBOSI_FUZZ_HELPERS_H_ + +#include <fuzzer/FuzzedDataProvider.h> +#include <vector> + +char* generateBuffer(FuzzedDataProvider* dataProvider, size_t max_buffer_size, + bool null_terminate) { + // Get our buffer size + size_t buf_size = + dataProvider->ConsumeIntegralInRange<size_t>(0, max_buffer_size); + if (buf_size == 0) { + return nullptr; + } + + // Allocate and copy in data + char* buf = reinterpret_cast<char*>(malloc(buf_size)); + std::vector<char> bytes = dataProvider->ConsumeBytes<char>(buf_size); + memcpy(buf, bytes.data(), bytes.size()); + + if (null_terminate) { + // Force a null-termination + buf[buf_size - 1] = 0x00; + } + + return buf; +} + +#endif // LIBOSI_FUZZ_HELPERS_H_ diff --git a/system/osi/test/fuzzers/list/Android.bp b/system/osi/test/fuzzers/list/Android.bp new file mode 100644 index 0000000000..2b664bcae1 --- /dev/null +++ b/system/osi/test/fuzzers/list/Android.bp @@ -0,0 +1,11 @@ +cc_fuzz { + name: "libosi_fuzz_list", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_list.cc", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/list/fuzz_list.cc b/system/osi/test/fuzzers/list/fuzz_list.cc new file mode 100644 index 0000000000..a5814740a1 --- /dev/null +++ b/system/osi/test/fuzzers/list/fuzz_list.cc @@ -0,0 +1,277 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/list.h" +#include "osi/test/fuzzers/include/libosiFuzzHelperFunctions.h" + +#define MAX_NUM_FUNCTIONS 512 +#define MAX_BUF_SIZE 256 + +struct list_node_t { + struct list_node_t* next; + void* data; +}; + +void cb(void* data) {} +// Pass a ptr to FuzzedDataProvider in context +bool list_iter_cb_impl(void* data, void* context) { + FuzzedDataProvider* dataProvider = + reinterpret_cast<FuzzedDataProvider*>(context); + return dataProvider->ConsumeBool(); +} + +list_t* createList(FuzzedDataProvider* dataProvider) { + bool should_callback = dataProvider->ConsumeBool(); + if (should_callback) { + return list_new(cb); + } else { + return list_new(nullptr); + } +} + +void* getArbitraryElement(std::vector<void*>* vector, + FuzzedDataProvider* dataProvider) { + if (vector->size() == 0) { + return nullptr; + } + // Get an index + size_t index = + dataProvider->ConsumeIntegralInRange<size_t>(0, vector->size() - 1); + return vector->at(index); +} + +list_node_t* getArbitraryNode(list_t* list, FuzzedDataProvider* dataProvider) { + if (list == nullptr || list_is_empty(list)) { + return nullptr; + } + size_t index = + dataProvider->ConsumeIntegralInRange<size_t>(0, list_length(list) - 1); + list_node_t* node = list_begin(list); + for (size_t i = 0; i < index; i++) { + node = node->next; + } + + return node; +} + +void callArbitraryFunction(std::vector<void*>* list_vector, + std::vector<void*>* alloc_vector, + FuzzedDataProvider* dataProvider) { + list_t* list = nullptr; + // Get our function identifier + switch (dataProvider->ConsumeIntegralInRange<char>(0, 18)) { + // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer + // (This will likely bias whatever action is here to run more often) + case 0: + return; + // Create a new list + case 1: + list = createList(dataProvider); + list_vector->push_back(list); + return; + // Free a list + case 2: { + size_t index = 0; + if (list_vector->size() > 0) { + // Get an index + index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, list_vector->size() - 1); + list = reinterpret_cast<list_t*>(list_vector->at(index)); + } + list_free(list); + // Otherwise free a valid list + if (list != nullptr) { + list_vector->erase(list_vector->begin() + index); + } + return; + } + case 3: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_is_empty(list); + } + return; + case 4: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + void* search_buf = getArbitraryElement(alloc_vector, dataProvider); + if (search_buf != nullptr) { + list_contains(list, search_buf); + } + } + return; + case 5: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_length(list); + } + return; + case 6: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr && !list_is_empty(list)) { + list_front(list); + } + return; + case 7: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr && !list_is_empty(list)) { + list_back(list); + } + return; + case 8: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr && !list_is_empty(list)) { + list_back_node(list); + } + return; + case 9: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list == nullptr) { + return; + } + void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false); + alloc_vector->push_back(buf); + list_node_t* node = getArbitraryNode(list, dataProvider); + if (node != nullptr && buf != nullptr) { + list_insert_after(list, node, buf); + } + return; + } + case 10: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false); + alloc_vector->push_back(buf); + if (list != nullptr && buf != nullptr) { + list_prepend(list, buf); + } + return; + } + case 11: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + void* buf = generateBuffer(dataProvider, MAX_BUF_SIZE, false); + alloc_vector->push_back(buf); + if (list != nullptr && buf != nullptr) { + list_append(list, buf); + } + return; + } + case 12: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + // The buffer will be valid, but may be for a different list + void* buf = getArbitraryElement(alloc_vector, dataProvider); + if (list != nullptr && buf != nullptr) { + list_remove(list, buf); + } + return; + } + case 13: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_clear(list); + } + return; + case 14: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_foreach(list, list_iter_cb_impl, dataProvider); + } + return; + case 15: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_begin(list); + } + return; + case 16: + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list != nullptr) { + list_end(list); + } + return; + case 17: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list == nullptr) { + return; + } + list_node_t* node = getArbitraryNode(list, dataProvider); + if (node != nullptr) { + list_next(node); + } + return; + } + case 18: { + list = reinterpret_cast<list_t*>( + getArbitraryElement(list_vector, dataProvider)); + if (list == nullptr) { + return; + } + list_node_t* node = getArbitraryNode(list, dataProvider); + if (node != nullptr && node != list_end(list)) { + list_node(node); + } + return; + } + default: + return; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Keep a vector of our allocated objects for freeing later + std::vector<void*> list_vector; + std::vector<void*> alloc_vector; + + // Call some functions, create some buffers + size_t num_functions = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS); + for (size_t i = 0; i < num_functions; i++) { + callArbitraryFunction(&list_vector, &alloc_vector, &dataProvider); + } + + // Free anything we've allocated + for (const auto& list : list_vector) { + if (list != nullptr) { + list_free(reinterpret_cast<list_t*>(list)); + } + } + for (const auto& alloc : alloc_vector) { + if (alloc != nullptr) { + free(alloc); + } + } + list_vector.clear(); + + return 0; +} diff --git a/system/osi/test/fuzzers/ringbuffer/Android.bp b/system/osi/test/fuzzers/ringbuffer/Android.bp new file mode 100644 index 0000000000..4343e02bb7 --- /dev/null +++ b/system/osi/test/fuzzers/ringbuffer/Android.bp @@ -0,0 +1,11 @@ +cc_fuzz { + name: "libosi_fuzz_ringbuffer", + defaults: ["libosi_fuzz_defaults"], + host_supported: true, + srcs: [ + "fuzz_ringbuffer.cc", + ], + static_libs: [ + "libosi", + ], +} diff --git a/system/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc b/system/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc new file mode 100644 index 0000000000..adc219c22c --- /dev/null +++ b/system/osi/test/fuzzers/ringbuffer/fuzz_ringbuffer.cc @@ -0,0 +1,167 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include "osi/include/ringbuffer.h" + +#define MAX_NUM_FUNCTIONS 512 +#define MAX_BUF_SIZE 2048 + +ringbuffer_t* getArbitraryRingBuf(std::vector<ringbuffer_t*>* ringbuf_vector, + FuzzedDataProvider* dataProvider) { + if (ringbuf_vector->empty()) { + return nullptr; + } + + size_t index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, ringbuf_vector->size() - 1); + return ringbuf_vector->at(index); +} + +void callArbitraryFunction(std::vector<ringbuffer_t*>* ringbuf_vector, + FuzzedDataProvider* dataProvider) { + // Get our function identifier + char func_id = dataProvider->ConsumeIntegralInRange<char>(0, 8); + + ringbuffer_t* buf = nullptr; + switch (func_id) { + // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer + // (This will likely bias whatever action is here to run more often) + case 0: + return; + case 1: { + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE); + buf = ringbuffer_init(size); + if (buf) { + ringbuf_vector->push_back(buf); + } + } + return; + case 2: { + if (ringbuf_vector->empty()) { + return; + } + size_t index = dataProvider->ConsumeIntegralInRange<size_t>( + 0, ringbuf_vector->size() - 1); + buf = ringbuf_vector->at(index); + if (buf) { + ringbuffer_free(buf); + ringbuf_vector->erase(ringbuf_vector->begin() + index); + } + } + return; + case 3: + buf = getArbitraryRingBuf(ringbuf_vector, dataProvider); + if (buf) { + ringbuffer_available(buf); + } + return; + case 4: + buf = getArbitraryRingBuf(ringbuf_vector, dataProvider); + if (buf) { + ringbuffer_size(buf); + } + return; + case 5: { + buf = getArbitraryRingBuf(ringbuf_vector, dataProvider); + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE); + if (buf == nullptr || size == 0) { + return; + } + void* src_buf = malloc(size); + if (src_buf == nullptr) { + return; + } + std::vector<uint8_t> bytes = dataProvider->ConsumeBytes<uint8_t>(size); + memcpy(src_buf, bytes.data(), bytes.size()); + + ringbuffer_insert(buf, reinterpret_cast<uint8_t*>(src_buf), size); + free(src_buf); + } + return; + case 6: + case 7: { + buf = getArbitraryRingBuf(ringbuf_vector, dataProvider); + if (buf == nullptr) { + return; + } + size_t max_size = ringbuffer_size(buf); + if (max_size == 0) { + return; + } + size_t size = dataProvider->ConsumeIntegralInRange<size_t>(1, max_size); + + // NOTE: 0-size may be a valid case, that crashes currently. + if (size == 0) { + return; + } + + void* dst_buf = malloc(size); + if (dst_buf == nullptr) { + return; + } + if (func_id == 6) { + off_t offset = dataProvider->ConsumeIntegral<off_t>(); + if (offset >= 0 && + static_cast<size_t>(offset) <= ringbuffer_size(buf)) { + ringbuffer_peek(buf, offset, reinterpret_cast<uint8_t*>(dst_buf), + size); + } + } else { + ringbuffer_pop(buf, reinterpret_cast<uint8_t*>(dst_buf), size); + } + free(dst_buf); + } + return; + case 8: { + buf = getArbitraryRingBuf(ringbuf_vector, dataProvider); + size_t size = + dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE); + if (buf) { + ringbuffer_delete(buf, size); + } + } + return; + default: + return; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // Init our wrapper + FuzzedDataProvider dataProvider(Data, Size); + + // Keep a vector of our allocated objects for freeing later + std::vector<ringbuffer_t*> ringbuf_vector; + + // Call some functions, create some buffers + size_t num_functions = + dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS); + for (size_t i = 0; i < num_functions; i++) { + callArbitraryFunction(&ringbuf_vector, &dataProvider); + } + + // Free anything we've allocated + for (const auto& ringbuf : ringbuf_vector) { + if (ringbuf != nullptr) { + ringbuffer_free(ringbuf); + } + } + ringbuf_vector.clear(); + return 0; +} |
