1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <fuzzer/FuzzedDataProvider.h>
18 #include "osi/include/allocation_tracker.h"
19
20 #define MAX_NUM_FUNCTIONS 512
21 #define MAX_BUF_SIZE 256
22
23 // Add a tracker_initialized bool to track if we initialized or not
24 // (This is to handle a call to allocation_tracker_notify_alloc immediately
25 // returning the provided pointer if the allocator is not ready, and
26 // notify_free on the same ptr failing as the allocator did not
27 // track that allocation)
28 bool tracker_initialized = false;
29
30 struct alloc_struct {
31 allocator_id_t alloc_id;
32 void* ptr;
33 };
34
freeAllocationVector(std::vector<alloc_struct> * alloc_vector)35 void freeAllocationVector(std::vector<alloc_struct>* alloc_vector) {
36 // Free our allocated buffers
37 for (const auto& alloc : *alloc_vector) {
38 void* real_ptr = allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr);
39 if (real_ptr) {
40 free(real_ptr);
41 }
42 }
43 alloc_vector->clear();
44 }
45
callArbitraryFunction(std::vector<alloc_struct> * alloc_vector,FuzzedDataProvider * dataProvider)46 void callArbitraryFunction(std::vector<alloc_struct>* alloc_vector,
47 FuzzedDataProvider* dataProvider) {
48 // Get our function identifier
49 switch (dataProvider->ConsumeIntegralInRange<char>(0, 6)) {
50 // Let 0 be a NO-OP, as ConsumeIntegral will return 0 on an empty buffer
51 // (This will likely bias whatever action is here to run more often)
52 case 0:
53 return;
54 // Init
55 case 1:
56 allocation_tracker_init();
57 tracker_initialized = true;
58 return;
59 case 2:
60 // NOTE: This will print to stderr if allocations exist. May clutter logs
61 allocation_tracker_expect_no_allocations();
62 return;
63 case 3: {
64 alloc_struct alloc;
65 // Determine allocator ID & buffer size (without canaries)
66 alloc.alloc_id = dataProvider->ConsumeIntegral<allocator_id_t>();
67 size_t size =
68 dataProvider->ConsumeIntegralInRange<size_t>(1, MAX_BUF_SIZE);
69 if (size == 0) {
70 return;
71 }
72 // Get our size with canaries & allocate
73 size_t real_size = allocation_tracker_resize_for_canary(size);
74 void* tmp_ptr = malloc(real_size);
75 if (tmp_ptr == nullptr) {
76 return;
77 }
78 alloc.ptr =
79 allocation_tracker_notify_alloc(alloc.alloc_id, tmp_ptr, size);
80 // Put our id/ptr pair in our tracking vector to be freed later
81 if (tracker_initialized && alloc.ptr) {
82 alloc_vector->push_back(alloc);
83 } else {
84 free(tmp_ptr);
85 }
86 }
87 return;
88 case 4: {
89 // Grab a ptr from our tracking vector & free it
90 if (!alloc_vector->empty()) {
91 size_t index = dataProvider->ConsumeIntegralInRange<size_t>(
92 0, alloc_vector->size() - 1);
93 alloc_struct alloc = alloc_vector->at(index);
94 void* real_ptr =
95 allocation_tracker_notify_free(alloc.alloc_id, alloc.ptr);
96 if (real_ptr) {
97 free(real_ptr);
98 }
99 alloc_vector->erase(alloc_vector->begin() + index);
100 }
101 }
102 return;
103 case 5: {
104 size_t size =
105 dataProvider->ConsumeIntegralInRange<size_t>(0, MAX_BUF_SIZE);
106 allocation_tracker_resize_for_canary(size);
107 }
108 return;
109 // Reset
110 // NOTE: Should this be exempted from fuzzing? Header says to not call this,
111 // but it's still exposed. It also doesn't perform a full reset.
112 case 6:
113 // Have to actually free the mem first as reset doesn't do it
114 freeAllocationVector(alloc_vector);
115 allocation_tracker_reset();
116 return;
117 default:
118 return;
119 }
120 }
121
LLVMFuzzerTestOneInput(const uint8_t * Data,size_t Size)122 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
123 // Init our wrapper
124 FuzzedDataProvider dataProvider(Data, Size);
125
126 // Keep a vector of our allocated pointers
127 std::vector<alloc_struct> alloc_vector;
128
129 // How many functions are we going to call?
130 size_t num_functions =
131 dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_NUM_FUNCTIONS);
132 for (size_t i = 0; i < num_functions; i++) {
133 callArbitraryFunction(&alloc_vector, &dataProvider);
134 }
135
136 // Free anything we've allocated over the course of the fuzzer loop
137 freeAllocationVector(&alloc_vector);
138
139 // Reset our tracker for the next run
140 allocation_tracker_reset();
141 return 0;
142 }
143