Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a47dfdaf authored by David Brazdil's avatar David Brazdil
Browse files

rpc_binder: Specify CID for vsock RpcServer

Currently RpcServer only allows to start a server listening for
connections from VMHOST_CID_ANY. This is not always desirable as clients
may want to listen only for connections from specific CIDs, eg. only the
host or only local.

This can be partially solved by setting the bind CID of the virtual
socket. The server can bind to VMADDR_CID_LOCAL for local interface
only. VMADDR_CID_ANY same as its own CID will bind to the remote
interface and accept connections from all clients.

To this end, add a callback for filtering accepted connections in
RpcServer. This may already be possible with per-session root object.
However, the root object is selected very late, after initial
negotiation with the client. For both performance and safety, add the
callback immediately after accept() to reject the connection as early as
possible.

Bug: 245727626
Test: atest binderRpcTest
Change-Id: I5b3e6fd5119c77ef8c5c4fbbfead9892c5de1a07
parent a1cf02cf
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -70,11 +70,8 @@ status_t RpcServer::setupUnixDomainServer(const char* path) {
    return setupSocketServer(UnixSocketAddress(path));
}

status_t RpcServer::setupVsockServer(unsigned int port) {
    // realizing value w/ this type at compile time to avoid ubsan abort
    constexpr unsigned int kAnyCid = VMADDR_CID_ANY;

    return setupSocketServer(VsockSocketAddress(kAnyCid, port));
status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) {
    return setupSocketServer(VsockSocketAddress(bindCid, port));
}

status_t RpcServer::setupInetServer(const char* address, unsigned int port,
@@ -157,6 +154,12 @@ void RpcServer::setPerSessionRootObject(
    mRootObjectFactory = std::move(makeObject);
}

void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) {
    RpcMutexLockGuard _l(mLock);
    LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
    mConnectionFilter = std::move(filter);
}

sp<IBinder> RpcServer::getRootObject() {
    RpcMutexLockGuard _l(mLock);
    bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -242,13 +245,19 @@ void RpcServer::join() {
        if (mAcceptFn(*this, &clientSocket) != OK) {
            continue;
        }

        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());

        if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
                        &addrLen)) {
            ALOGE("Could not getpeername socket: %s", strerror(errno));
            continue;
        }

        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
        if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
            ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
            continue;
        }

        {
            RpcMutexLockGuard _l(mLock);
+13 −2
Original line number Diff line number Diff line
@@ -81,9 +81,9 @@ public:
    [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);

    /**
     * Creates an RPC server at the current port.
     * Creates an RPC server binding to the given CID at the given port.
     */
    [[nodiscard]] status_t setupVsockServer(unsigned int port);
    [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);

    /**
     * Creates an RPC server at the current port using IPv4.
@@ -170,6 +170,16 @@ public:
    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object);
    sp<IBinder> getRootObject();

    /**
     * Set optional filter of incoming connections based on the peer's address.
     *
     * Takes one argument: a callable that is invoked on each accept()-ed
     * connection and returns false if the connection should be dropped.
     * See the description of setPerSessionRootObject() for details about
     * the callable's arguments.
     */
    void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);

    /**
     * See RpcTransportCtx::getCertificate
     */
@@ -253,6 +263,7 @@ private:
    sp<IBinder> mRootObject;
    wp<IBinder> mRootObjectWeak;
    std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
    std::function<bool(const void*, size_t)> mConnectionFilter;
    std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
    std::unique_ptr<FdTrigger> mShutdownTrigger;
    RpcConditionVariable mShutdownCv;
+5 −1
Original line number Diff line number Diff line
@@ -25,9 +25,13 @@ struct AIBinder;
struct ARpcServer;

// Starts an RPC server on a given port and a given root IBinder object.
// The server will only accept connections from the given CID.
// Set `cid` to VMADDR_CID_ANY to accept connections from any client.
// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
                                              unsigned int port);

// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
// and a given root IBinder object.
+29 −7
Original line number Diff line number Diff line
@@ -51,21 +51,26 @@ static void freeRpcServerHandle(ARpcServer* handle) {
    ref->decStrong(ref);
}

static unsigned int cidFromStructAddr(const void* addr, size_t addrlen) {
    LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
    const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
    LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
    return vaddr->svm_cid;
}

extern "C" {

bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
                                  void* factoryContext, unsigned int port) {
    auto server = RpcServer::make();
    if (status_t status = server->setupVsockServer(port); status != OK) {
    if (status_t status = server->setupVsockServer(VMADDR_CID_ANY, port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock server with port " << port
                   << " error: " << statusToString(status).c_str();
        return false;
    }
    server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
        LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
        const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
        LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
        return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
        unsigned int cid = cidFromStructAddr(addr, addrlen);
        return AIBinder_toPlatformBinder(factory(cid, factoryContext));
    });

    server->join();
@@ -75,13 +80,30 @@ bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* c
    return true;
}

ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
    auto server = RpcServer::make();
    if (status_t status = server->setupVsockServer(port); status != OK) {

    unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
    if (cid == VMADDR_CID_LOCAL) {
        bindCid = VMADDR_CID_LOCAL; // bind to the local interface
        cid = VMADDR_CID_ANY;       // no need for a connection filter
    }

    if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
        LOG(ERROR) << "Failed to set up vsock server with port " << port
                   << " error: " << statusToString(status).c_str();
        return nullptr;
    }
    if (cid != VMADDR_CID_ANY) {
        server->setConnectionFilter([=](const void* addr, size_t addrlen) {
            unsigned int remoteCid = cidFromStructAddr(addr, addrlen);
            if (cid != remoteCid) {
                LOG(ERROR) << "Rejected vsock connection from CID " << remoteCid;
                return false;
            }
            return true;
        });
    }
    server->setRootObject(AIBinder_toPlatformBinder(service));
    return createRpcServerHandle(server);
}
+8 −3
Original line number Diff line number Diff line
@@ -41,14 +41,19 @@ unsafe impl Sync for RpcServer {}

impl RpcServer {
    /// Creates a binder RPC server, serving the supplied binder service implementation on the given
    /// vsock port.
    pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
    /// vsock port. Only connections from the given CID are accepted.
    ///
    // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
    // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
    pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
        let service = service.as_native_mut();

        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
        // Plus the binder objects are threadsafe.
        unsafe {
            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
            Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
                service, cid, port,
            ))
        }
    }

Loading