Skip to content

Commit 76b1815

Browse files
committed
Replay recorded fds and binders in fuzzers
Save object offsets in RecordedTransaction and generate seed corpus so that objects will be replaced by random binders and random fds. Adding tests to replay fds and binders in binderRecordReplayTests. Test: m binderRecordReplayTest && adb sync && atest -c binderRecordReplayTest Bug: 299650657 Change-Id: Ib9e1827b31194d8ea85def5dd3089a98b207da25
1 parent aea65bc commit 76b1815

11 files changed

Lines changed: 377 additions & 69 deletions

File tree

libs/binder/RecordedTransaction.cpp

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ static_assert(PADDING8(8) == 0);
114114

115115
RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept {
116116
mData = t.mData;
117-
mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
118-
mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
117+
mSentDataOnly.setData(t.getDataParcel().data(), t.getDataParcel().dataSize());
118+
mReplyDataOnly.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize());
119119
}
120120

121121
std::optional<RecordedTransaction> RecordedTransaction::fromDetails(
@@ -136,12 +136,21 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails(
136136
return std::nullopt;
137137
}
138138

139-
if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
139+
if (const auto* kernelFields = dataParcel.maybeKernelFields()) {
140+
for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
141+
uint64_t offset = kernelFields->mObjects[i];
142+
t.mData.mSentObjectData.push_back(offset);
143+
}
144+
}
145+
146+
if (t.mSentDataOnly.setData(dataParcel.data(), dataParcel.dataBufferSize()) !=
147+
android::NO_ERROR) {
140148
ALOGE("Failed to set sent parcel data.");
141149
return std::nullopt;
142150
}
143151

144-
if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
152+
if (t.mReplyDataOnly.setData(replyParcel.data(), replyParcel.dataBufferSize()) !=
153+
android::NO_ERROR) {
145154
ALOGE("Failed to set reply parcel data.");
146155
return std::nullopt;
147156
}
@@ -154,6 +163,7 @@ enum {
154163
DATA_PARCEL_CHUNK = 2,
155164
REPLY_PARCEL_CHUNK = 3,
156165
INTERFACE_NAME_CHUNK = 4,
166+
DATA_PARCEL_OBJECT_CHUNK = 5,
157167
END_CHUNK = 0x00ffffff,
158168
};
159169

@@ -265,21 +275,30 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd
265275
break;
266276
}
267277
case DATA_PARCEL_CHUNK: {
268-
if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
269-
chunk.dataSize) != android::NO_ERROR) {
278+
if (t.mSentDataOnly.setData(reinterpret_cast<const unsigned char*>(payloadMap),
279+
chunk.dataSize) != android::NO_ERROR) {
270280
ALOGE("Failed to set sent parcel data.");
271281
return std::nullopt;
272282
}
273283
break;
274284
}
275285
case REPLY_PARCEL_CHUNK: {
276-
if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
277-
chunk.dataSize) != android::NO_ERROR) {
286+
if (t.mReplyDataOnly.setData(reinterpret_cast<const unsigned char*>(payloadMap),
287+
chunk.dataSize) != android::NO_ERROR) {
278288
ALOGE("Failed to set reply parcel data.");
279289
return std::nullopt;
280290
}
281291
break;
282292
}
293+
case DATA_PARCEL_OBJECT_CHUNK: {
294+
const uint64_t* objects = reinterpret_cast<const uint64_t*>(payloadMap);
295+
size_t metaDataSize = (chunk.dataSize / sizeof(uint64_t));
296+
ALOGI("Total objects found in saved parcel %zu", metaDataSize);
297+
for (size_t index = 0; index < metaDataSize; ++index) {
298+
t.mData.mSentObjectData.push_back(objects[index]);
299+
}
300+
break;
301+
}
283302
case END_CHUNK:
284303
break;
285304
default:
@@ -343,14 +362,26 @@ android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const {
343362
return UNKNOWN_ERROR;
344363
}
345364

