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