diff options
author | 2022-04-05 05:56:52 +0000 | |
---|---|---|
committer | 2022-08-16 21:57:42 +0000 | |
commit | a8fa78c5fdb652375440aa2b4b412e9e0bbc4c6d (patch) | |
tree | d9a1f54f7fcba9562925b34802edd786b0253b06 | |
parent | 44732cde9e19a005def5e110140963a5c2e2f5ae (diff) |
libbinder: add RpcTransportTipcAndroid
Adds a new Binder RPC transport in Android for Trusty IPC.
To enable code sharing between the transports, this moves
interruptableReadOrWrite into a new RpcTransportUtils.h header
that is used by RpcTransportRaw and RpcTransportTrusty.
Bug: 224644083
Test: trusty_binder_test
Change-Id: I47843560374049821cbfc36875a738083ffd5d75
-rw-r--r-- | libs/binder/Android.bp | 28 | ||||
-rw-r--r-- | libs/binder/RpcTransportTipcAndroid.cpp | 218 | ||||
-rw-r--r-- | libs/binder/RpcTrusty.cpp | 46 | ||||
-rw-r--r-- | libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h | 40 | ||||
-rw-r--r-- | libs/binder/include_trusty/binder/RpcTrusty.h | 25 |
5 files changed, 357 insertions, 0 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 441a4a8be4..fabf3ebdae 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -339,6 +339,34 @@ cc_library_shared { defaults: ["libbinder_tls_defaults"], } +cc_library_shared { + name: "libbinder_trusty", + vendor: true, + srcs: [ + "RpcTransportTipcAndroid.cpp", + "RpcTrusty.cpp", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libtrusty", + "libutils", + ], + static_libs: [ + "libbase", + ], + export_include_dirs: ["include_trusty"], + + // Most of Android doesn't need this library and shouldn't use it, + // so we restrict its visibility to the Trusty-specific packages. + visibility: [ + ":__subpackages__", + "//system/core/trusty:__subpackages__", + "//vendor:__subpackages__", + ], +} + // For testing cc_library_static { name: "libbinder_tls_static", diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp new file mode 100644 index 0000000000..79983f46d3 --- /dev/null +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "RpcTransportTipcAndroid" + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <log/log.h> +#include <poll.h> +#include <trusty/tipc.h> + +#include "FdTrigger.h" +#include "RpcState.h" +#include "RpcTransportUtils.h" + +using android::base::Error; +using android::base::Result; + +namespace android { + +namespace { + +// RpcTransport for writing Trusty IPC clients in Android. +class RpcTransportTipcAndroid : public RpcTransport { +public: + explicit RpcTransportTipcAndroid(android::base::unique_fd socket) + : mSocket(std::move(socket)) {} + + status_t pollRead() override { + if (mReadBufferPos < mReadBufferSize) { + // We have more data in the read buffer + return OK; + } + + // Trusty IPC device is not a socket, so MSG_PEEK is not available + pollfd pfd{.fd = mSocket.get(), .events = static_cast<int16_t>(POLLIN), .revents = 0}; + ssize_t ret = TEMP_FAILURE_RETRY(::poll(&pfd, 1, 0)); + if (ret < 0) { + int savedErrno = errno; + if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { + return WOULD_BLOCK; + } + + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); + return -savedErrno; + } + + if (pfd.revents & POLLNVAL) { + return BAD_VALUE; + } + if (pfd.revents & POLLERR) { + return DEAD_OBJECT; + } + if (pfd.revents & POLLHUP) { + return DEAD_OBJECT; + } + if (pfd.revents & POLLIN) { + return OK; + } + + return WOULD_BLOCK; + } + + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // TODO: send ancillaryFds. For now, we just abort if anyone tries + // to send any. + LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(), + "File descriptors are not supported on Trusty yet"); + return TEMP_FAILURE_RETRY(tipc_send(mSocket.get(), iovs, niovs, nullptr, 0)); + }; + return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, writeFn, "tipc_send", + POLLOUT, altPoll); + } + + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) + override { + auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // Fill the read buffer at most once per readFn call, then try to + // return as much of it as possible. If the input iovecs are spread + // across multiple messages that require multiple fillReadBuffer + // calls, we expect the caller to advance the iovecs past the first + // read and call readFn as many times as needed to get all the data + status_t ret = fillReadBuffer(); + if (ret != OK) { + return ret; + } + + ssize_t processSize = 0; + for (size_t i = 0; i < niovs && mReadBufferPos < mReadBufferSize; i++) { + auto& iov = iovs[i]; + size_t numBytes = std::min(iov.iov_len, mReadBufferSize - mReadBufferPos); + memcpy(iov.iov_base, mReadBuffer.get() + mReadBufferPos, numBytes); + mReadBufferPos += numBytes; + processSize += numBytes; + } + + return processSize; + }; + return interruptableReadOrWrite(mSocket.get(), fdTrigger, iovs, niovs, readFn, "read", + POLLIN, altPoll); + } + +private: + status_t fillReadBuffer() { + if (mReadBufferPos < mReadBufferSize) { + return OK; + } + + if (!mReadBuffer) { + // Guarantee at least kDefaultBufferSize bytes + mReadBufferCapacity = std::max(mReadBufferCapacity, kDefaultBufferSize); + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + } + + // Reset the size and position in case we have to exit with an error. + // After we read a message into the buffer, we update the size + // with the actual value. + mReadBufferPos = 0; + mReadBufferSize = 0; + + while (true) { + ssize_t processSize = + TEMP_FAILURE_RETRY(read(mSocket.get(), mReadBuffer.get(), mReadBufferCapacity)); + if (processSize == 0) { + return DEAD_OBJECT; + } else if (processSize < 0) { + int savedErrno = errno; + if (savedErrno == EMSGSIZE) { + // Buffer was too small, double it and retry + if (__builtin_mul_overflow(mReadBufferCapacity, 2, &mReadBufferCapacity)) { + return NO_MEMORY; + } + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + continue; + } else { + LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno)); + return -savedErrno; + } + } else { + mReadBufferSize = static_cast<size_t>(processSize); + return OK; + } + } + } + + base::unique_fd mSocket; + + // For now, we copy all the input data into a temporary buffer because + // we might get multiple interruptableReadFully calls per message, but + // the tipc device only allows one read call. We read every message into + // this temporary buffer, then return pieces of it from our method. + // + // The special transaction GET_MAX_THREADS takes 40 bytes, so the default + // size should start pretty high. + static constexpr size_t kDefaultBufferSize = 64; + std::unique_ptr<uint8_t[]> mReadBuffer; + size_t mReadBufferPos = 0; + size_t mReadBufferSize = 0; + size_t mReadBufferCapacity = 0; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcAndroid : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcAndroid>(std::move(fd)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +const char* RpcTransportCtxFactoryTipcAndroid::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcAndroid::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcAndroid>( + new RpcTransportCtxFactoryTipcAndroid()); +} + +} // namespace android diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp new file mode 100644 index 0000000000..ea49eef676 --- /dev/null +++ b/libs/binder/RpcTrusty.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "RpcTrusty" + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <trusty/tipc.h> + +namespace android { + +using android::base::unique_fd; + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port) { + auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make()); + auto request = [=] { + int tipcFd = tipc_connect(device, port); + if (tipcFd < 0) { + LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd; + return unique_fd(); + } + return unique_fd(tipcFd); + }; + if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { + LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str(); + return nullptr; + } + return session->getRootObject(); +} + +} // namespace android diff --git a/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h new file mode 100644 index 0000000000..4a4172a3a7 --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 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. + */ + +// Wraps the transport layer of RPC. Implementation uses Trusty IPC. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory for writing Trusty IPC clients in Android. +class RpcTransportCtxFactoryTipcAndroid : public RpcTransportCtxFactory { +public: + static std::unique_ptr<RpcTransportCtxFactory> make(); + + std::unique_ptr<RpcTransportCtx> newServerCtx() const override; + std::unique_ptr<RpcTransportCtx> newClientCtx() const override; + const char* toCString() const override; + +private: + RpcTransportCtxFactoryTipcAndroid() = default; +}; + +} // namespace android diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h new file mode 100644 index 0000000000..f124e0c824 --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTrusty.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include <binder/IBinder.h> + +namespace android { + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port); + +} // namespace android |