346-
if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
365+
if (NO_ERROR !=
366+
writeChunk(fd, DATA_PARCEL_CHUNK, mSentDataOnly.dataBufferSize(), mSentDataOnly.data())) {
347367
ALOGE("Failed to write sent Parcel to fd %d", fd.get());
348368
return UNKNOWN_ERROR;
349369
}
350-
if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
370+
371+
if (NO_ERROR !=
372+
writeChunk(fd, REPLY_PARCEL_CHUNK, mReplyDataOnly.dataBufferSize(),
373+
mReplyDataOnly.data())) {
351374
ALOGE("Failed to write reply Parcel to fd %d", fd.get());
352375
return UNKNOWN_ERROR;
353376
}
377+
378+
if (NO_ERROR !=
379+
writeChunk(fd, DATA_PARCEL_OBJECT_CHUNK, mData.mSentObjectData.size() * sizeof(uint64_t),
380+
reinterpret_cast<const uint8_t*>(mData.mSentObjectData.data()))) {
381+
ALOGE("Failed to write sent parcel object metadata to fd %d", fd.get());
382+
return UNKNOWN_ERROR;
383+
}
384+
354385
if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
355386
ALOGE("Failed to write end chunk to fd %d", fd.get());
356387
return UNKNOWN_ERROR;
@@ -384,10 +415,14 @@ uint32_t RecordedTransaction::getVersion() const {
384415
return mData.mHeader.version;
385416
}
386417

418+
const std::vector<uint64_t>& RecordedTransaction::getObjectOffsets() const {
419+
return mData.mSentObjectData;
420+
}
421+
387422
const Parcel& RecordedTransaction::getDataParcel() const {
388-
return mSent;
423+
return mSentDataOnly;
389424
}
390425

391426
const Parcel& RecordedTransaction::getReplyParcel() const {
392-
return mReply;
427+
return mReplyDataOnly;
393428
}

libs/binder/include/binder/Parcel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class String8;
5555
class TextOutput;
5656
namespace binder {
5757
class Status;
58+
namespace debug {
59+
class RecordedTransaction;
60+
}
5861
}
5962

