xref: /aosp_15_r20/frameworks/native/libs/binder/tests/parcel_fuzzer/main.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright (C) 2019 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 #define FUZZ_LOG_TAG "main"
17 
18 #include "binder.h"
19 #include "binder_ndk.h"
20 #include "hwbinder.h"
21 #include "util.h"
22 
23 #include <iostream>
24 
25 #include <android-base/logging.h>
26 #include <android/binder_auto_utils.h>
27 #include <android/binder_libbinder.h>
28 #include <fuzzbinder/random_parcel.h>
29 #include <fuzzer/FuzzedDataProvider.h>
30 
31 #include <cstdlib>
32 #include <ctime>
33 #include <sys/resource.h>
34 #include <sys/time.h>
35 
36 #include "../../Utils.h"
37 
38 using android::fillRandomParcel;
39 using android::RandomParcelOptions;
40 using android::sp;
41 using android::HexString;
42 
fillRandomParcel(::android::hardware::Parcel * p,FuzzedDataProvider && provider,RandomParcelOptions * options)43 void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider,
44                       RandomParcelOptions* options) {
45     // TODO: functionality to create random parcels for libhwbinder parcels
46     (void)options;
47 
48     std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
49 
50     if (input.size() % 4 != 0) {
51         input.resize(input.size() + (sizeof(uint32_t) - input.size() % sizeof(uint32_t)));
52     }
53     CHECK_EQ(0, input.size() % 4);
54 
55     p->setDataCapacity(input.size());
56     for (size_t i = 0; i < input.size(); i += 4) {
57         p->writeInt32(*((int32_t*)(input.data() + i)));
58     }
59 
60     CHECK_EQ(0, memcmp(input.data(), p->data(), p->dataSize()));
61 }
fillRandomParcel(NdkParcelAdapter * p,FuzzedDataProvider && provider,RandomParcelOptions * options)62 static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider,
63                              RandomParcelOptions* options) {
64     // fill underlying parcel using functions to fill random libbinder parcel
65     fillRandomParcel(p->parcel(), std::move(provider), options);
66 }
67 
68 template <typename P, typename B>
doTransactFuzz(const char * backend,const sp<B> & binder,FuzzedDataProvider && provider)69 void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider&& provider) {
70     uint32_t code = provider.ConsumeIntegral<uint32_t>();
71     uint32_t flag = provider.ConsumeIntegral<uint32_t>();
72 
73     FUZZ_LOG() << "backend: " << backend;
74 
75     RandomParcelOptions options;
76 
77     P reply;
78     P data;
79     fillRandomParcel(&data, std::move(provider), &options);
80     (void)binder->transact(code, data, &reply, flag);
81 }
82 
83 // start with a Parcel full of data (e.g. like you get from another process)
84 template <typename P>
doReadFuzz(const char * backend,const std::vector<ParcelRead<P>> & reads,FuzzedDataProvider && provider)85 void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
86                 FuzzedDataProvider&& provider) {
87     // Allow some majority of the bytes to be dedicated to telling us what to
88     // do. The fixed value added here represents that we want to test doing a
89     // lot of 'instructions' even on really short parcels.
90     size_t maxInstructions = 20 + (provider.remaining_bytes() * 2 / 3);
91     // but don't always use that many instructions. We want to allow the fuzzer
92     // to explore large parcels with few instructions if it wants to.
93     std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
94             provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
95 
96     RandomParcelOptions options;
97 
98     P p;
99     fillRandomParcel(&p, std::move(provider), &options); // consumes provider
100 
101     // since we are only using a byte to index
102     CHECK_LE(reads.size(), 255u) << reads.size();
103 
104     FUZZ_LOG() << "backend: " << backend;
105     FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
106     FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size());
107 
108     FuzzedDataProvider instructionsProvider(instructions.data(), instructions.size());
109     while (instructionsProvider.remaining_bytes() > 0) {
110         uint8_t idx = instructionsProvider.ConsumeIntegralInRange<uint8_t>(0, reads.size() - 1);
111 
112         FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
113                    << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
114 
115         reads[idx](p, instructionsProvider);
116     }
117 }
118 
119 template <typename P>
doReadWriteFuzz(const char * backend,const std::vector<ParcelRead<P>> & reads,const std::vector<ParcelWrite<P>> & writes,FuzzedDataProvider && provider)120 void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
121                      const std::vector<ParcelWrite<P>>& writes, FuzzedDataProvider&& provider) {
122     RandomParcelOptions options;
123     P p;
124 
125     // since we are only using a byte to index
126     CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();
127 
128     FUZZ_LOG() << "backend: " << backend;
129 
130     while (provider.remaining_bytes() > 0) {
131         uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
132 
133         FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
134                    << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
135 
136         if (idx < reads.size()) {
137             reads.at(idx)(p, provider);
138         } else {
139             writes.at(idx - reads.size())(p, provider, &options);
140         }
141     }
142 }
143 
NothingClass_onCreate(void * args)144 void* NothingClass_onCreate(void* args) {
145     return args;
146 }
NothingClass_onDestroy(void *)147 void NothingClass_onDestroy(void* /*userData*/) {}
NothingClass_onTransact(AIBinder *,transaction_code_t,const AParcel *,AParcel *)148 binder_status_t NothingClass_onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
149     return STATUS_UNKNOWN_ERROR;
150 }
151 static AIBinder_Class* kNothingClass =
152         AIBinder_Class_define("nothing", NothingClass_onCreate, NothingClass_onDestroy,
153                               NothingClass_onTransact);
154 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)155 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
156     if (size <= 1) return 0;  // no use
157 
158     // avoid timeouts, see b/142617274, b/142473153
159     if (size > 50000) return 0;
160 
161     FuzzedDataProvider provider = FuzzedDataProvider(data, size);
162 
163     const std::function<void(FuzzedDataProvider&&)> fuzzBackend[] = {
164             [](FuzzedDataProvider&& provider) {
165                 doTransactFuzz<
166                         ::android::hardware::Parcel>("hwbinder",
167                                                      sp<::android::hardware::BHwBinder>::make(),
168                                                      std::move(provider));
169             },
170             [](FuzzedDataProvider&& provider) {
171                 doTransactFuzz<::android::Parcel>("binder", sp<::android::BBinder>::make(),
172                                                   std::move(provider));
173             },
174             [](FuzzedDataProvider&& provider) {
175                 // fuzz from the libbinder layer since it's a superset of the
176                 // interface you get at the libbinder_ndk layer
177                 auto ndkBinder = ndk::SpAIBinder(AIBinder_new(kNothingClass, nullptr));
178                 auto binder = AIBinder_toPlatformBinder(ndkBinder.get());
179                 doTransactFuzz<::android::Parcel>("binder_ndk", binder, std::move(provider));
180             },
181             [](FuzzedDataProvider&& provider) {
182                 doReadFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
183                                                         std::move(provider));
184             },
185             [](FuzzedDataProvider&& provider) {
186                 doReadFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
187                                               std::move(provider));
188             },
189             [](FuzzedDataProvider&& provider) {
190                 doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
191                                              std::move(provider));
192             },
193             [](FuzzedDataProvider&& provider) {
194                 doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
195                                                    BINDER_PARCEL_WRITE_FUNCTIONS,
196                                                    std::move(provider));
197             },
198             [](FuzzedDataProvider&& provider) {
199                 doReadWriteFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
200                                                   BINDER_NDK_PARCEL_WRITE_FUNCTIONS,
201                                                   std::move(provider));
202             },
203     };
204 
205     provider.PickValueInArray(fuzzBackend)(std::move(provider));
206 
207     return 0;
208 }
209