Skip to content

Commit a8021fc

Browse files
Treehugger RobotGerrit Code Review
authored andcommitted
Merge "Replay recorded fds and binders in fuzzers" into main
2 parents eee1cf5 + 76b1815 commit a8021fc

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)