Merge "Updates OWNERS files" into sc-dev am: f2109f9118
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/14687234
Change-Id: If62878215818848b05b102644e19eadb44a0a5a1
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index be260e8..4b12927 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -64,6 +64,9 @@
"PermissionCache.cpp",
"PermissionController.cpp",
]
+libbinder_no_vendor_interface_sources = [
+ ":packagemanager_aidl",
+]
cc_library {
name: "libbinder",
@@ -120,9 +123,8 @@
"Status.cpp",
"TextOutput.cpp",
"Utils.cpp",
- ":packagemanager_aidl",
":libbinder_aidl",
- ],
+ ] + libbinder_no_vendor_interface_sources,
target: {
android: {
@@ -134,7 +136,7 @@
},
},
vendor: {
- exclude_srcs: libbinder_device_interface_sources,
+ exclude_srcs: libbinder_device_interface_sources + libbinder_no_vendor_interface_sources,
},
darwin: {
enabled: false,
@@ -162,6 +164,10 @@
binder32bit: {
cflags: ["-DBINDER_IPC_32BIT=1"],
},
+
+ debuggable: {
+ cflags: ["-DBINDER_RPC_DEV_SERVERS"],
+ },
},
shared_libs: [
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index d5bdd1c..c83c383 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -17,16 +17,24 @@
#include <binder/Binder.h>
#include <atomic>
-#include <utils/misc.h>
+
+#include <android-base/unique_fd.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <private/android_filesystem_config.h>
+#include <utils/misc.h>
+#include <inttypes.h>
#include <linux/sched.h>
#include <stdio.h>
+#include "RpcState.h"
+
namespace android {
// Service implementations inherit from BBinder and IBinder, and this is frozen
@@ -39,6 +47,12 @@
static_assert(sizeof(BBinder) == 20);
#endif
+#ifdef BINDER_RPC_DEV_SERVERS
+constexpr const bool kEnableRpcDevServers = true;
+#else
+constexpr const bool kEnableRpcDevServers = false;
+#endif
+
// ---------------------------------------------------------------------------
IBinder::IBinder()
@@ -136,6 +150,33 @@
return OK;
}
+status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, uint32_t maxRpcThreads) {
+ if constexpr (!kEnableRpcDevServers) {
+ ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
+ return INVALID_OPERATION;
+ }
+
+ BBinder* local = this->localBinder();
+ if (local != nullptr) {
+ return local->BBinder::setRpcClientDebug(std::move(socketFd), maxRpcThreads);
+ }
+
+ BpBinder* proxy = this->remoteBinder();
+ LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote");
+
+ Parcel data;
+ Parcel reply;
+ status_t status;
+ if (status = data.writeBool(socketFd.ok()); status != OK) return status;
+ if (socketFd.ok()) {
+ // writeUniqueFileDescriptor currently makes an unnecessary dup().
+ status = data.writeFileDescriptor(socketFd.release(), true /* own */);
+ if (status != OK) return status;
+ }
+ if (status = data.writeUint32(maxRpcThreads); status != OK) return status;
+ return transact(SET_RPC_CLIENT_TRANSACTION, data, &reply);
+}
+
// ---------------------------------------------------------------------------
class BBinder::Extras
@@ -150,6 +191,7 @@
// for below objects
Mutex mLock;
+ sp<RpcServer> mRpcServer;
BpBinder::ObjectManager mObjects;
};
@@ -199,6 +241,10 @@
case DEBUG_PID_TRANSACTION:
err = reply->writeInt32(getDebugPid());
break;
+ case SET_RPC_CLIENT_TRANSACTION: {
+ err = setRpcClientDebug(data);
+ break;
+ }
default:
err = onTransact(code, data, reply, flags);
break;
@@ -368,6 +414,75 @@
e->mExtension = extension;
}
+status_t BBinder::setRpcClientDebug(const Parcel& data) {
+ if constexpr (!kEnableRpcDevServers) {
+ ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid != AID_ROOT) {
+ ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid);
+ return PERMISSION_DENIED;
+ }
+ status_t status;
+ bool hasSocketFd;
+ android::base::unique_fd clientFd;
+ uint32_t maxRpcThreads;
+
+ if (status = data.readBool(&hasSocketFd); status != OK) return status;
+ if (hasSocketFd) {
+ if (status = data.readUniqueFileDescriptor(&clientFd); status != OK) return status;
+ }
+ if (status = data.readUint32(&maxRpcThreads); status != OK) return status;
+
+ return setRpcClientDebug(std::move(clientFd), maxRpcThreads);
+}
+
+status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, uint32_t maxRpcThreads) {
+ if constexpr (!kEnableRpcDevServers) {
+ ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ const int socketFdForPrint = socketFd.get();
+ LOG_RPC_DETAIL("%s(%d, %" PRIu32 ")", __PRETTY_FUNCTION__, socketFdForPrint, maxRpcThreads);
+
+ if (!socketFd.ok()) {
+ ALOGE("%s: No socket FD provided.", __PRETTY_FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (maxRpcThreads <= 0) {
+ ALOGE("%s: RPC is useless with %" PRIu32 " threads.", __PRETTY_FUNCTION__, maxRpcThreads);
+ return BAD_VALUE;
+ }
+
+ // TODO(b/182914638): RPC and binder should share the same thread pool count.
+ size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxThreadCount();
+ if (binderThreadPoolMaxCount <= 1) {
+ ALOGE("%s: ProcessState thread pool max count is %zu. RPC is disabled for this service "
+ "because RPC requires the service to support multithreading.",
+ __PRETTY_FUNCTION__, binderThreadPoolMaxCount);
+ return INVALID_OPERATION;
+ }
+
+ Extras* e = getOrCreateExtras();
+ AutoMutex _l(e->mLock);
+ if (e->mRpcServer != nullptr) {
+ ALOGE("%s: Already have RPC client", __PRETTY_FUNCTION__);
+ return ALREADY_EXISTS;
+ }
+ e->mRpcServer = RpcServer::make();
+ LOG_ALWAYS_FATAL_IF(e->mRpcServer == nullptr, "RpcServer::make returns null");
+ e->mRpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ // Weak ref to avoid circular dependency: BBinder -> RpcServer -X-> BBinder
+ e->mRpcServer->setRootObjectWeak(wp<BBinder>::fromExisting(this));
+ e->mRpcServer->setupExternalServer(std::move(socketFd));
+ e->mRpcServer->start();
+ LOG_RPC_DETAIL("%s(%d, %" PRIu32 ") successful", __PRETTY_FUNCTION__, socketFdForPrint,
+ maxRpcThreads);
+ return OK;
+}
+
BBinder::~BBinder()
{
Extras* e = mExtras.load(std::memory_order_relaxed);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index c605e67..e933f7e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,19 +366,46 @@
pid_t IPCThreadState::getCallingPid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingPid;
}
const char* IPCThreadState::getCallingSid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingSid;
}
uid_t IPCThreadState::getCallingUid() const
{
+ checkContextIsBinderForUse(__func__);
return mCallingUid;
}
+const IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(const SpGuard* guard) {
+ const SpGuard* orig = mServingStackPointerGuard;
+ mServingStackPointerGuard = guard;
+ return orig;
+}
+
+void IPCThreadState::restoreGetCallingSpGuard(const SpGuard* guard) {
+ mServingStackPointerGuard = guard;
+}
+
+void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
+ if (LIKELY(mServingStackPointerGuard == nullptr)) return;
+
+ if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) {
+ LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).",
+ mServingStackPointerGuard->context, use, mServingStackPointer,
+ mServingStackPointerGuard->address);
+ }
+
+ // in the case mServingStackPointer is deeper in the stack than the guard,
+ // we must be serving a binder transaction (maybe nested). This is a binder
+ // context, so we don't abort
+}
+
int64_t IPCThreadState::clearCallingIdentity()
{
// ignore mCallingSid for legacy reasons
@@ -851,6 +878,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
+ mServingStackPointerGuard(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
mIsLooper(false),
@@ -1232,7 +1260,7 @@
tr.offsets_size/sizeof(binder_size_t), freeBuffer);
const void* origServingStackPointer = mServingStackPointer;
- mServingStackPointer = &origServingStackPointer; // anything on the stack
+ mServingStackPointer = __builtin_frame_address(0);
const pid_t origPid = mCallingPid;
const char* origSid = mCallingSid;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index ee834ea..ac8867a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1466,6 +1466,29 @@
return nullptr;
}
+status_t Parcel::readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const {
+ if (status_t status = readInt32(size); status != OK) return status;
+ if (*size < 0) return OK; // may be null, client to handle
+
+ LOG_ALWAYS_FATAL_IF(elmSize > INT32_MAX, "Cannot have element as big as %zu", elmSize);
+
+ // approximation, can't know max element size (e.g. if it makes heap
+ // allocations)
+ static_assert(sizeof(int) == sizeof(int32_t), "Android is LP64");
+ int32_t allocationSize;
+ if (__builtin_smul_overflow(elmSize, *size, &allocationSize)) return NO_MEMORY;
+
+ // High limit of 1MB since something this big could never be returned. Could
+ // probably scope this down, but might impact very specific usecases.
+ constexpr int32_t kMaxAllocationSize = 1 * 1000 * 1000;
+
+ if (allocationSize >= kMaxAllocationSize) {
+ return NO_MEMORY;
+ }
+
+ return OK;
+}
+
template<class T>
status_t Parcel::readAligned(T *pArg) const {
static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4fd0dc7..179a9d2 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -359,6 +359,14 @@
return result;
}
+size_t ProcessState::getThreadPoolMaxThreadCount() const {
+ // may actually be one more than this, if join is called
+ if (mThreadPoolStarted) return mMaxThreads;
+ // must not be initialized or maybe has poll thread setup, we
+ // currently don't track this in libbinder
+ return 0;
+}
+
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
uint32_t enableDetection = enable ? 1 : 0;
if (ioctl(mDriverFD, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enableDetection) == -1) {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 9cc6e7f..540c346 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -26,9 +26,9 @@
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <log/log.h>
-#include "RpcState.h"
#include "RpcSocketAddress.h"
+#include "RpcState.h"
#include "RpcWireFormat.h"
namespace android {
@@ -37,7 +37,9 @@
using base::unique_fd;
RpcServer::RpcServer() {}
-RpcServer::~RpcServer() {}
+RpcServer::~RpcServer() {
+ (void)shutdown();
+}
sp<RpcServer> RpcServer::make() {
return sp<RpcServer>::make();
@@ -99,7 +101,7 @@
void RpcServer::setMaxThreads(size_t threads) {
LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads");
- LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started");
+ LOG_ALWAYS_FATAL_IF(mJoinThreadRunning, "Cannot set max threads while running");
mMaxThreads = threads;
}
@@ -126,16 +128,41 @@
return ret;
}
+static void joinRpcServer(sp<RpcServer>&& thiz) {
+ thiz->join();
+}
+
+void RpcServer::start() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!");
+ mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this));
+}
+
void RpcServer::join() {
- while (true) {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join.");
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+ mJoinThreadRunning = true;
+ mShutdownTrigger = RpcSession::FdTrigger::make();
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Cannot create join signaler");
+ }
+
+ while (mShutdownTrigger->triggerablePollRead(mServer)) {
(void)acceptOne();
}
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ mJoinThreadRunning = false;
+ }
+ mShutdownCv.notify_all();
}
bool RpcServer::acceptOne() {
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
- LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
-
unique_fd clientFd(
TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
@@ -147,15 +174,44 @@
{
std::lock_guard<std::mutex> _l(mLock);
- std::thread thread =
- std::thread(&RpcServer::establishConnection, this,
- std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+ std::thread thread = std::thread(&RpcServer::establishConnection,
+ sp<RpcServer>::fromExisting(this), std::move(clientFd));
mConnectingThreads[thread.get_id()] = std::move(thread);
}
return true;
}
+bool RpcServer::shutdown() {
+ std::unique_lock<std::mutex> _l(mLock);
+ if (mShutdownTrigger == nullptr) {
+ LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed.");
+ return false;
+ }
+
+ mShutdownTrigger->trigger();
+ while (mJoinThreadRunning || !mConnectingThreads.empty()) {
+ ALOGI("Waiting for RpcServer to shut down. Join thread running: %d, Connecting threads: "
+ "%zu",
+ mJoinThreadRunning, mConnectingThreads.size());
+ mShutdownCv.wait(_l);
+ }
+
+ // At this point, we know join() is about to exit, but the thread that calls
+ // join() may not have exited yet.
+ // If RpcServer owns the join thread (aka start() is called), make sure the thread exits;
+ // otherwise ~thread() may call std::terminate(), which may crash the process.
+ // If RpcServer does not own the join thread (aka join() is called directly),
+ // then the owner of RpcServer is responsible for cleaning up that thread.
+ if (mJoinThread.get()) {
+ mJoinThread->join();
+ mJoinThread.reset();
+ }
+
+ mShutdownTrigger = nullptr;
+ return true;
+}
+
std::vector<sp<RpcSession>> RpcServer::listSessions() {
std::lock_guard<std::mutex> _l(mLock);
std::vector<sp<RpcSession>> sessions;
@@ -172,44 +228,56 @@
}
void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
- LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
-
// TODO(b/183988761): cannot trust this simple ID
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
- bool idValid = true;
+ LOG_ALWAYS_FATAL_IF(!server->mAgreedExperimental, "no!");
+
+ // mShutdownTrigger can only be cleared once connection threads have joined.
+ // It must be set before this thread is started
+ LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
+
int32_t id;
- if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
- ALOGE("Could not read ID from fd %d", clientFd.get());
- idValid = false;
+ bool idValid = server->mShutdownTrigger->interruptableRecv(clientFd.get(), &id, sizeof(id));
+ if (!idValid) {
+ ALOGE("Failed to read ID for client connecting to RPC server.");
}
std::thread thisThread;
sp<RpcSession> session;
{
- std::lock_guard<std::mutex> _l(mLock);
+ std::unique_lock<std::mutex> _l(server->mLock);
- auto threadId = mConnectingThreads.find(std::this_thread::get_id());
- LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+ auto threadId = server->mConnectingThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
"Must establish connection on owned thread");
thisThread = std::move(threadId->second);
ScopeGuard detachGuard = [&]() { thisThread.detach(); };
- mConnectingThreads.erase(threadId);
+ server->mConnectingThreads.erase(threadId);
+
+ // TODO(b/185167543): we currently can't disable this because we don't
+ // shutdown sessions as well, only the server itself. So, we need to
+ // keep this separate from the detachGuard, since we temporarily want to
+ // give a notification even when we pass ownership of the thread to
+ // a session.
+ ScopeGuard threadLifetimeGuard = [&]() {
+ _l.unlock();
+ server->mShutdownCv.notify_all();
+ };
if (!idValid) {
return;
}
if (id == RPC_SESSION_ID_NEW) {
- LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
- mSessionIdCounter++;
+ LOG_ALWAYS_FATAL_IF(server->mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+ server->mSessionIdCounter++;
session = RpcSession::make();
- session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+ session->setForServer(wp<RpcServer>(server), server->mSessionIdCounter);
- mSessions[mSessionIdCounter] = session;
+ server->mSessions[server->mSessionIdCounter] = session;
} else {
- auto it = mSessions.find(id);
- if (it == mSessions.end()) {
+ auto it = server->mSessions.find(id);
+ if (it == server->mSessions.end()) {
ALOGE("Cannot add thread, no record of session with ID %d", id);
return;
}
@@ -222,10 +290,6 @@
// avoid strong cycle
server = nullptr;
- //
- //
- // DO NOT ACCESS MEMBER VARIABLES BELOW
- //
session->join(std::move(clientFd));
}
@@ -255,7 +319,10 @@
LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
- mServer = std::move(serverFd);
+ if (!setupExternalServer(std::move(serverFd))) {
+ ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
+ return false;
+ }
return true;
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 05fa49e..771d738 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -19,10 +19,12 @@
#include <binder/RpcSession.h>
#include <inttypes.h>
+#include <poll.h>
#include <unistd.h>
#include <string_view>
+#include <android-base/macros.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/Stability.h>
@@ -113,6 +115,51 @@
return state()->sendDecStrong(connection.fd(), address);
}
+std::unique_ptr<RpcSession::FdTrigger> RpcSession::FdTrigger::make() {
+ auto ret = std::make_unique<RpcSession::FdTrigger>();
+ if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) return nullptr;
+ return ret;
+}
+
+void RpcSession::FdTrigger::trigger() {
+ mWrite.reset();
+}
+
+bool RpcSession::FdTrigger::triggerablePollRead(base::borrowed_fd fd) {
+ while (true) {
+ pollfd pfd[]{{.fd = fd.get(), .events = POLLIN, .revents = 0},
+ {.fd = mRead.get(), .events = POLLHUP, .revents = 0}};
+ int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+ if (ret < 0) {
+ ALOGE("Could not poll: %s", strerror(errno));
+ continue;
+ }
+ if (ret == 0) {
+ continue;
+ }
+ if (pfd[1].revents & POLLHUP) {
+ return false;
+ }
+ return true;
+ }
+}
+
+bool RpcSession::FdTrigger::interruptableRecv(base::borrowed_fd fd, void* data, size_t size) {
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
+ uint8_t* end = buffer + size;
+
+ while (triggerablePollRead(fd)) {
+ ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
+ if (readSize < 0) {
+ ALOGE("Failed to read %s", strerror(errno));
+ return false;
+ }
+ buffer += readSize;
+ if (buffer == end) return true;
+ }
+ return false;
+}
+
status_t RpcSession::readId() {
{
std::lock_guard<std::mutex> _l(mMutex);
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 2ba9fa2..230de6f 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,7 +18,9 @@
#include "RpcState.h"
+#include <android-base/scopeguard.h>
#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
#include <binder/RpcServer.h>
#include "Debug.h"
@@ -28,6 +30,8 @@
namespace android {
+using base::ScopeGuard;
+
RpcState::RpcState() {}
RpcState::~RpcState() {}
@@ -470,6 +474,21 @@
status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
+ IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
+ IPCThreadState::SpGuard spGuard{
+ .address = __builtin_frame_address(0),
+ .context = "processing binder RPC command",
+ };
+ const IPCThreadState::SpGuard* origGuard;
+ if (kernelBinderState != nullptr) {
+ origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
+ }
+ ScopeGuard guardUnguard = [&]() {
+ if (kernelBinderState != nullptr) {
+ kernelBinderState->restoreGetCallingSpGuard(origGuard);
+ }
+ };
+
switch (command.command) {
case RPC_COMMAND_TRANSACT:
return processTransact(fd, session, command);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 7e9be41..754f87c 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -94,6 +94,9 @@
pid_t getDebugPid();
+ [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
+ uint32_t maxRpcThreads);
+
protected:
virtual ~BBinder();
@@ -111,6 +114,8 @@
Extras* getOrCreateExtras();
+ [[nodiscard]] status_t setRpcClientDebug(const Parcel& data);
+
std::atomic<Extras*> mExtras;
friend ::android::internal::Stability;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 97c826c..ce28d7c 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -60,6 +60,7 @@
SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'),
EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'),
DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'),
+ SET_RPC_CLIENT_TRANSACTION = B_PACK_CHARS('_', 'R', 'P', 'C'),
// See android.os.IBinder.TWEET_TRANSACTION
// Most importantly, messages can be anything not exceeding 130 UTF-8
@@ -152,6 +153,27 @@
*/
status_t getDebugPid(pid_t* outPid);
+ /**
+ * Set the RPC client fd to this binder service, for debugging. This is only available on
+ * debuggable builds.
+ *
+ * |maxRpcThreads| must be positive because RPC is useless without threads.
+ *
+ * When this is called on a binder service, the service:
+ * 1. sets up RPC server
+ * 2. spawns 1 new thread that calls RpcServer::join()
+ * - join() spawns at most |maxRpcThreads| threads that accept() connections; see RpcServer
+ *
+ * setRpcClientDebug() may only be called once.
+ * TODO(b/182914638): If allow to shut down the client, addRpcClient can be called repeatedly.
+ *
+ * Note: A thread is spawned for each accept()'ed fd, which may call into functions of the
+ * interface freely. See RpcServer::join(). To avoid such race conditions, implement the service
+ * functions with multithreading support.
+ */
+ [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd socketFd,
+ uint32_t maxRpcThreads);
+
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t transact( uint32_t code,
const Parcel& data,
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 5d04ebe..204926d 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -62,7 +62,7 @@
* call. If not in a binder call, this will return getpid. If the
* call is oneway, this will return 0.
*/
- pid_t getCallingPid() const;
+ [[nodiscard]] pid_t getCallingPid() const;
/**
* Returns the SELinux security identifier of the process which has
@@ -73,13 +73,43 @@
* This can't be restored once it's cleared, and it does not return the
* context of the current process when not in a binder call.
*/
- const char* getCallingSid() const;
+ [[nodiscard]] const char* getCallingSid() const;
/**
* Returns the UID of the process which has made the current binder
* call. If not in a binder call, this will return 0.
*/
- uid_t getCallingUid() const;
+ [[nodiscard]] uid_t getCallingUid() const;
+
+ /**
+ * Make it an abort to rely on getCalling* for a section of
+ * execution.
+ *
+ * Usage:
+ * IPCThreadState::SpGuard guard {
+ * .address = __builtin_frame_address(0),
+ * .context = "...",
+ * };
+ * const auto* orig = pushGetCallingSpGuard(&guard);
+ * {
+ * // will abort if you call getCalling*, unless you are
+ * // serving a nested binder transaction
+ * }
+ * restoreCallingSpGuard(orig);
+ */
+ struct SpGuard {
+ const void* address;
+ const char* context;
+ };
+ const SpGuard* pushGetCallingSpGuard(const SpGuard* guard);
+ void restoreGetCallingSpGuard(const SpGuard* guard);
+ /**
+ * Used internally by getCalling*. Can also be used to assert that
+ * you are in a binder context (getCalling* is valid). This is
+ * intentionally not exposed as a boolean API since code should be
+ * written to know its environment.
+ */
+ void checkContextIsBinderForUse(const char* use) const;
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
@@ -203,6 +233,7 @@
Parcel mOut;
status_t mLastError;
const void* mServingStackPointer;
+ const SpGuard* mServingStackPointerGuard;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5aaaa0c..02052ad 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -561,6 +561,8 @@
status_t flattenBinder(const sp<IBinder>& binder);
status_t unflattenBinder(sp<IBinder>* out) const;
+ status_t readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const;
+
template<class T>
status_t readAligned(T *pArg) const;
@@ -1315,7 +1317,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::vector<T>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
@@ -1330,7 +1332,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
@@ -1346,7 +1348,7 @@
template<typename T>
status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
int32_t size;
- status_t err = readInt32(&size);
+ status_t err = readOutVectorSizeWithCheck(sizeof(T), &size);
if (err != NO_ERROR) {
return err;
}
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index b9db5d7..72c2ab7 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -18,8 +18,8 @@
#include <binder/IBinder.h>
#include <utils/KeyedVector.h>
-#include <utils/String8.h>
#include <utils/String16.h>
+#include <utils/String8.h>
#include <utils/threads.h>
@@ -30,11 +30,10 @@
class IPCThreadState;
-class ProcessState : public virtual RefBase
-{
+class ProcessState : public virtual RefBase {
public:
- static sp<ProcessState> self();
- static sp<ProcessState> selfOrNull();
+ static sp<ProcessState> self();
+ static sp<ProcessState> selfOrNull();
/* initWithDriver() can be used to configure libbinder to use
* a different binder driver dev node. It must be called *before*
@@ -44,94 +43,101 @@
*
* If this is called with nullptr, the behavior is the same as selfOrNull.
*/
- static sp<ProcessState> initWithDriver(const char *driver);
+ static sp<ProcessState> initWithDriver(const char* driver);
- sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ sp<IBinder> getContextObject(const sp<IBinder>& caller);
- void startThreadPool();
+ void startThreadPool();
- bool becomeContextManager();
+ bool becomeContextManager();
- sp<IBinder> getStrongProxyForHandle(int32_t handle);
- void expungeHandle(int32_t handle, IBinder* binder);
+ sp<IBinder> getStrongProxyForHandle(int32_t handle);
+ void expungeHandle(int32_t handle, IBinder* binder);
- void spawnPooledThread(bool isMain);
-
- status_t setThreadPoolMaxThreadCount(size_t maxThreads);
- status_t enableOnewaySpamDetection(bool enable);
- void giveThreadPoolName();
+ void spawnPooledThread(bool isMain);
- String8 getDriverName();
+ status_t setThreadPoolMaxThreadCount(size_t maxThreads);
+ status_t enableOnewaySpamDetection(bool enable);
+ void giveThreadPoolName();
- ssize_t getKernelReferences(size_t count, uintptr_t* buf);
+ String8 getDriverName();
- // Only usable by the context manager.
- // This refcount includes:
- // 1. Strong references to the node by this and other processes
- // 2. Temporary strong references held by the kernel during a
- // transaction on the node.
- // It does NOT include local strong references to the node
- ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
+ ssize_t getKernelReferences(size_t count, uintptr_t* buf);
- enum class CallRestriction {
- // all calls okay
- NONE,
- // log when calls are blocking
- ERROR_IF_NOT_ONEWAY,
- // abort process on blocking calls
- FATAL_IF_NOT_ONEWAY,
- };
- // Sets calling restrictions for all transactions in this process. This must be called
- // before any threads are spawned.
- void setCallRestriction(CallRestriction restriction);
+ // Only usable by the context manager.
+ // This refcount includes:
+ // 1. Strong references to the node by this and other processes
+ // 2. Temporary strong references held by the kernel during a
+ // transaction on the node.
+ // It does NOT include local strong references to the node
+ ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
+
+ enum class CallRestriction {
+ // all calls okay
+ NONE,
+ // log when calls are blocking
+ ERROR_IF_NOT_ONEWAY,
+ // abort process on blocking calls
+ FATAL_IF_NOT_ONEWAY,
+ };
+ // Sets calling restrictions for all transactions in this process. This must be called
+ // before any threads are spawned.
+ void setCallRestriction(CallRestriction restriction);
+
+ /**
+ * Get the max number of threads that the kernel can start.
+ *
+ * Note: this is the lower bound. Additional threads may be started.
+ */
+ size_t getThreadPoolMaxThreadCount() const;
private:
- static sp<ProcessState> init(const char *defaultDriver, bool requireDefault);
+ static sp<ProcessState> init(const char* defaultDriver, bool requireDefault);
friend class IPCThreadState;
friend class sp<ProcessState>;
- explicit ProcessState(const char* driver);
- ~ProcessState();
+ explicit ProcessState(const char* driver);
+ ~ProcessState();
- ProcessState(const ProcessState& o);
- ProcessState& operator=(const ProcessState& o);
- String8 makeBinderThreadName();
+ ProcessState(const ProcessState& o);
+ ProcessState& operator=(const ProcessState& o);
+ String8 makeBinderThreadName();
- struct handle_entry {
- IBinder* binder;
- RefBase::weakref_type* refs;
- };
+ struct handle_entry {
+ IBinder* binder;
+ RefBase::weakref_type* refs;
+ };
- handle_entry* lookupHandleLocked(int32_t handle);
+ handle_entry* lookupHandleLocked(int32_t handle);
- String8 mDriverName;
- int mDriverFD;
- void* mVMStart;
+ String8 mDriverName;
+ int mDriverFD;
+ void* mVMStart;
- // Protects thread count and wait variables below.
- pthread_mutex_t mThreadCountLock;
- // Broadcast whenever mWaitingForThreads > 0
- pthread_cond_t mThreadCountDecrement;
- // Number of binder threads current executing a command.
- size_t mExecutingThreadsCount;
- // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
- size_t mWaitingForThreads;
- // Maximum number for binder threads allowed for this process.
- size_t mMaxThreads;
- // Time when thread pool was emptied
- int64_t mStarvationStartTimeMs;
+ // Protects thread count and wait variables below.
+ pthread_mutex_t mThreadCountLock;
+ // Broadcast whenever mWaitingForThreads > 0
+ pthread_cond_t mThreadCountDecrement;
+ // Number of binder threads current executing a command.
+ size_t mExecutingThreadsCount;
+ // Number of threads calling IPCThreadState::blockUntilThreadAvailable()
+ size_t mWaitingForThreads;
+ // Maximum number for binder threads allowed for this process.
+ size_t mMaxThreads;
+ // Time when thread pool was emptied
+ int64_t mStarvationStartTimeMs;
- mutable Mutex mLock; // protects everything below.
+ mutable Mutex mLock; // protects everything below.
- Vector<handle_entry>mHandleToObject;
+ Vector<handle_entry> mHandleToObject;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
+ bool mThreadPoolStarted;
+ volatile int32_t mThreadPoolSeq;
- CallRestriction mCallRestriction;
+ CallRestriction mCallRestriction;
};
-
+
} // namespace android
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 8f0c6fd..50770f1 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -117,17 +117,27 @@
sp<IBinder> getRootObject();
/**
+ * Runs join() in a background thread. Immediately returns.
+ */
+ void start();
+
+ /**
* You must have at least one client session before calling this.
*
- * TODO(b/185167543): way to shut down?
+ * If a client needs to actively terminate join, call shutdown() in a separate thread.
+ *
+ * At any given point, there can only be one thread calling join().
*/
void join();
/**
- * Accept one connection on this server. You must have at least one client
- * session before calling this.
+ * Shut down any existing join(). Return true if successfully shut down, false otherwise
+ * (e.g. no join() is running). Will wait for the server to be fully
+ * shutdown.
+ *
+ * TODO(b/185167543): wait for sessions to shutdown as well
*/
- [[nodiscard]] bool acceptOne();
+ [[nodiscard]] bool shutdown();
/**
* For debugging!
@@ -145,20 +155,24 @@
friend sp<RpcServer>;
RpcServer();
- void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
+ static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd);
bool setupSocketServer(const RpcSocketAddress& address);
+ [[nodiscard]] bool acceptOne();
bool mAgreedExperimental = false;
- bool mStarted = false; // TODO(b/185167543): support dynamically added clients
size_t mMaxThreads = 1;
base::unique_fd mServer; // socket we are accepting sessions on
std::mutex mLock; // for below
+ std::unique_ptr<std::thread> mJoinThread;
+ bool mJoinThreadRunning = false;
std::map<std::thread::id, std::thread> mConnectingThreads;
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::map<int32_t, sp<RpcSession>> mSessions;
int32_t mSessionIdCounter = 0;
+ std::unique_ptr<RpcSession::FdTrigger> mShutdownTrigger;
+ std::condition_variable mShutdownCv;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index bcc213c..3d22002 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -112,6 +112,42 @@
friend RpcServer;
RpcSession();
+ /** This is not a pipe. */
+ struct FdTrigger {
+ static std::unique_ptr<FdTrigger> make();
+ /**
+ * poll() on this fd for POLLHUP to get notification when trigger is called
+ */
+ base::borrowed_fd readFd() const { return mRead; }
+
+ /**
+ * Close the write end of the pipe so that the read end receives POLLHUP.
+ */
+ void trigger();
+
+ /**
+ * Poll for a read event.
+ *
+ * Return:
+ * true - time to read!
+ * false - trigger happened
+ */
+ bool triggerablePollRead(base::borrowed_fd fd);
+
+ /**
+ * Read, but allow the read to be interrupted by this trigger.
+ *
+ * Return:
+ * true - read succeeded at 'size'
+ * false - interrupted (failure or trigger)
+ */
+ bool interruptableRecv(base::borrowed_fd fd, void* data, size_t size);
+
+ private:
+ base::unique_fd mWrite;
+ base::unique_fd mRead;
+ };
+
status_t readId();
// transfer ownership of thread
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 83190aa..5092d87 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -910,6 +910,9 @@
if (err != STATUS_OK) return err;
if (size < 0) return STATUS_UNEXPECTED_NULL;
+ // TODO(b/188215728): delegate to libbinder_ndk
+ if (size > 1000000) return STATUS_NO_MEMORY;
+
vec->resize(static_cast<size_t>(size));
return STATUS_OK;
}
@@ -931,6 +934,9 @@
return STATUS_OK;
}
+ // TODO(b/188215728): delegate to libbinder_ndk
+ if (size > 1000000) return STATUS_NO_MEMORY;
+
*vec = std::optional<std::vector<T>>(std::vector<T>{});
(*vec)->resize(static_cast<size_t>(size));
return STATUS_OK;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index ec7c7d8..b2f21c7 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -46,7 +46,8 @@
template <typename T>
using ArraySetter = void (*)(void* arrayData, size_t index, T value);
-binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) {
+static binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray,
+ int32_t length) {
// only -1 can be used to represent a null array
if (length < -1) return STATUS_BAD_VALUE;
@@ -61,12 +62,24 @@
Parcel* rawParcel = parcel->get();
- status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+ status_t status = rawParcel->writeInt32(length);
if (status != STATUS_OK) return PruneStatusT(status);
return STATUS_OK;
}
+static binder_status_t ReadAndValidateArraySize(const AParcel* parcel, int32_t* length) {
+ if (status_t status = parcel->get()->readInt32(length); status != STATUS_OK) {
+ return PruneStatusT(status);
+ }
+
+ if (*length < -1) return STATUS_BAD_VALUE; // libbinder_ndk reserves these
+ if (*length <= 0) return STATUS_OK; // null
+ if (static_cast<size_t>(*length) > parcel->get()->dataAvail()) return STATUS_NO_MEMORY;
+
+ return STATUS_OK;
+}
+
template <typename T>
binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) {
binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length);
@@ -111,10 +124,9 @@
const Parcel* rawParcel = parcel->get();
int32_t length;
- status_t status = rawParcel->readInt32(&length);
-
- if (status != STATUS_OK) return PruneStatusT(status);
- if (length < -1) return STATUS_BAD_VALUE;
+ if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+ return status;
+ }
T* array;
if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
@@ -140,10 +152,9 @@
const Parcel* rawParcel = parcel->get();
int32_t length;
- status_t status = rawParcel->readInt32(&length);
-
- if (status != STATUS_OK) return PruneStatusT(status);
- if (length < -1) return STATUS_BAD_VALUE;
+ if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+ return status;
+ }
char16_t* array;
if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
@@ -155,7 +166,7 @@
if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
for (int32_t i = 0; i < length; i++) {
- status = rawParcel->readChar(array + i);
+ status_t status = rawParcel->readChar(array + i);
if (status != STATUS_OK) return PruneStatusT(status);
}
@@ -189,10 +200,9 @@
const Parcel* rawParcel = parcel->get();
int32_t length;
- status_t status = rawParcel->readInt32(&length);
-
- if (status != STATUS_OK) return PruneStatusT(status);
- if (length < -1) return STATUS_BAD_VALUE;
+ if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+ return status;
+ }
if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
@@ -200,7 +210,7 @@
for (int32_t i = 0; i < length; i++) {
T readTarget;
- status = (rawParcel->*read)(&readTarget);
+ status_t status = (rawParcel->*read)(&readTarget);
if (status != STATUS_OK) return PruneStatusT(status);
setter(arrayData, i, readTarget);
@@ -402,13 +412,10 @@
binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData,
AParcel_stringArrayAllocator allocator,
AParcel_stringArrayElementAllocator elementAllocator) {
- const Parcel* rawParcel = parcel->get();
-
int32_t length;
- status_t status = rawParcel->readInt32(&length);
-
- if (status != STATUS_OK) return PruneStatusT(status);
- if (length < -1) return STATUS_BAD_VALUE;
+ if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+ return status;
+ }
if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
@@ -449,13 +456,10 @@
binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData,
AParcel_parcelableArrayAllocator allocator,
AParcel_readParcelableElement elementReader) {
- const Parcel* rawParcel = parcel->get();
-
int32_t length;
- status_t status = rawParcel->readInt32(&length);
-
- if (status != STATUS_OK) return PruneStatusT(status);
- if (length < -1) return STATUS_BAD_VALUE;
+ if (binder_status_t status = ReadAndValidateArraySize(parcel, &length); status != STATUS_OK) {
+ return status;
+ }
if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 49d3401..7d655d8 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -26,6 +26,7 @@
},
apex_available: [
"//apex_available:platform",
+ "com.android.compos",
"com.android.virt",
],
}
@@ -48,6 +49,7 @@
},
apex_available: [
"//apex_available:platform",
+ "com.android.compos",
"com.android.virt",
],
lints: "none",
@@ -99,6 +101,7 @@
},
apex_available: [
"//apex_available:platform",
+ "com.android.compos",
"com.android.virt",
],
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index ec231b2..9cf433d 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,7 @@
defaults: ["binder_test_defaults"],
srcs: ["binderLibTest.cpp"],
shared_libs: [
+ "libbase",
"libbinder",
"libutils",
],
@@ -101,6 +102,7 @@
srcs: ["binderLibTest.cpp"],
shared_libs: [
+ "libbase",
"libbinder",
"libutils",
],
@@ -133,6 +135,9 @@
darwin: {
enabled: false,
},
+ android: {
+ test_suites: ["vts"],
+ },
},
defaults: [
"binder_test_defaults",
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index ef4198d..41daccc 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -55,4 +55,6 @@
oneway void sleepMsAsync(int ms);
void die(boolean cleanup);
+
+ void useKernelBinderCallingId();
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 0c3fbcd..6006fd3 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -21,20 +21,29 @@
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
+
+#include <chrono>
#include <thread>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ParcelRef.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
#include <linux/sched.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include "../binder_module.h"
#include "binderAbiHelper.h"
@@ -42,7 +51,11 @@
#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
using namespace android;
+using namespace std::string_literals;
+using namespace std::chrono_literals;
+using testing::ExplainMatchResult;
using testing::Not;
+using testing::WithParamInterface;
// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
@@ -50,6 +63,15 @@
return expected == arg;
}
+// e.g. Result<int32_t> v = 0; EXPECT_THAT(result, ResultHasValue(0));
+MATCHER_P(ResultHasValue, resultMatcher, "") {
+ if (!arg.ok()) {
+ *result_listener << "contains error " << arg.error();
+ return false;
+ }
+ return ExplainMatchResult(resultMatcher, arg.value(), result_listener);
+}
+
static ::testing::AssertionResult IsPageAligned(void *buf) {
if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
return ::testing::AssertionSuccess();
@@ -73,6 +95,7 @@
BINDER_LIB_TEST_REGISTER_SERVER,
BINDER_LIB_TEST_ADD_SERVER,
BINDER_LIB_TEST_ADD_POLL_SERVER,
+ BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION,
BINDER_LIB_TEST_CALL_BACK,
BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
BINDER_LIB_TEST_DELAYED_CALL_BACK,
@@ -97,6 +120,8 @@
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
BINDER_LIB_TEST_CAN_GET_SID,
+ BINDER_LIB_TEST_USLEEP,
+ BINDER_LIB_TEST_CREATE_TEST_SERVICE,
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -157,6 +182,20 @@
return pid;
}
+android::base::Result<int32_t> GetId(sp<IBinder> service) {
+ using android::base::Error;
+ Parcel data, reply;
+ data.markForBinder(service);
+ const char *prefix = data.isForRpc() ? "On RPC server, " : "On binder server, ";
+ status_t status = service->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
+ if (status != OK)
+ return Error(status) << prefix << "transact(GET_ID): " << statusToString(status);
+ int32_t result = 0;
+ status = reply.readInt32(&result);
+ if (status != OK) return Error(status) << prefix << "readInt32: " << statusToString(status);
+ return result;
+}
+
class BinderLibTestEnv : public ::testing::Environment {
public:
BinderLibTestEnv() {}
@@ -476,12 +515,7 @@
}
TEST_F(BinderLibTest, GetId) {
- int32_t id;
- Parcel data, reply;
- EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
- StatusEq(NO_ERROR));
- EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
- EXPECT_EQ(0, id);
+ EXPECT_THAT(GetId(m_server), ResultHasValue(0));
}
TEST_F(BinderLibTest, PtrSize) {
@@ -604,6 +638,13 @@
EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, BinderCallContextGuard) {
+ sp<IBinder> binder = addServer();
+ Parcel data, reply;
+ EXPECT_THAT(binder->transact(BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION, data, &reply),
+ StatusEq(DEAD_OBJECT));
+}
+
TEST_F(BinderLibTest, AddServer)
{
sp<IBinder> server = addServer();
@@ -1165,39 +1206,187 @@
EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
}
-class BinderLibTestService : public BBinder
-{
- public:
- explicit BinderLibTestService(int32_t id)
- : m_id(id)
- , m_nextServerId(id + 1)
- , m_serverStartRequested(false)
- , m_callback(nullptr)
- {
- pthread_mutex_init(&m_serverWaitMutex, nullptr);
- pthread_cond_init(&m_serverWaitCond, nullptr);
+class BinderLibRpcTestBase : public BinderLibTest {
+public:
+ void SetUp() override {
+ if (!base::GetBoolProperty("ro.debuggable", false)) {
+ GTEST_SKIP() << "Binder RPC is only enabled on debuggable builds, skipping test on "
+ "non-debuggable builds.";
}
- ~BinderLibTestService()
- {
- exit(EXIT_SUCCESS);
- }
+ BinderLibTest::SetUp();
+ }
- void processPendingCall() {
- if (m_callback != nullptr) {
- Parcel data;
- data.writeInt32(NO_ERROR);
- m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
- m_callback = nullptr;
- }
+ std::tuple<android::base::unique_fd, unsigned int> CreateSocket() {
+ auto rpcServer = RpcServer::make();
+ EXPECT_NE(nullptr, rpcServer);
+ if (rpcServer == nullptr) return {};
+ rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ unsigned int port;
+ if (!rpcServer->setupInetServer(0, &port)) {
+ ADD_FAILURE() << "setupInetServer failed";
+ return {};
}
+ return {rpcServer->releaseServer(), port};
+ }
+};
- virtual status_t onTransact(uint32_t code,
- const Parcel& data, Parcel* reply,
- uint32_t flags = 0) {
- if (getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) {
- return PERMISSION_DENIED;
- }
- switch (code) {
+class BinderLibRpcTest : public BinderLibRpcTestBase, public WithParamInterface<bool> {
+public:
+ sp<IBinder> GetService() {
+ return GetParam() ? sp<IBinder>(addServer()) : sp<IBinder>(sp<BBinder>::make());
+ }
+ static std::string ParamToString(const testing::TestParamInfo<ParamType> &info) {
+ return info.param ? "remote" : "local";
+ }
+};
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreads) {
+ auto binder = GetService();
+ ASSERT_TRUE(binder != nullptr);
+ auto [socket, port] = CreateSocket();
+ ASSERT_TRUE(socket.ok());
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), 1), StatusEq(OK));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcClientNoFd) {
+ auto binder = GetService();
+ ASSERT_TRUE(binder != nullptr);
+ EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), 1), StatusEq(BAD_VALUE));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreadsZero) {
+ auto binder = GetService();
+ ASSERT_TRUE(binder != nullptr);
+ auto [socket, port] = CreateSocket();
+ ASSERT_TRUE(socket.ok());
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket), 0), StatusEq(BAD_VALUE));
+}
+
+TEST_P(BinderLibRpcTest, SetRpcMaxThreadsTwice) {
+ auto binder = GetService();
+ ASSERT_TRUE(binder != nullptr);
+
+ auto [socket1, port1] = CreateSocket();
+ ASSERT_TRUE(socket1.ok());
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket1), 1), StatusEq(OK));
+
+ auto [socket2, port2] = CreateSocket();
+ ASSERT_TRUE(socket2.ok());
+ EXPECT_THAT(binder->setRpcClientDebug(std::move(socket2), 1), StatusEq(ALREADY_EXISTS));
+}
+
+INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcTest, testing::Bool(),
+ BinderLibRpcTest::ParamToString);
+
+class BinderLibTestService;
+class BinderLibRpcClientTest : public BinderLibRpcTestBase,
+ public WithParamInterface<std::tuple<bool, uint32_t>> {
+public:
+ static std::string ParamToString(const testing::TestParamInfo<ParamType> &info) {
+ auto [isRemote, numThreads] = info.param;
+ return (isRemote ? "remote" : "local") + "_server_with_"s + std::to_string(numThreads) +
+ "_threads";
+ }
+ sp<IBinder> CreateRemoteService(int32_t id) {
+ Parcel data, reply;
+ status_t status = data.writeInt32(id);
+ EXPECT_THAT(status, StatusEq(OK));
+ if (status != OK) return nullptr;
+ status = m_server->transact(BINDER_LIB_TEST_CREATE_TEST_SERVICE, data, &reply);
+ EXPECT_THAT(status, StatusEq(OK));
+ if (status != OK) return nullptr;
+ sp<IBinder> ret;
+ status = reply.readStrongBinder(&ret);
+ EXPECT_THAT(status, StatusEq(OK));
+ if (status != OK) return nullptr;
+ return ret;
+ }
+};
+
+TEST_P(BinderLibRpcClientTest, Test) {
+ auto [isRemote, numThreadsParam] = GetParam();
+ uint32_t numThreads = numThreadsParam; // ... to be captured in lambda
+ int32_t id = 0xC0FFEE00 + numThreads;
+ sp<IBinder> server = isRemote ? sp<IBinder>(CreateRemoteService(id))
+ : sp<IBinder>(sp<BinderLibTestService>::make(id, false));
+ ASSERT_EQ(isRemote, !!server->remoteBinder());
+ ASSERT_THAT(GetId(server), ResultHasValue(id));
+
+ unsigned int port = 0;
+ // Fake servicedispatcher.
+ {
+ auto [socket, socketPort] = CreateSocket();
+ ASSERT_TRUE(socket.ok());
+ port = socketPort;
+ ASSERT_THAT(server->setRpcClientDebug(std::move(socket), numThreads), StatusEq(OK));
+ }
+
+ auto callUsleep = [](sp<IBinder> server, uint64_t us) {
+ Parcel data, reply;
+ data.markForBinder(server);
+ const char *name = data.isForRpc() ? "RPC" : "binder";
+ EXPECT_THAT(data.writeUint64(us), StatusEq(OK));
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_USLEEP, data, &reply), StatusEq(OK))
+ << "for " << name << " server";
+ };
+
+ auto threadFn = [&](size_t threadNum) {
+ usleep(threadNum * 50 * 1000); // threadNum * 50ms. Need this to avoid SYN flooding.
+ auto rpcSession = RpcSession::make();
+ ASSERT_TRUE(rpcSession->setupInetClient("127.0.0.1", port));
+ auto rpcServerBinder = rpcSession->getRootObject();
+ ASSERT_NE(nullptr, rpcServerBinder);
+
+ EXPECT_EQ(OK, rpcServerBinder->pingBinder());
+
+ // Check that |rpcServerBinder| and |server| points to the same service.
+ EXPECT_THAT(GetId(rpcServerBinder), ResultHasValue(id));
+
+ // Occupy the server thread. The server should still have enough threads to handle
+ // other connections.
+ // (numThreads - threadNum) * 100ms
+ callUsleep(rpcServerBinder, (numThreads - threadNum) * 100 * 1000);
+ };
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < numThreads; ++i) threads.emplace_back(std::bind(threadFn, i));
+ for (auto &t : threads) t.join();
+}
+
+INSTANTIATE_TEST_CASE_P(BinderLibTest, BinderLibRpcClientTest,
+ testing::Combine(testing::Bool(), testing::Range(1u, 10u)),
+ BinderLibRpcClientTest::ParamToString);
+
+class BinderLibTestService : public BBinder {
+public:
+ explicit BinderLibTestService(int32_t id, bool exitOnDestroy = true)
+ : m_id(id),
+ m_nextServerId(id + 1),
+ m_serverStartRequested(false),
+ m_callback(nullptr),
+ m_exitOnDestroy(exitOnDestroy) {
+ pthread_mutex_init(&m_serverWaitMutex, nullptr);
+ pthread_cond_init(&m_serverWaitCond, nullptr);
+ }
+ ~BinderLibTestService() {
+ if (m_exitOnDestroy) exit(EXIT_SUCCESS);
+ }
+
+ void processPendingCall() {
+ if (m_callback != nullptr) {
+ Parcel data;
+ data.writeInt32(NO_ERROR);
+ m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
+ m_callback = nullptr;
+ }
+ }
+
+ virtual status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0) {
+ // TODO(b/182914638): also checks getCallingUid() for RPC
+ if (!data.isForRpc() && getuid() != (uid_t)IPCThreadState::self()->getCallingUid()) {
+ return PERMISSION_DENIED;
+ }
+ switch (code) {
case BINDER_LIB_TEST_REGISTER_SERVER: {
int32_t id;
sp<IBinder> binder;
@@ -1207,8 +1396,7 @@
return BAD_VALUE;
}
- if (m_id != 0)
- return INVALID_OPERATION;
+ if (m_id != 0) return INVALID_OPERATION;
pthread_mutex_lock(&m_serverWaitMutex);
if (m_serverStartRequested) {
@@ -1262,6 +1450,21 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
+ case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
+ IPCThreadState::SpGuard spGuard{
+ .address = __builtin_frame_address(0),
+ .context = "GuardInBinderTransaction",
+ };
+ const IPCThreadState::SpGuard *origGuard =
+ IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+ // if the guard works, this should abort
+ (void)IPCThreadState::self()->getCallingPid();
+
+ IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+ return NO_ERROR;
+ }
+
case BINDER_LIB_TEST_GETPID:
reply->writeInt32(getpid());
return NO_ERROR;
@@ -1378,8 +1581,7 @@
return BAD_VALUE;
}
ret = target->linkToDeath(testDeathRecipient);
- if (ret == NO_ERROR)
- ret = testDeathRecipient->waitEvent(5);
+ if (ret == NO_ERROR) ret = testDeathRecipient->waitEvent(5);
data2.writeInt32(ret);
callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
return NO_ERROR;
@@ -1403,8 +1605,7 @@
return BAD_VALUE;
}
ret = write(fd, buf, size);
- if (ret != size)
- return UNKNOWN_ERROR;
+ if (ret != size) return UNKNOWN_ERROR;
return NO_ERROR;
}
case BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION: {
@@ -1459,8 +1660,7 @@
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
- if (err != NO_ERROR)
- return err;
+ if (err != NO_ERROR) return err;
reply->writeUint64Vector(vector);
return NO_ERROR;
}
@@ -1470,25 +1670,47 @@
case BINDER_LIB_TEST_CAN_GET_SID: {
return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR;
}
+ case BINDER_LIB_TEST_USLEEP: {
+ uint64_t us;
+ if (status_t status = data.readUint64(&us); status != NO_ERROR) return status;
+ usleep(us);
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CREATE_TEST_SERVICE: {
+ int32_t id;
+ if (status_t status = data.readInt32(&id); status != NO_ERROR) return status;
+ reply->writeStrongBinder(sp<BinderLibTestService>::make(id, false));
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
- };
- }
- private:
- int32_t m_id;
- int32_t m_nextServerId;
- pthread_mutex_t m_serverWaitMutex;
- pthread_cond_t m_serverWaitCond;
- bool m_serverStartRequested;
- sp<IBinder> m_serverStarted;
- sp<IBinder> m_strongRef;
- sp<IBinder> m_callback;
+ };
+ }
+
+private:
+ int32_t m_id;
+ int32_t m_nextServerId;
+ pthread_mutex_t m_serverWaitMutex;
+ pthread_cond_t m_serverWaitCond;
+ bool m_serverStartRequested;
+ sp<IBinder> m_serverStarted;
+ sp<IBinder> m_strongRef;
+ sp<IBinder> m_callback;
+ bool m_exitOnDestroy;
};
int run_server(int index, int readypipefd, bool usePoll)
{
binderLibTestServiceName += String16(binderserversuffix);
+ // Testing to make sure that calls that we are serving can use getCallin*
+ // even though we don't here.
+ IPCThreadState::SpGuard spGuard{
+ .address = __builtin_frame_address(0),
+ .context = "main server thread",
+ };
+ (void)IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
BinderLibTestService* testServicePtr;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index a96deb5..e10fe2f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -23,6 +23,7 @@
#include <android/binder_libbinder.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
@@ -40,6 +41,8 @@
#include "../RpcState.h" // for debugging
#include "../vm_sockets.h" // for VMADDR_*
+using namespace std::chrono_literals;
+
namespace android {
TEST(BinderRpcParcel, EntireParcelFormatted) {
@@ -191,6 +194,13 @@
_exit(1);
}
}
+ Status useKernelBinderCallingId() override {
+ // this is WRONG! It does not make sense when using RPC binder, and
+ // because it is SO wrong, and so much code calls this, it should abort!
+
+ (void)IPCThreadState::self()->getCallingPid();
+ return Status::ok();
+ }
};
sp<IBinder> MyBinderRpcTest::mHeldBinder;
@@ -887,6 +897,19 @@
}
}
+TEST_P(BinderRpc, UseKernelBinderCallingId) {
+ auto proc = createRpcTestSocketServerProcess(1);
+
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+
+ // second time! we catch the error :)
+ EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
+
+ proc.expectInvalid = true;
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
auto proc = createRpcTestSocketServerProcess(1);
@@ -970,6 +993,54 @@
INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
::testing::Combine(::testing::Bool(), ::testing::Bool()));
+class OneOffSignal {
+public:
+ // If notify() was previously called, or is called within |duration|, return true; else false.
+ template <typename R, typename P>
+ bool wait(std::chrono::duration<R, P> duration) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mCv.wait_for(lock, duration, [this] { return mValue; });
+ }
+ void notify() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mValue = true;
+ lock.unlock();
+ mCv.notify_all();
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCv;
+ bool mValue = false;
+};
+
+TEST(BinderRpc, Shutdown) {
+ auto addr = allocateSocketAddress();
+ unlink(addr.c_str());
+ auto server = RpcServer::make();
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ ASSERT_TRUE(server->setupUnixDomainServer(addr.c_str()));
+ auto joinEnds = std::make_shared<OneOffSignal>();
+
+ // If things are broken and the thread never stops, don't block other tests. Because the thread
+ // may run after the test finishes, it must not access the stack memory of the test. Hence,
+ // shared pointers are passed.
+ std::thread([server, joinEnds] {
+ server->join();
+ joinEnds->notify();
+ }).detach();
+
+ bool shutdown = false;
+ for (int i = 0; i < 10 && !shutdown; i++) {
+ usleep(300 * 1000); // 300ms; total 3s
+ if (server->shutdown()) shutdown = true;
+ }
+ ASSERT_TRUE(shutdown) << "server->shutdown() never returns true";
+
+ ASSERT_TRUE(joinEnds->wait(2s))
+ << "After server->shutdown() returns true, join() did not stop after 2s";
+}
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 624def1..a717ce9 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -66,6 +66,10 @@
int32_t mValue = 0;
};
+struct BigStruct {
+ uint8_t data[1337];
+};
+
#define PARCEL_READ_WITH_STATUS(T, FUN) \
[] (const ::android::Parcel& p, uint8_t /*data*/) {\
FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
@@ -165,22 +169,20 @@
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<ByteEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<IntEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<LongEnum>>, readEnumVector),
// only reading one parcelable type for now
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
- // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<ExampleParcelable>>>, readParcelableVector),
+ PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
PARCEL_READ_WITH_STATUS(std::optional<ExampleParcelable>, readParcelable),
@@ -189,45 +191,43 @@
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
- // PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
- // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
+ PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+ PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
- // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
- // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
- // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
- // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
- // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
- // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
- // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
- // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
- // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<int8_t>>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<int32_t>>, readInt32Vector),
+ PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<int64_t>>, readInt64Vector),
+ PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint64_t>>, readUint64Vector),
+ PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<float>>, readFloatVector),
+ PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<double>>, readDoubleVector),
+ PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<bool>>, readBoolVector),
+ PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<char16_t>>, readCharVector),
+ PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<android::String16>>>, readString16Vector),
+ PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
+ PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
[] (const android::Parcel& p, uint8_t /*len*/) {
FUZZ_LOG() << "about to read flattenable";
@@ -242,8 +242,12 @@
FUZZ_LOG() << "read lite flattenable: " << status;
},
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // TODO: resizeOutVector
+ PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<uint8_t>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::vector<BigStruct>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<BigStruct>>, resizeOutVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector),
PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
[] (const android::Parcel& p, uint8_t /*len*/) {
@@ -261,10 +265,9 @@
PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
- // TODO(b/131868573): can force read of arbitrarily sized vector
- // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
- // PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
- // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
[] (const android::Parcel& p, uint8_t len) {
FUZZ_LOG() << "about to readBlob";
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 008780c..6b783a4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -91,28 +91,27 @@
PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor),
PARCEL_READ(std::string, ndk::AParcel_readString),
PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString),
- // TODO(b/131868573): can force process to allocate arbitrary amount of
- // memory
- // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>,
- // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>,
- // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
- // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
- // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
- // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+
+ PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<SomeParcelable>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
+ PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
};
// clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index a47b753..2a79e85 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -23,7 +23,8 @@
#include <iostream>
#include <android-base/logging.h>
-#include <binder/RpcSession.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_libbinder.h>
#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
@@ -33,7 +34,6 @@
#include <sys/time.h>
using android::fillRandomParcel;
-using android::RpcSession;
using android::sp;
void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
@@ -46,9 +46,22 @@
fillRandomParcel(p->parcel(), std::move(provider));
}
+template <typename P, typename B>
+void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider&& provider) {
+ uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ uint32_t flag = provider.ConsumeIntegral<uint32_t>();
+
+ FUZZ_LOG() << "backend: " << backend;
+
+ P reply;
+ P data;
+ fillRandomParcel(&data, std::move(provider));
+ (void)binder->transact(code, data, &reply, flag);
+}
+
template <typename P>
-void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
- FuzzedDataProvider&& provider) {
+void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ FuzzedDataProvider&& provider) {
// Allow some majority of the bytes to be dedicated to telling us what to
// do. The fixed value added here represents that we want to test doing a
// lot of 'instructions' even on really short parcels.
@@ -59,18 +72,7 @@
provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
P p;
- if constexpr (std::is_same_v<P, android::Parcel>) {
- if (provider.ConsumeBool()) {
- auto session = sp<RpcSession>::make();
- CHECK(session->addNullDebuggingClient());
- p.markForRpc(session);
- fillRandomParcelData(&p, std::move(provider));
- } else {
- fillRandomParcel(&p, std::move(provider));
- }
- } else {
- fillRandomParcel(&p, std::move(provider));
- }
+ fillRandomParcel(&p, std::move(provider));
// since we are only using a byte to index
CHECK(reads.size() <= 255) << reads.size();
@@ -95,25 +97,18 @@
}
}
-size_t getHardMemoryLimit() {
- struct rlimit limit;
- CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
- return limit.rlim_max;
+void* NothingClass_onCreate(void* args) {
+ return args;
}
-
-void setMemoryLimit(size_t cur, size_t max) {
- const struct rlimit kLimit = {
- .rlim_cur = cur,
- .rlim_max = max,
- };
- CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+void NothingClass_onDestroy(void* /*userData*/) {}
+binder_status_t NothingClass_onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
+ return STATUS_UNKNOWN_ERROR;
}
+static AIBinder_Class* kNothingClass =
+ AIBinder_Class_define("nothing", NothingClass_onCreate, NothingClass_onDestroy,
+ NothingClass_onTransact);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static constexpr size_t kMemLimit = 1 * 1024 * 1024;
- size_t hardLimit = getHardMemoryLimit();
- setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
-
if (size <= 1) return 0; // no use
// avoid timeouts, see b/142617274, b/142473153
@@ -121,24 +116,39 @@
FuzzedDataProvider provider = FuzzedDataProvider(data, size);
- const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+ const std::function<void(FuzzedDataProvider &&)> fuzzBackend[] = {
[](FuzzedDataProvider&& provider) {
- doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
- std::move(provider));
+ doTransactFuzz<
+ ::android::hardware::Parcel>("hwbinder",
+ sp<::android::hardware::BHwBinder>::make(),
+ std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
- std::move(provider));
+ doTransactFuzz<::android::Parcel>("binder", sp<::android::BBinder>::make(),
+ std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
- std::move(provider));
+ // fuzz from the libbinder layer since it's a superset of the
+ // interface you get at the libbinder_ndk layer
+ auto ndkBinder = ndk::SpAIBinder(AIBinder_new(kNothingClass, nullptr));
+ auto binder = AIBinder_toPlatformBinder(ndkBinder.get());
+ doTransactFuzz<::android::Parcel>("binder_ndk", binder, std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doReadFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doReadFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
},
};
provider.PickValueInArray(fuzzBackend)(std::move(provider));
- setMemoryLimit(hardLimit, hardLimit);
-
return 0;
}
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index b045a22..92fdc72 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
#include <fuzzbinder/random_fd.h>
#include <utils/String16.h>
@@ -33,6 +34,14 @@
};
void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+ if (provider.ConsumeBool()) {
+ auto session = sp<RpcSession>::make();
+ CHECK(session->addNullDebuggingClient());
+ p->markForRpc(session);
+ fillRandomParcelData(p, std::move(provider));
+ return;
+ }
+
while (provider.remaining_bytes() > 0) {
auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
// write data
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index 3603ebe..e6fd392 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
+#include <fuzzer/FuzzedDataProvider.h>
#include <sys/resource.h>
#include <sys/un.h>
@@ -29,20 +30,6 @@
static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
"/binderRpcFuzzerSocket_" + std::to_string(getpid());
-size_t getHardMemoryLimit() {
- struct rlimit limit;
- CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
- return limit.rlim_max;
-}
-
-void setMemoryLimit(size_t cur, size_t max) {
- const struct rlimit kLimit = {
- .rlim_cur = cur,
- .rlim_max = max,
- };
- CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
-}
-
class SomeBinder : public BBinder {
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
(void)flags;
@@ -67,6 +54,7 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size > 50000) return 0;
+ FuzzedDataProvider provider(data, size);
unlink(kSock.c_str());
@@ -75,11 +63,7 @@
server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
CHECK(server->setupUnixDomainServer(kSock.c_str()));
- static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
- size_t hardLimit = getHardMemoryLimit();
- setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
-
- std::thread serverThread([=] { (void)server->acceptOne(); });
+ std::thread serverThread([=] { (void)server->join(); });
sockaddr_un addr{
.sun_family = AF_UNIX,
@@ -94,8 +78,6 @@
connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
<< strerror(errno);
- serverThread.join();
-
// TODO(b/182938024): fuzz multiple sessions, instead of just one
#if 0
@@ -104,17 +86,28 @@
CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
#endif
- CHECK(base::WriteFully(clientFd, data, size));
+ bool hangupBeforeShutdown = provider.ConsumeBool();
+
+ std::vector<uint8_t> writeData = provider.ConsumeRemainingBytes<uint8_t>();
+ CHECK(base::WriteFully(clientFd, writeData.data(), writeData.size()));
+
+ if (hangupBeforeShutdown) {
+ clientFd.reset();
+ }
+
+ // TODO(185167543): currently this is okay because we only shutdown the one
+ // thread, but once we can shutdown other sessions, we'll need to change
+ // this behavior in order to make sure all of the input is actually read.
+ while (!server->shutdown()) usleep(100);
clientFd.reset();
+ serverThread.join();
// TODO(b/185167543): better way to force a server to shutdown
while (!server->listSessions().empty() && server->numUninitializedSessions()) {
usleep(1);
}
- setMemoryLimit(hardLimit, hardLimit);
-
return 0;
}
diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
index 69f1b9d..72c5bc4 100644
--- a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
@@ -72,6 +72,11 @@
},
[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
bbinder->getDebugPid();
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ auto rpcMaxThreads = fdp->ConsumeIntegralInRange<uint32_t>(0, 20);
+ (void)bbinder->setRpcClientDebug(android::base::unique_fd(),
+ rpcMaxThreads);
}};
} // namespace android