6063
class Parcel {
@@ -1443,6 +1446,9 @@ class Parcel {
14431446
// TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference
14441447
// this
14451448
size_t getBlobAshmemSize() const;
1449+
1450+
// Needed so that we can save object metadata to the disk
1451+
friend class android::binder::debug::RecordedTransaction;
14461452
};
14471453

14481454
// ---------------------------------------------------------------------------

libs/binder/include/binder/RecordedTransaction.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class RecordedTransaction {
5050
uint32_t getVersion() const;
5151
const Parcel& getDataParcel() const;
5252
const Parcel& getReplyParcel() const;
53+
const std::vector<uint64_t>& getObjectOffsets() const;
5354

5455
private:
5556
RecordedTransaction() = default;
@@ -75,10 +76,11 @@ class RecordedTransaction {
7576
struct MovableData { // movable
7677
TransactionHeader mHeader;
7778
std::string mInterfaceName;
79+
std::vector<uint64_t> mSentObjectData; /* Object Offsets */
7880
};
7981
MovableData mData;
80-
Parcel mSent;
81-
Parcel mReply;
82+
Parcel mSentDataOnly;
83+
Parcel mReplyDataOnly;
8284
};
8385

8486
} // namespace binder::debug

libs/binder/tests/Android.bp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,14 @@ cc_test {
6969
cc_test {
7070
name: "binderRecordReplayTest",
7171
srcs: ["binderRecordReplayTest.cpp"],
72+
cflags: [
73+
"-DBINDER_WITH_KERNEL_IPC",
74+
],
7275
shared_libs: [
7376
"libbinder",
7477
"libcutils",
7578
"libutils",
79+
"liblog",
7680
],
7781
static_libs: [
7882
"binderRecordReplayTestIface-cpp",
@@ -96,6 +100,9 @@ aidl_interface {
96100
enabled: true,
97101
platform_apis: true,
98102
},
103+
ndk: {
104+
enabled: false,
105+
},
99106
},
100107
}
101108

libs/binder/tests/IBinderRecordReplayTest.aidl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,10 @@ interface IBinderRecordReplayTest {
6969

7070
void setSingleDataParcelableArray(in SingleDataParcelable[] input);
7171
SingleDataParcelable[] getSingleDataParcelableArray();
72+
73+
void setBinder(in IBinder binder);
74+
IBinder getBinder();
75+
76+
void setFileDescriptor(in FileDescriptor fd);
77+
FileDescriptor getFileDescriptor();
7278
}

libs/binder/tests/binderRecordReplayTest.cpp

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
#include <binder/RecordedTransaction.h>
2525
#include <binder/unique_fd.h>
2626

27+
#include <cutils/ashmem.h>
28+
2729
#include <fuzzbinder/libbinder_driver.h>
30+
#include <fuzzbinder/random_binder.h>
2831
#include <fuzzer/FuzzedDataProvider.h>
2932
#include <fuzzseeds/random_parcel_seeds.h>
3033

@@ -37,13 +40,15 @@
3740

3841
using namespace android;
3942
using android::generateSeedsFromRecording;
43+
using android::RandomBinder;
4044
using android::binder::borrowed_fd;
4145
using android::binder::Status;
4246
using android::binder::unique_fd;
4347
using android::binder::debug::RecordedTransaction;
4448
using parcelables::SingleDataParcelable;
4549

4650
const String16 kServerName = String16("binderRecordReplay");
51+
extern std::string kRandomInterfaceName;
4752

4853
#define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \
4954
Status set##name(T input) { \
@@ -81,6 +86,7 @@ class MyRecordReplay : public BnBinderRecordReplayTest {
8186

8287
GENERATE_GETTER_SETTER(String, String16);
8388
GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable);
89+
GENERATE_GETTER_SETTER(Binder, sp<IBinder>);
8490

8591
GENERATE_GETTER_SETTER(BooleanArray, std::vector<bool>);
8692
GENERATE_GETTER_SETTER(ByteArray, std::vector<uint8_t>);
@@ -91,12 +97,22 @@ class MyRecordReplay : public BnBinderRecordReplayTest {
9197
GENERATE_GETTER_SETTER(DoubleArray, std::vector<double>);
9298
GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>);
9399
GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
100+
101+
Status setFileDescriptor(unique_fd input) {
102+
mFd = std::move(unique_fd(dup(input)));
103+
return Status::ok();
104+
}
105+
106+
Status getFileDescriptor(unique_fd* output) {
107+
*output = std::move(unique_fd(dup(mFd)));
108+
return Status::ok();
109+
}
110+
unique_fd mFd;
94111
};
95112

96113
std::vector<uint8_t> retrieveData(borrowed_fd fd) {
97114
struct stat fdStat;
98115
EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1);
99-
EXPECT_TRUE(fdStat.st_size != 0);
100116

101117
std::vector<uint8_t> buffer(fdStat.st_size);
102118
auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size);
@@ -115,6 +131,7 @@ void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& tr
115131
// Read the data which has been written to seed corpus
116132
ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET));
117133
std::vector<uint8_t> seedData = retrieveData(seedFd);
134+
EXPECT_TRUE(seedData.size() != 0);
118135

119136
// use fuzzService to replay the corpus
120137
FuzzedDataProvider provider(seedData.data(), seedData.size());
@@ -148,32 +165,45 @@ class BinderRecordReplayTest : public ::testing::Test {
148165
template <typename T, typename U>
149166
void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue,
150167
Status (IBinderRecordReplayTest::*get)(U*), U changedValue) {
151-
auto replayFunctions = {&replayBinder, &replayFuzzService};
168+
using ReplayFunc = decltype(&replayFuzzService);
169+
vector<ReplayFunc> replayFunctions = {&replayFuzzService};
170+
if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) {
171+
// Parcel retrieved from record replay doesn't have object information. use it for
172+
// replaying primitive types only.
173+
replayFunctions.push_back(&replayBinder);
174+
}
175+
152176
for (auto replayFunc : replayFunctions) {
153177
unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
154178
O_RDWR | O_CREAT | O_CLOEXEC, 0666));
155179
ASSERT_TRUE(fd.ok());
156180

157181
// record a transaction
158182
mBpBinder->startRecordingBinder(fd);
159-
auto status = (*mInterface.*set)(recordedValue);
183+
auto status = (*mInterface.*set)(std::move(recordedValue));
160184
EXPECT_TRUE(status.isOk());
161185
mBpBinder->stopRecordingBinder();
162186

