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

Commit 91538243 authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder: RPC prevent binder address collision

It's not clear that we're going to continue using such large addresses
(they are expensive, and we don't actually need addresses themselves to
be cryptographically unguessable - also since we are reading from
urandom, we're using a lot of entropy - ...), but just in case, clearing
out TODOs (and also in preparation of using RpcAddress for something
else, which we probably will continue using).

This prevents address collision by doing two things:
- create a bitspace for server vs client addresses (one bit in the
  address, in a newly defined header, determines the originating side
  of the connection for the address).
- instead of aborting when a duplicated address is created, try to
  create a new one.

As a side-effect, this also adds a header to binder RPC addresses.

Bug: 182939933
Test: binderRpcTest

Change-Id: I8ff0d29ca6df25b3f1d9662978fccbb3eb76c8ad
parent 7b8bc4c6
Loading
Loading
Loading
Loading
+24 −3
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ RpcAddress RpcAddress::zero() {
}

bool RpcAddress::isZero() const {
    RpcWireAddress ZERO{0};
    RpcWireAddress ZERO{.options = 0};
    return memcmp(mRawAddr.get(), &ZERO, sizeof(RpcWireAddress)) == 0;
}

@@ -51,13 +51,34 @@ static void ReadRandomBytes(uint8_t* buf, size_t len) {
    close(fd);
}

RpcAddress RpcAddress::unique() {
RpcAddress RpcAddress::random(bool forServer) {
    // The remainder of this header acts as reserved space for different kinds
    // of binder objects.
    uint64_t options = RPC_WIRE_ADDRESS_OPTION_CREATED;

    // servers and clients allocate addresses independently, so this bit can
    // tell you where an address originates
    if (forServer) options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;

    RpcAddress ret;
    ReadRandomBytes((uint8_t*)ret.mRawAddr.get(), sizeof(RpcWireAddress));
    RpcWireAddress* raw = ret.mRawAddr.get();

    raw->options = options;
    ReadRandomBytes(raw->address, sizeof(raw->address));

    LOG_RPC_DETAIL("Creating new address: %s", ret.toString().c_str());
    return ret;
}

bool RpcAddress::isForServer() const {
    return mRawAddr.get()->options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
}

bool RpcAddress::isRecognizedType() const {
    uint64_t allKnownOptions = RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER;
    return (mRawAddr.get()->options & ~allKnownOptions) == 0;
}

RpcAddress RpcAddress::fromRawEmbedded(const RpcWireAddress* raw) {
    RpcAddress addr;
    memcpy(addr.mRawAddr.get(), raw, sizeof(RpcWireAddress));
+43 −11
Original line number Diff line number Diff line
@@ -83,21 +83,45 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind
    }
    LOG_ALWAYS_FATAL_IF(isRpc, "RPC binder must have known address at this point");

    auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::unique(),
    bool forServer = session->server() != nullptr;

    for (size_t tries = 0; tries < 5; tries++) {
        auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::random(forServer),
                                                        BinderNode{
                                                                .binder = binder,
                                                                .timesSent = 1,
                                                                .sentRef = binder,
                                                        }});
    // TODO(b/182939933): better organization could avoid needing this log
    LOG_ALWAYS_FATAL_IF(!inserted);

        if (inserted) {
            *outAddress = it->first;
            return OK;
        }

        // well, we don't have visibility into the header here, but still
        static_assert(sizeof(RpcWireAddress) == 40, "this log needs updating");
        ALOGW("2**256 is 1e77. If you see this log, you probably have some entropy issue, or maybe "
              "you witness something incredible!");
    }

    ALOGE("Unable to create an address in order to send out %p", binder.get());
    return WOULD_BLOCK;
}

status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address,
                                    sp<IBinder>* out) {
    // ensure that: if we want to use addresses for something else in the future (for
    //   instance, allowing transitive binder sends), that we don't accidentally
    //   send those addresses to old server. Accidentally ignoring this in that
    //   case and considering the binder to be recognized could cause this
    //   process to accidentally proxy transactions for that binder. Of course,
    //   if we communicate with a binder, it could always be proxying
    //   information. However, we want to make sure that isn't done on accident
    //   by a client.
    if (!address.isRecognizedType()) {
        ALOGE("Address is of an unknown type, rejecting: %s", address.toString().c_str());
        return BAD_VALUE;
    }

    std::unique_lock<std::mutex> _l(mNodeMutex);
    if (mTerminated) return DEAD_OBJECT;

@@ -117,6 +141,14 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddr
        return OK;
    }

    // we don't know about this binder, so the other side of the connection
    // should have created it.
    if (address.isForServer() == !!session->server()) {
        ALOGE("Server received unrecognized address which we should own the creation of %s.",
              address.toString().c_str());
        return BAD_VALUE;
    }

    auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}});
    LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy");

+4 −0
Original line number Diff line number Diff line
@@ -89,7 +89,11 @@ struct RpcWireHeader {
    uint32_t reserved[2];
};

constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_CREATED = 1 << 0; // distinguish from '0' address
constexpr uint64_t RPC_WIRE_ADDRESS_OPTION_FOR_SERVER = 1 << 1;

struct RpcWireAddress {
    uint64_t options;
    uint8_t address[32];
};

+13 −2
Original line number Diff line number Diff line
@@ -46,9 +46,20 @@ public:
    bool isZero() const;

    /**
     * Create a new address which is unique
     * Create a new random address.
     */
    static RpcAddress unique();
    static RpcAddress random(bool forServer);

    /**
     * Whether this address was created with 'bool forServer' true
     */
    bool isForServer() const;

    /**
     * Whether this address is one that could be created with this version of
     * libbinder.
     */
    bool isRecognizedType() const;

    /**
     * Creates a new address as a copy of an embedded object.