Skip to content

Commit 4d78e34

Browse files
committed
Support deleting param for preconnected sessions
Although we can pass arbitrary pointers to setup preconnected clients, the lifetime isn't clear and it's very easy to make UAF bugs. To prevent UAF, an additional function can be passed to delete param when param is no longer required, effectively transferring the ownership of param to RPC session. Bug: 398890208 Test: atest MicrodroidTests Change-Id: I1a1c149a56876f56fba81b89cdc90ee372d2d7fe
1 parent 48f26a3 commit 4d78e34

6 files changed

Lines changed: 50 additions & 16 deletions

File tree

libs/binder/RpcSession.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) {
188188
}
189189

190190
status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
191-
return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
191+
return setupClient([&, fd = std::move(fd),
192+
request = std::move(request)](const std::vector<uint8_t>& sessionId,
193+
bool incoming) mutable -> status_t {
192194
if (!fd.ok()) {
193195
fd = request();
194196
if (!fd.ok()) return BAD_VALUE;
@@ -476,8 +478,10 @@ sp<RpcServer> RpcSession::server() {
476478
return server;
477479
}
478480

479-
status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId,
480-
bool incoming)>& connectAndInit) {
481+
template <typename Fn,
482+
typename /* = std::enable_if_t<std::is_invocable_r_v<
483+
status_t, Fn, const std::vector<uint8_t>&, bool>> */>
484+
status_t RpcSession::setupClient(Fn&& connectAndInit) {
481485
{
482486
RpcMutexLockGuard _l(mMutex);
483487
LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once");

libs/binder/include/binder/RpcSession.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <map>
2727
#include <optional>
28+
#include <type_traits>
2829
#include <vector>
2930

3031
namespace android {
@@ -291,9 +292,14 @@ class RpcSession final : public virtual RefBase {
291292
// join on thread passed to preJoinThreadOwnership
292293
static void join(sp<RpcSession>&& session, PreJoinSetupResult&& result);
293294

294-
[[nodiscard]] status_t setupClient(
295-
const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>&
296-
connectAndInit);
295+
// This is a workaround to support move-only functors.
296+
// TODO: use std::move_only_function when it becomes available.
297+
template <typename Fn,
298+
// Fn must be a callable type taking (const std::vector<uint8_t>&, bool) and returning
299+
// status_t
300+
typename = std::enable_if_t<
301+
std::is_invocable_r_v<status_t, Fn, const std::vector<uint8_t>&, bool>>>
302+
[[nodiscard]] status_t setupClient(Fn&& connectAndInit);
297303
[[nodiscard]] status_t setupSocketClient(const RpcSocketAddress& address);
298304
[[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address,
299305
const std::vector<uint8_t>& sessionId,

libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,14 @@ AIBinder* ARpcSession_setupInet(ARpcSession* session, const char* address, unsig
134134
//
135135
// param will be passed to requestFd. Callers can use param to pass contexts to
136136
// the requestFd function.
137+
//
138+
// paramDeleteFd will be called when requestFd and param are no longer in use.
139+
// Callers can pass a function deleting param if necessary. Callers can set
140+
// paramDeleteFd to null if callers doesn't need to clean up param.
137141
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
138142
int (*requestFd)(void* param),
139-
void* param);
143+
void* param,
144+
void (*paramDeleteFd)(void* param));
140145

141146
// Sets the file descriptor transport mode for this session.
142147
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,

libs/binder/libbinder_rpc_unstable.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,18 @@ AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsign
248248
#endif // __TRUSTY__
249249

250250
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
251-
void* param) {
251+
void* param, void (*paramDeleteFd)(void* param)) {
252252
auto session = handleToStrongPointer<RpcSession>(handle);
253-
auto request = [=] { return unique_fd{requestFd(param)}; };
253+
auto deleter = [=](void* param) {
254+
if (paramDeleteFd) {
255+
paramDeleteFd(param);
256+
}
257+
};
258+
// TODO: use unique_ptr once setupPreconnectedClient uses std::move_only_function.
259+
std::shared_ptr<void> sharedParam(param, deleter);
260+
auto request = [=, sharedParam = std::move(sharedParam)] {
261+
return unique_fd{requestFd(sharedParam.get())};
262+
};
254263
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
255264
ALOGE("Failed to set up preconnected client. error: %s", statusToString(status).c_str());
256265
return nullptr;

libs/binder/rust/rpcbinder/src/session.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,13 @@ impl RpcSessionRef {
195195
/// take ownership of) file descriptors already connected to it.
196196
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
197197
&self,
198-
mut request_fd: impl FnMut() -> Option<RawFd>,
198+
request_fd: impl FnMut() -> Option<RawFd>,
199199
) -> Result<Strong<T>, StatusCode> {
200-
// Double reference the factory because trait objects aren't FFI safe.
201-
let mut request_fd_ref: RequestFd = &mut request_fd;
202-
let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
200+
// Trait objects aren't FFI safe, so *mut c_void can't be converted back to
201+
// *mut dyn FnMut() -> Option<RawFd>>. Double box the factory to make it possible to get
202+
// the factory from *mut c_void (to *mut Box<dyn<...>>) in the callbacks.
203+
let request_fd_box: Box<dyn FnMut() -> Option<RawFd>> = Box::new(request_fd);
204+
let param = Box::into_raw(Box::new(request_fd_box)) as *mut c_void;
203205

204206
// SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
205207
// ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
@@ -209,6 +211,7 @@ impl RpcSessionRef {
209211
self.as_ptr(),
210212
Some(request_fd_wrapper),
211213
param,
214+
Some(param_delete_fd_wrapper),
212215
))
213216
};
214217
Self::get_interface(service)
@@ -225,13 +228,18 @@ impl RpcSessionRef {
225228
}
226229
}
227230

228-
type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
229-
230231
unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
231-
let request_fd_ptr = param as *mut RequestFd;
232+
let request_fd_ptr = param as *mut Box<dyn FnMut() -> Option<RawFd>>;
232233
// SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
233234
// BinderFdFactory reference, with param being a properly aligned non-null pointer to an
234235
// initialized instance.
235236
let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() };
236237
request_fd().unwrap_or(-1)
237238
}
239+
240+
unsafe extern "C" fn param_delete_fd_wrapper(param: *mut c_void) {
241+
// SAFETY: This is only ever called by RpcPreconnectedClient, with param being the
242+
// pointer returned from Box::into_raw.
243+
let request_fd_box = unsafe { Box::from_raw(param as *mut Box<dyn FnMut() -> Option<RawFd>>) };
244+
drop(request_fd_box);
245+
}

libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ MODULE_LIBRARY_DEPS += \
3030
trusty/user/base/lib/libstdc++-trusty \
3131
trusty/user/base/lib/trusty-sys \
3232

33+
MODULE_SRCDEPS := $(LIBBINDER_DIR)/include_rpc_unstable/binder_rpc_unstable.hpp
34+
3335
MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
3436

3537
MODULE_BINDGEN_FLAGS += \

0 commit comments

Comments
 (0)