163187
// test transaction does the thing we expect it to do
164188
U output;
165189
status = (*mInterface.*get)(&output);
166190
EXPECT_TRUE(status.isOk());
167-
EXPECT_EQ(output, recordedValue);
191+
192+
// Expect this equal only if types are primitives
193+
if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) {
194+
EXPECT_EQ(output, recordedValue);
195+
}
168196

169197
// write over the existing state
170-
status = (*mInterface.*set)(changedValue);
198+
status = (*mInterface.*set)(std::move(changedValue));
171199
EXPECT_TRUE(status.isOk());
172200

173201
status = (*mInterface.*get)(&output);
174202
EXPECT_TRUE(status.isOk());
175203

176-
EXPECT_EQ(output, changedValue);
204+
if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) {
205+
EXPECT_EQ(output, changedValue);
206+
}
177207

178208
// replay transaction
179209
ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET));
@@ -186,7 +216,23 @@ class BinderRecordReplayTest : public ::testing::Test {
186216

187217
status = (*mInterface.*get)(&output);
188218
EXPECT_TRUE(status.isOk());
189-
EXPECT_EQ(output, recordedValue);
219+
220+
// FDs and binders will be replaced with random fd and random binders
221+
if constexpr (std::is_same_v<U, unique_fd>) {
222+
// verify that replayed fd is /dev/null. This is being replayed from random_fd.cpp
223+
// and choosing /dav/null while generating seed in binder2corpus
224+
std::string fdPath = "/proc/self/fd/" + std::to_string(output.get());
225+
char path[PATH_MAX];
226+
ASSERT_GT(readlink(fdPath.c_str(), path, sizeof(path)), 0);
227+
EXPECT_EQ(strcmp("/dev/null", path), 0);
228+
} else if constexpr (std::is_same_v<U, sp<IBinder>>) {
229+
// This is binder is replayed from random_binder.cpp using seed data which writes
230+
// this interface.
231+
EXPECT_EQ(String16(kRandomInterfaceName.c_str(), kRandomInterfaceName.size()),
232+
output->getInterfaceDescriptor());
233+
} else {
234+
ASSERT_EQ(recordedValue, output);
235+
}
190236
}
191237
}
192238

@@ -319,6 +365,32 @@ TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) {
319365
&IBinderRecordReplayTest::getSingleDataParcelableArray, changed);
320366
}
321367

368+
TEST_F(BinderRecordReplayTest, ReplayBinder) {
369+
vector<uint8_t> data = {0x8A, 0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, 0x9B, 0xAA, 0xF3, 0xDA};
370+
sp<IBinder> saved = new RandomBinder(String16("random_interface"), std::move(data));
371+
sp<IBinder> changed = IInterface::asBinder(defaultServiceManager());
372+
recordReplay(&IBinderRecordReplayTest::setBinder, saved, &IBinderRecordReplayTest::getBinder,
373+
changed);
374+
}
375+
376+
TEST_F(BinderRecordReplayTest, ReplayFd) {
377+
// Write something to both fds we are setting
378+
unique_fd saved(open("/data/local/tmp/test_fd", O_RDWR | O_CREAT | O_CLOEXEC, 0666));
379+
std::string contentSaved = "This will be never read again for recorded fd!";
380+
CHECK(android::base::WriteFully(saved, contentSaved.data(), contentSaved.size()))
381+
<< saved.get();
382+
383+
unique_fd changed(open("/data/local/tmp/test_des", O_RDWR | O_CREAT | O_CLOEXEC, 0666));
384+
std::string contentChanged = "This will be never read again from changed fd!";
385+
CHECK(android::base::WriteFully(changed, contentChanged.data(), contentChanged.size()))
386+
<< changed.get();
387+
388+
// When fds are replayed, it will be replaced by /dev/null..reading from it should yield
389+
// null data
390+
recordReplay(&IBinderRecordReplayTest::setFileDescriptor, std::move(unique_fd(dup(saved))),
391+
&IBinderRecordReplayTest::getFileDescriptor, std::move(unique_fd(dup(changed))));
392+
}
393+
322394
int main(int argc, char** argv) {
323395
::testing::InitGoogleTest(&argc, argv);
324396

0 commit comments

Comments
 (0)