1 /*
2 * Copyright (C) 2023 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 <linux/android/binder.h>
18
19 #include <android-base/logging.h>
20
21 #include <binder/Parcel.h>
22 #include <binder/RecordedTransaction.h>
23
24 #include <fuzzseeds/random_parcel_seeds.h>
25
26 #include <stack>
27 #include <string>
28 #include "../../file.h"
29
30 using android::binder::borrowed_fd;
31 using android::binder::WriteFully;
32 using std::stack;
33
34 extern size_t kRandomInterfaceLength;
35 // Keep this in sync with max_length in random_binder.cpp while creating a RandomBinder
36 std::string kRandomInterfaceName(kRandomInterfaceLength, 'i');
37
38 namespace android {
39 namespace impl {
40 template <typename T>
reverseBytes(T min,T max,T val)41 std::vector<uint8_t> reverseBytes(T min, T max, T val) {
42 uint64_t range = static_cast<uint64_t>(max) - min;
43 uint64_t result = val - min;
44 size_t offset = 0;
45
46 std::vector<uint8_t> reverseData;
47 uint8_t reversed = 0;
48 reversed |= result;
49
50 while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0) {
51 reverseData.push_back(reversed);
52 reversed = 0;
53 reversed |= (result >> CHAR_BIT);
54 result = result >> CHAR_BIT;
55 offset += CHAR_BIT;
56 }
57
58 return std::move(reverseData);
59 }
60
61 template <typename T>
writeReversedBuffer(std::vector<uint8_t> & integralBuffer,T min,T max,T val)62 void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T min, T max, T val) {
63 std::vector<uint8_t> reversedData = reverseBytes(min, max, val);
64 // ConsumeIntegral Calls read buffer from the end. Keep inserting at the front of the buffer
65 // so that we can align fuzzService operations with seed generation for readability.
66 integralBuffer.insert(integralBuffer.begin(), reversedData.begin(), reversedData.end());
67 }
68
69 template <typename T>
writeReversedBuffer(std::vector<uint8_t> & integralBuffer,T val)70 void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T val) {
71 // For ConsumeIntegral<T>() calls, FuzzedDataProvider uses numeric limits min and max
72 // as range
73 writeReversedBuffer(integralBuffer, std::numeric_limits<T>::min(),
74 std::numeric_limits<T>::max(), val);
75 }
76
77 } // namespace impl
78
79 struct ProviderMetadata {
80 size_t position;
81 size_t value;
82
ProviderMetadataandroid::ProviderMetadata83 ProviderMetadata() {
84 value = 0;
85 position = 0;
86 }
87 };
88
89 // Assuming current seed path is inside the fillRandomParcel function, start of the loop.
writeRandomBinder(borrowed_fd fd,vector<uint8_t> & fillParcelBuffer,stack<ProviderMetadata> & remainingPositions)90 void writeRandomBinder(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer,
91 stack<ProviderMetadata>& remainingPositions) {
92 // Choose 2 index in array
93 size_t fillFuncIndex = 2;
94 impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2),
95 fillFuncIndex);
96
97 // navigate to getRandomBinder. provide consume bool false
98 bool flag = false;
99 impl::writeReversedBuffer(fillParcelBuffer, flag);
100
101 // selecting RandomBinder, other binders in the list are not recorded as KernelObjects
102 size_t randomBinderIndex = 0;
103 impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2),
104 randomBinderIndex);
105
106 // write random string of length 100 in actual buffer array.
107 CHECK(WriteFully(fd, kRandomInterfaceName.c_str(), kRandomInterfaceName.size())) << fd.get();
108
109 // These will be bytes which are used inside of RandomBinder
110 // simplest path for these bytes is going to be consume bool -> return random status
111 vector<uint8_t> randomBinderBuffer;
112
113 bool returnRandomInt = true;
114 impl::writeReversedBuffer(randomBinderBuffer, returnRandomInt);
115
116 status_t randomStatus = 0;
117 impl::writeReversedBuffer(randomBinderBuffer, randomStatus);
118
119 // write integral in range to consume bytes for random binder
120 ProviderMetadata providerData;
121 providerData.position = fillParcelBuffer.size();
122 providerData.value = randomBinderBuffer.size();
123 remainingPositions.push(providerData);
124
125 // Write to fd
126 CHECK(WriteFully(fd, randomBinderBuffer.data(), randomBinderBuffer.size())) << fd.get();
127 }
128
129 // Assuming current seed path is inside the fillRandomParcelFunction, start of the loop.
writeRandomFd(vector<uint8_t> & fillParcelBuffer)130 void writeRandomFd(vector<uint8_t>& fillParcelBuffer) {
131 // path to random fd
132 size_t fillFuncIndex = 1;
133 impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2),
134 fillFuncIndex);
135
136 bool flag = false;
137 impl::writeReversedBuffer(fillParcelBuffer, flag);
138
139 // go for /dev/null index 1
140 size_t fdIndex = 1;
141 impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(3),
142 fdIndex);
143 }
144
writeParcelData(borrowed_fd fd,vector<uint8_t> & fillParcelBuffer,stack<ProviderMetadata> & remainingPositions,const uint8_t * data,size_t start,size_t length)145 void writeParcelData(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer,
146 stack<ProviderMetadata>& remainingPositions, const uint8_t* data, size_t start,
147 size_t length) {
148 // need to write parcel data till next offset with instructions to pick random bytes till offset
149 size_t fillFuncIndex = 0;
150 impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2),
151 fillFuncIndex);
152
153 // provide how much bytes to read in control buffer
154 ProviderMetadata providerData;
155 providerData.position = fillParcelBuffer.size();
156 providerData.value = length;
157 remainingPositions.push(providerData);
158
159 // provide actual bytes
160 CHECK(WriteFully(fd, data + start, length)) << fd.get();
161 }
162
163 /**
164 * Generate sequence of copy data, write fd and write binder instructions and required data.
165 * Data which will be read using consumeBytes is written to fd directly. Data which is read in
166 * form integer is consumed from rear end FuzzedDataProvider. So insert it in fillParcelBuffer and
167 * then write to fd
168 */
regenerateParcel(borrowed_fd fd,vector<uint8_t> & fillParcelBuffer,const Parcel & p,size_t dataSize,const vector<uint64_t> & objectOffsets)169 size_t regenerateParcel(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, const Parcel& p,
170 size_t dataSize, const vector<uint64_t>& objectOffsets) {
171 stack<ProviderMetadata> remainingPositions;
172 size_t copiedDataPosition = 0;
173 const uint8_t* parcelData = p.data();
174 size_t numBinders = 0;
175 size_t numFds = 0;
176
177 for (auto offset : objectOffsets) {
178 // Check what type of object is present here
179 const flat_binder_object* flatObject =
180 reinterpret_cast<const flat_binder_object*>(parcelData + offset);
181 // Copy till the object offset
182 writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition,
183 offset - copiedDataPosition);
184 copiedDataPosition = offset;
185 if (flatObject->hdr.type == BINDER_TYPE_BINDER ||
186 flatObject->hdr.type == BINDER_TYPE_HANDLE) {
187 writeRandomBinder(fd, fillParcelBuffer, remainingPositions);
188 numBinders++;
189 // In case of binder, stability is written after the binder object.
190 // We want to move the copiedDataPosition further to account for this stability field
191 copiedDataPosition += sizeof(int32_t) + sizeof(flat_binder_object);
192 } else if (flatObject->hdr.type == BINDER_TYPE_FD) {
193 writeRandomFd(fillParcelBuffer);
194 numFds++;
195 copiedDataPosition += sizeof(flat_binder_object);
196 }
197 }
198
199 if (copiedDataPosition < dataSize) {
200 // copy remaining data from recorded parcel -> last Object to end of the data
201 writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition,
202 dataSize - copiedDataPosition);
203 }
204
205 // We need to write bytes for selecting integer within range of 0 to provide.remaining_bytes()
206 // is called.
207 size_t totalWrittenBytes = dataSize - (sizeof(flat_binder_object) * objectOffsets.size()) -
208 (sizeof(int32_t) * numBinders) +
209 (kRandomInterfaceName.size() /*Interface String*/ + sizeof(bool) + sizeof(status_t)) *
210 numBinders;
211
212 // Code in fuzzService relies on provider.remaining_bytes() to select random bytes using
213 // consume integer. use the calculated remaining_bytes to generate byte buffer which can
214 // generate required fds and binders in fillRandomParcel function.
215 while (!remainingPositions.empty()) {
216 auto meta = remainingPositions.top();
217 remainingPositions.pop();
218 size_t remainingBytes = totalWrittenBytes + fillParcelBuffer.size() - meta.position;
219
220 vector<uint8_t> remReversedBytes;
221 impl::writeReversedBuffer(remReversedBytes, static_cast<size_t>(0), remainingBytes,
222 meta.value);
223 // Check the order of buffer which is being written
224 fillParcelBuffer.insert(fillParcelBuffer.end() - meta.position, remReversedBytes.begin(),
225 remReversedBytes.end());
226 }
227
228 return totalWrittenBytes;
229 }
230
231 /**
232 * Current corpus format
233 * |Reserved bytes(8)|parcel data|fillParcelBuffer|integralBuffer|
234 */
generateSeedsFromRecording(borrowed_fd fd,const binder::debug::RecordedTransaction & transaction)235 void generateSeedsFromRecording(borrowed_fd fd,
236 const binder::debug::RecordedTransaction& transaction) {
237 // Write Reserved bytes for future use
238 std::vector<uint8_t> reservedBytes(8);
239 CHECK(WriteFully(fd, reservedBytes.data(), reservedBytes.size())) << fd.get();
240
241 std::vector<uint8_t> integralBuffer;
242
243 // Write UID array : Array elements are initialized in the order that they are declared
244 // UID array index 2 element
245 // int64_t aidRoot = 0;
246 impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32,
247 static_cast<int64_t>(AID_USER) << 32,
248 static_cast<int64_t>(AID_ROOT) << 32);
249
250 // UID array index 3 element
251 impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32);
252
253 // always pick AID_ROOT -> index 0
254 size_t uidIndex = 0;
255 impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(3),
256 uidIndex);
257
258 // Never set uid in seed corpus
259 uint8_t writeUid = 0;
260 impl::writeReversedBuffer(integralBuffer, writeUid);
261
262 // Read random code. this will be from recorded transaction
263 uint8_t selectCode = 1;
264 impl::writeReversedBuffer(integralBuffer, selectCode);
265
266 // Get from recorded transaction
267 uint32_t code = transaction.getCode();
268 impl::writeReversedBuffer(integralBuffer, code);
269
270 // Get from recorded transaction
271 uint32_t flags = transaction.getFlags();
272 impl::writeReversedBuffer(integralBuffer, flags);
273
274 // always fuzz primary binder
275 size_t extraBindersIndex = 0;
276 impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(0),
277 extraBindersIndex);
278
279 const Parcel& dataParcel = transaction.getDataParcel();
280
281 // This buffer holds the bytes which will be used for fillRandomParcel API
282 std::vector<uint8_t> fillParcelBuffer;
283
284 // Don't take rpc path
285 uint8_t rpcBranch = 0;
286 impl::writeReversedBuffer(fillParcelBuffer, rpcBranch);
287
288 // Implicit branch on this path -> options->writeHeader(p, provider)
289 uint8_t writeHeaderInternal = 0;
290 impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal);
291
292 auto objectMetadata = transaction.getObjectOffsets();
293 size_t toWrite = regenerateParcel(fd, fillParcelBuffer, dataParcel, dataParcel.dataBufferSize(),
294 objectMetadata);
295
296 // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data
297 size_t subDataSize = toWrite + fillParcelBuffer.size();
298 impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), subDataSize, subDataSize);
299
300 // Write fill parcel buffer
301 CHECK(WriteFully(fd, fillParcelBuffer.data(), fillParcelBuffer.size())) << fd.get();
302
303 // Write the integralBuffer to data
304 CHECK(WriteFully(fd, integralBuffer.data(), integralBuffer.size())) << fd.get();
305 }
306 } // namespace android
307