libbinder: Don't abort when rpc parcel size is invalid
No test becaue this is only reachable by bypassing the binder client
library (i.e. writing non-sense directly to the socket).
Test: binder_rpc_fuzzer
Bug: 238497894
Change-Id: I85ef57df4b8970c35904a5e84e61cb87653a39be
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 0ae75cd..a898497 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -643,14 +643,21 @@
Span<const uint32_t> objectTableSpan;
if (session->getProtocolVersion().value() >=
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
- Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize);
+ std::optional<Span<const uint8_t>> objectTableBytes =
+ parcelSpan.splitOff(rpcReply.parcelDataSize);
+ if (!objectTableBytes.has_value()) {
+ ALOGE("Parcel size larger than available bytes: %" PRId32 " vs %zu. Terminating!",
+ rpcReply.parcelDataSize, parcelSpan.byteSize());
+ (void)session->shutdownAndWait(false);
+ return BAD_VALUE;
+ }
std::optional<Span<const uint32_t>> maybeSpan =
- objectTableBytes.reinterpret<const uint32_t>();
+ objectTableBytes->reinterpret<const uint32_t>();
if (!maybeSpan.has_value()) {
ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32
" sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!",
command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize,
- objectTableBytes.size);
+ objectTableBytes->size);
return BAD_VALUE;
}
objectTableSpan = *maybeSpan;
@@ -893,15 +900,22 @@
Span<const uint32_t> objectTableSpan;
if (session->getProtocolVersion().value() >
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
- Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize);
+ std::optional<Span<const uint8_t>> objectTableBytes =
+ parcelSpan.splitOff(transaction->parcelDataSize);
+ if (!objectTableBytes.has_value()) {
+ ALOGE("Parcel size (%" PRId32 ") greater than available bytes (%zu). Terminating!",
+ transaction->parcelDataSize, parcelSpan.byteSize());
+ (void)session->shutdownAndWait(false);
+ return BAD_VALUE;
+ }
std::optional<Span<const uint32_t>> maybeSpan =
- objectTableBytes.reinterpret<const uint32_t>();
+ objectTableBytes->reinterpret<const uint32_t>();
if (!maybeSpan.has_value()) {
ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu "
"sizeofHeader=%zu parcelSize=%" PRId32
" objectTableBytesSize=%zu. Terminating!",
transactionData.size(), sizeof(RpcWireTransaction),
- transaction->parcelDataSize, objectTableBytes.size);
+ transaction->parcelDataSize, objectTableBytes->size);
return BAD_VALUE;
}
objectTableSpan = *maybeSpan;
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index 7c6d6f1..e04199c 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -48,9 +48,11 @@
// Truncates `this` to a length of `offset` and returns a span with the
// remainder.
//
- // Aborts if offset > size.
- Span<T> splitOff(size_t offset) {
- LOG_ALWAYS_FATAL_IF(offset > size);
+ // `std::nullopt` iff offset > size.
+ std::optional<Span<T>> splitOff(size_t offset) {
+ if (offset > size) {
+ return std::nullopt;
+ }
Span<T> rest = {data + offset, size - offset};
size = offset;
return rest;