Merge "Make available to com.android.compos apex"
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,4 +1,5 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 4f6a89e..97a63ca 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,5 +1,6 @@
set noparent
+gavincorkery@google.com
nandana@google.com
jsharkey@android.com
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index fc745d0..d6807ff 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -9,3 +9,4 @@
ngeoffray@google.com
rpl@google.com
toddke@google.com
+patb@google.com
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index ef7fd44..18b77e6 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,19 +366,45 @@
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;
}
+IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(SpGuard* guard) {
+ SpGuard* orig = mServingStackPointerGuard;
+ mServingStackPointerGuard = guard;
+ return orig;
+}
+
+void IPCThreadState::restoreGetCallingSpGuard(SpGuard* guard) {
+ mServingStackPointerGuard = guard;
+}
+
+void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
+ if (mServingStackPointerGuard == nullptr) return;
+
+ if (!mServingStackPointer || mServingStackPointerGuard < mServingStackPointer) {
+ LOG_ALWAYS_FATAL("In context %s, %s does not make sense.",
+ mServingStackPointerGuard->context, use);
+ }
+
+ // 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
@@ -847,15 +873,15 @@
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()),
- mServingStackPointer(nullptr),
- mWorkSource(kUnsetWorkSource),
- mPropagateWorkSource(false),
- mIsLooper(false),
- mStrictModePolicy(0),
- mLastTransactionBinderFlags(0),
- mCallRestriction(mProcess->mCallRestriction)
-{
+ : mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
+ mServingStackPointerGuard(nullptr),
+ mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
+ mIsLooper(false),
+ mStrictModePolicy(0),
+ mLastTransactionBinderFlags(0),
+ mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index c0cdcd6..9cc6e7f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -22,6 +22,7 @@
#include <thread>
#include <vector>
+#include <android-base/scopeguard.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <log/log.h>
@@ -32,6 +33,7 @@
namespace android {
+using base::ScopeGuard;
using base::unique_fd;
RpcServer::RpcServer() {}
@@ -107,65 +109,51 @@
void RpcServer::setRootObject(const sp<IBinder>& binder) {
std::lock_guard<std::mutex> _l(mLock);
- mRootObject = binder;
+ mRootObjectWeak = mRootObject = binder;
+}
+
+void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mRootObject.clear();
+ mRootObjectWeak = binder;
}
sp<IBinder> RpcServer::getRootObject() {
std::lock_guard<std::mutex> _l(mLock);
- return mRootObject;
+ bool hasWeak = mRootObjectWeak.unsafe_get();
+ sp<IBinder> ret = mRootObjectWeak.promote();
+ ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
+ return ret;
}
void RpcServer::join() {
+ while (true) {
+ (void)acceptOne();
+ }
+}
+
+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)));
+
+ if (clientFd < 0) {
+ ALOGE("Could not accept4 socket: %s", strerror(errno));
+ return false;
+ }
+ LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
{
std::lock_guard<std::mutex> _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join.");
+ std::thread thread =
+ std::thread(&RpcServer::establishConnection, this,
+ std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+ mConnectingThreads[thread.get_id()] = std::move(thread);
}
- while (true) {
- unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
-
- if (clientFd < 0) {
- ALOGE("Could not accept4 socket: %s", strerror(errno));
- continue;
- }
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
- // TODO(b/183988761): cannot trust this simple ID, should not block this
- // thread
- LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
- int32_t id;
- if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
- ALOGE("Could not read ID from fd %d", clientFd.get());
- continue;
- }
-
- {
- std::lock_guard<std::mutex> _l(mLock);
-
- sp<RpcSession> session;
- if (id == RPC_SESSION_ID_NEW) {
- // new client!
- LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
- mSessionIdCounter++;
-
- session = RpcSession::make();
- session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
-
- mSessions[mSessionIdCounter] = session;
- } else {
- auto it = mSessions.find(id);
- if (it == mSessions.end()) {
- ALOGE("Cannot add thread, no record of session with ID %d", id);
- continue;
- }
- session = it->second;
- }
-
- session->startThread(std::move(clientFd));
- }
- }
+ return true;
}
std::vector<sp<RpcSession>> RpcServer::listSessions() {
@@ -178,14 +166,74 @@
return sessions;
}
-bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
- LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+size_t RpcServer::numUninitializedSessions() {
+ std::lock_guard<std::mutex> _l(mLock);
+ return mConnectingThreads.size();
+}
+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;
+ int32_t id;
+ if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+ ALOGE("Could not read ID from fd %d", clientFd.get());
+ idValid = false;
+ }
+
+ std::thread thisThread;
+ sp<RpcSession> session;
{
std::lock_guard<std::mutex> _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcServer can only have one server.");
+
+ auto threadId = mConnectingThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+ "Must establish connection on owned thread");
+ thisThread = std::move(threadId->second);
+ ScopeGuard detachGuard = [&]() { thisThread.detach(); };
+ mConnectingThreads.erase(threadId);
+
+ if (!idValid) {
+ return;
+ }
+
+ if (id == RPC_SESSION_ID_NEW) {
+ LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+ mSessionIdCounter++;
+
+ session = RpcSession::make();
+ session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+ mSessions[mSessionIdCounter] = session;
+ } else {
+ auto it = mSessions.find(id);
+ if (it == mSessions.end()) {
+ ALOGE("Cannot add thread, no record of session with ID %d", id);
+ return;
+ }
+ session = it->second;
+ }
+
+ detachGuard.Disable();
+ session->preJoin(std::move(thisThread));
}
+ // avoid strong cycle
+ server = nullptr;
+ //
+ //
+ // DO NOT ACCESS MEMBER VARIABLES BELOW
+ //
+
+ session->join(std::move(clientFd));
+}
+
+bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+ LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+ LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
+
unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
if (serverFd == -1) {
@@ -223,4 +271,27 @@
(void)mSessions.erase(it);
}
+bool RpcServer::hasServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return mServer.ok();
+}
+
+unique_fd RpcServer::releaseServer() {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ return std::move(mServer);
+}
+
+bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+ LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+ std::lock_guard<std::mutex> _l(mLock);
+ if (mServer.ok()) {
+ ALOGE("Each RpcServer can only have one server.");
+ return false;
+ }
+ mServer = std::move(serverFd);
+ return true;
+}
+
} // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index f38135b..05fa49e 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -131,21 +131,13 @@
return OK;
}
-void RpcSession::startThread(unique_fd client) {
- std::lock_guard<std::mutex> _l(mMutex);
- sp<RpcSession> holdThis = sp<RpcSession>::fromExisting(this);
- int fd = client.release();
- auto thread = std::thread([=] {
- holdThis->join(unique_fd(fd));
- {
- std::lock_guard<std::mutex> _l(holdThis->mMutex);
- auto it = mThreads.find(std::this_thread::get_id());
- LOG_ALWAYS_FATAL_IF(it == mThreads.end());
- it->second.detach();
- mThreads.erase(it);
- }
- });
- mThreads[thread.get_id()] = std::move(thread);
+void RpcSession::preJoin(std::thread thread) {
+ LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ mThreads[thread.get_id()] = std::move(thread);
+ }
}
void RpcSession::join(unique_fd client) {
@@ -165,6 +157,14 @@
LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
"bad state: connection object guaranteed to be in list");
+
+ {
+ std::lock_guard<std::mutex> _l(mMutex);
+ auto it = mThreads.find(std::this_thread::get_id());
+ LOG_ALWAYS_FATAL_IF(it == mThreads.end());
+ it->second.detach();
+ mThreads.erase(it);
+ }
}
void RpcSession::terminateLocked() {
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 20fdbfe..e5a6026 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() {}
@@ -182,6 +186,27 @@
}
}
+RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+ // The maximum size for regular binder is 1MB for all concurrent
+ // transactions. A very small proportion of transactions are even
+ // larger than a page, but we need to avoid allocating too much
+ // data on behalf of an arbitrary client, or we could risk being in
+ // a position where a single additional allocation could run out of
+ // memory.
+ //
+ // Note, this limit may not reflect the total amount of data allocated for a
+ // transaction (in some cases, additional fixed size amounts are added),
+ // though for rough consistency, we should avoid cases where this data type
+ // is used for multiple dynamic allocations for a single transaction.
+ constexpr size_t kMaxTransactionAllocation = 100 * 1000;
+ if (size == 0) return;
+ if (size > kMaxTransactionAllocation) {
+ ALOGW("Transaction requested too much data allocation %zu", size);
+ return;
+ }
+ mData.reset(new (std::nothrow) uint8_t[size]);
+}
+
bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
@@ -326,7 +351,7 @@
.asyncNumber = asyncNumber,
};
- ByteVec transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+ CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize());
if (!transactionData.valid()) {
return NO_MEMORY;
}
@@ -383,7 +408,7 @@
if (status != OK) return status;
}
- ByteVec data(command.bodySize);
+ CommandData data(command.bodySize);
if (!data.valid()) {
return NO_MEMORY;
}
@@ -449,6 +474,18 @@
status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
const RpcWireHeader& command) {
+ IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
+ IPCThreadState::SpGuard spGuard{"processing binder RPC command"};
+ 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);
@@ -469,7 +506,7 @@
const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
- ByteVec transactionData(command.bodySize);
+ CommandData transactionData(command.bodySize);
if (!transactionData.valid()) {
return NO_MEMORY;
}
@@ -490,7 +527,7 @@
}
status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
- ByteVec transactionData) {
+ CommandData transactionData) {
if (transactionData.size() < sizeof(RpcWireTransaction)) {
ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
sizeof(RpcWireTransaction), transactionData.size());
@@ -640,7 +677,7 @@
// justification for const_cast (consider avoiding priority_queue):
// - AsyncTodo operator< doesn't depend on 'data' object
// - gotta go fast
- ByteVec data = std::move(
+ CommandData data = std::move(
const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
it->second.asyncTodo.pop();
_l.unlock();
@@ -654,7 +691,7 @@
.status = replyStatus,
};
- ByteVec replyData(sizeof(RpcWireReply) + reply.dataSize());
+ CommandData replyData(sizeof(RpcWireReply) + reply.dataSize());
if (!replyData.valid()) {
return NO_MEMORY;
}
@@ -684,7 +721,7 @@
status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
- ByteVec commandData(command.bodySize);
+ CommandData commandData(command.bodySize);
if (!commandData.valid()) {
return NO_MEMORY;
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 83d0344..31f8a22 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -101,10 +101,10 @@
*/
void terminate();
- // alternative to std::vector<uint8_t> that doesn't abort on too big of allocations
- struct ByteVec {
- explicit ByteVec(size_t size)
- : mData(size > 0 ? new (std::nothrow) uint8_t[size] : nullptr), mSize(size) {}
+ // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
+ // large allocations to avoid being requested from allocating too much data.
+ struct CommandData {
+ explicit CommandData(size_t size);
bool valid() { return mSize == 0 || mData != nullptr; }
size_t size() { return mSize; }
uint8_t* data() { return mData.get(); }
@@ -128,7 +128,7 @@
const RpcWireHeader& command);
[[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
const sp<RpcSession>& session,
- ByteVec transactionData);
+ CommandData transactionData);
[[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
const RpcWireHeader& command);
@@ -163,7 +163,7 @@
// async transaction queue, _only_ for local binder
struct AsyncTodo {
- ByteVec data;
+ CommandData data;
uint64_t asyncNumber = 0;
bool operator<(const AsyncTodo& o) const {
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 23a0cb0..5220b62 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -81,6 +81,32 @@
*/
uid_t getCallingUid() const;
+ /**
+ * Make it an abort to rely on getCalling* for a section of
+ * execution.
+ *
+ * Usage:
+ * IPCThreadState::SpGuard guard { "..." };
+ * auto* orig = pushGetCallingSpGuard(&guard);
+ * {
+ * // will abort if you call getCalling*, unless you are
+ * // serving a nested binder transaction
+ * }
+ * restoreCallingSpGuard(orig);
+ */
+ struct SpGuard {
+ const char* context;
+ };
+ SpGuard* pushGetCallingSpGuard(SpGuard* guard);
+ void restoreGetCallingSpGuard(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 +229,7 @@
Parcel mOut;
status_t mLastError;
const void* mServingStackPointer;
+ SpGuard* mServingStackPointerGuard;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 9659732..f3ba830 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -50,8 +50,12 @@
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
/**
* Force the service to persist, even when it has 0 clients.
- * If setting this flag from the server side, make sure to do so before calling registerService,
- * or there may be a race with the default dynamic shutdown.
+ * If setting this flag from the server side, make sure to do so before calling
+ * registerService, or there may be a race with the default dynamic shutdown.
+ *
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism such as ctl.start.
*/
void forcePersist(bool persist);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 3534d51..8f0c6fd 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -22,6 +22,7 @@
#include <utils/RefBase.h>
#include <mutex>
+#include <thread>
// WARNING: This is a feature which is still in development, and it is subject
// to radical change. Any production use of this may subject your code to any
@@ -73,6 +74,22 @@
*/
[[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
+ /**
+ * If setup*Server has been successful, return true. Otherwise return false.
+ */
+ [[nodiscard]] bool hasServer();
+
+ /**
+ * If hasServer(), return the server FD. Otherwise return invalid FD.
+ */
+ [[nodiscard]] base::unique_fd releaseServer();
+
+ /**
+ * Set up server using an external FD previously set up by releaseServer().
+ * Return false if there's already a server.
+ */
+ bool setupExternalServer(base::unique_fd serverFd);
+
void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
/**
@@ -89,8 +106,14 @@
/**
* The root object can be retrieved by any client, without any
* authentication. TODO(b/183988761)
+ *
+ * Holds a strong reference to the root object.
*/
void setRootObject(const sp<IBinder>& binder);
+ /**
+ * Holds a weak reference to the root object.
+ */
+ void setRootObjectWeak(const wp<IBinder>& binder);
sp<IBinder> getRootObject();
/**
@@ -101,9 +124,16 @@
void join();
/**
+ * Accept one connection on this server. You must have at least one client
+ * session before calling this.
+ */
+ [[nodiscard]] bool acceptOne();
+
+ /**
* For debugging!
*/
std::vector<sp<RpcSession>> listSessions();
+ size_t numUninitializedSessions();
~RpcServer();
@@ -115,6 +145,7 @@
friend sp<RpcServer>;
RpcServer();
+ void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
bool setupSocketServer(const RpcSocketAddress& address);
bool mAgreedExperimental = false;
@@ -123,7 +154,9 @@
base::unique_fd mServer; // socket we are accepting sessions on
std::mutex mLock; // for below
+ 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;
};
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index c8ab9e4..bcc213c 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -114,7 +114,9 @@
status_t readId();
- void startThread(base::unique_fd client);
+ // transfer ownership of thread
+ void preJoin(std::thread thread);
+ // join on thread passed to preJoin
void join(base::unique_fd client);
void terminateLocked();
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index a90b4aa..2a66941 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -35,7 +35,7 @@
* \return EX_NONE on success.
*/
__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService(
- AIBinder* binder, const char* instance);
+ AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -47,7 +47,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
@@ -59,7 +60,8 @@
*
* \param instance identifier of the service used to lookup the service.
*/
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
+ __INTRODUCED_IN(29);
/**
* Registers a lazy service with the default service manager under the 'instance' name.
@@ -135,6 +137,10 @@
/**
* Prevent lazy services without client from shutting down their process
*
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism.
+ *
* \param persist 'true' if the process should not exit.
*/
void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31);
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..45b2776 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -73,6 +73,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,
@@ -604,6 +605,24 @@
EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, NoBinderCallContextGuard) {
+ IPCThreadState::SpGuard spGuard{"NoBinderCallContext"};
+ IPCThreadState::SpGuard *origGuard = IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+ // yes, this test uses threads, but it's careful and uses fork in addServer
+ EXPECT_DEATH({ IPCThreadState::self()->getCallingPid(); },
+ "In context NoBinderCallContext, getCallingPid does not make sense.");
+
+ IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+}
+
+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();
@@ -1262,6 +1281,18 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
+ case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
+ IPCThreadState::SpGuard spGuard{"GuardInBinderTransaction"};
+ 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;
@@ -1489,6 +1520,11 @@
{
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{"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 b3ce744..3f94df2 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>
@@ -49,6 +50,19 @@
EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
}
+TEST(BinderRpc, SetExternalServer) {
+ base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ int sinkFd = sink.get();
+ auto server = RpcServer::make();
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+ ASSERT_TRUE(server->hasServer());
+ base::unique_fd retrieved = server->releaseServer();
+ ASSERT_FALSE(server->hasServer());
+ ASSERT_EQ(sinkFd, retrieved.get());
+}
+
using android::binder::Status;
#define EXPECT_OK(status) \
@@ -178,6 +192,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;
@@ -874,6 +895,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);
@@ -929,6 +963,34 @@
}),
PrintSocketType);
+class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+
+TEST_P(BinderRpcServerRootObject, WeakRootObject) {
+ using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
+ auto setRootObject = [](bool isStrong) -> SetFn {
+ return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
+ };
+
+ auto server = RpcServer::make();
+ auto [isStrong1, isStrong2] = GetParam();
+ auto binder1 = sp<BBinder>::make();
+ IBinder* binderRaw1 = binder1.get();
+ setRootObject(isStrong1)(server.get(), binder1);
+ EXPECT_EQ(binderRaw1, server->getRootObject());
+ binder1.clear();
+ EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject());
+
+ auto binder2 = sp<BBinder>::make();
+ IBinder* binderRaw2 = binder2.get();
+ setRootObject(isStrong2)(server.get(), binder2);
+ EXPECT_EQ(binderRaw2, server->getRootObject());
+ binder2.clear();
+ EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
+ ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
} // namespace android
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
new file mode 100644
index 0000000..1c75306
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+ name: "binder_rpc_fuzzer",
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
+
+ srcs: [
+ "main.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder",
+ ],
+ },
+ },
+}
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
new file mode 100644
index 0000000..3603ebe
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <sys/resource.h>
+#include <sys/un.h>
+
+namespace android {
+
+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;
+
+ if ((code & 1) == 0) {
+ sp<IBinder> binder;
+ (void)data.readStrongBinder(&binder);
+ if (binder != nullptr) {
+ (void)binder->pingBinder();
+ }
+ }
+ if ((code & 2) == 0) {
+ (void)data.readInt32();
+ }
+ if ((code & 4) == 0) {
+ (void)reply->writeStrongBinder(sp<BBinder>::make());
+ }
+
+ return OK;
+ }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > 50000) return 0;
+
+ unlink(kSock.c_str());
+
+ sp<RpcServer> server = RpcServer::make();
+ server->setRootObject(sp<SomeBinder>::make());
+ 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(); });
+
+ sockaddr_un addr{
+ .sun_family = AF_UNIX,
+ };
+ CHECK_LT(kSock.size(), sizeof(addr.sun_path));
+ memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
+
+ base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ CHECK_NE(clientFd.get(), -1);
+ CHECK_EQ(0,
+ TEMP_FAILURE_RETRY(
+ 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
+ // make fuzzer more productive locally by forcing it to create a new session
+ int32_t id = -1;
+ CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
+#endif
+
+ CHECK(base::WriteFully(clientFd, data, size));
+
+ clientFd.reset();
+
+ // TODO(b/185167543): better way to force a server to shutdown
+ while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+ usleep(1);
+ }
+
+ setMemoryLimit(hardLimit, hardLimit);
+
+ return 0;
+}
+
+} // namespace android