• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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