diff options
124 files changed, 5384 insertions, 1498 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 1c3a4f201c..a417493189 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -292,12 +292,9 @@ on late-init write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" -# Set up histogram triggers - # rss_stat_throttled (bucket size == 512KB) - chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + # allow creating event triggers chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger - write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" - write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a2491e503f..a60972b722 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -101,6 +101,7 @@ cc_defaults { "libhidlbase", "liblog", "libutils", + "libvintf", "libbinderdebug", "packagemanager_aidl-cpp", ], diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 0dab0e472e..6dea91bc2b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "dumpstate" +#define ATRACE_TAG ATRACE_TAG_ALWAYS #include <dirent.h> #include <errno.h> @@ -76,6 +77,7 @@ #include <cutils/native_handle.h> #include <cutils/properties.h> #include <cutils/sockets.h> +#include <cutils/trace.h> #include <debuggerd/client.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> @@ -88,6 +90,7 @@ #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> #include <utils/StrongPointer.h> +#include <vintf/VintfObject.h> #include "DumpstateInternal.h" #include "DumpstateService.h" #include "dumpstate.h" @@ -1396,6 +1399,23 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { } } +// Dump all of the files that make up the vendor interface. +// See the files listed in dumpFileList() for the latest list of files. +static void DumpVintf() { + const auto vintfFiles = android::vintf::details::dumpFileList(); + for (const auto vintfFile : vintfFiles) { + struct stat st; + if (stat(vintfFile.c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ds.AddDir(vintfFile, true /* recursive */); + } else { + ds.EnqueueAddZipEntryAndCleanupIfNeeded(ZIP_ROOT_DIR + vintfFile, + vintfFile); + } + } + } +} + static void DumpExternalFragmentationInfo() { struct stat st; if (stat("/proc/buddyinfo", &st) != 0) { @@ -1488,7 +1508,6 @@ static void DumpCheckins(int out_fd = STDOUT_FILENO) { dprintf(out_fd, "========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd); RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd); @@ -1621,6 +1640,8 @@ static Dumpstate::RunStatus dumpstate() { do_dmesg(); } + DumpVintf(); + RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); @@ -2086,7 +2107,7 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { int timeout_failures = 0; bool dalvik_found = false; - const std::set<int> hal_pids = get_interesting_hal_pids(); + const std::set<int> hal_pids = get_interesting_pids(); struct dirent* d; while ((d = readdir(proc.get()))) { @@ -3078,7 +3099,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); // Zip the (now complete) .tmp file within the internal directory. + ATRACE_BEGIN("FinalizeFile"); FinalizeFile(); + ATRACE_END(); // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; @@ -3389,6 +3412,9 @@ DurationReporter::DurationReporter(const std::string& title, bool logcat_only, b duration_fd_(duration_fd) { if (!title_.empty()) { started_ = Nanotime(); + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_BEGIN(title_.c_str(), 0); + } } } @@ -3403,6 +3429,9 @@ DurationReporter::~DurationReporter() { dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_END(title_.c_str(), 0); + } } } diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 49c1318945..f0c19b93ec 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -65,6 +65,7 @@ class ServiceManagerMock : public IServiceManager { const sp<LocalRegistrationCallback>&)); MOCK_METHOD2(unregisterForNotifications, status_t(const String16&, const sp<LocalRegistrationCallback>&)); + MOCK_METHOD0(getServiceDebugInfo, std::vector<ServiceDebugInfo>()); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index c7bea3f4a9..65b25a495b 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -1182,8 +1182,8 @@ static int wait_child(pid_t pid) { int wait_child_with_timeout(pid_t pid, int timeout_ms) { int pidfd = pidfd_open(pid, /*flags=*/0); if (pidfd < 0) { - PLOG(ERROR) << "pidfd_open failed for pid " << pid; - kill(pid, SIGKILL); + PLOG(ERROR) << "pidfd_open failed for pid " << pid + << ", waiting for child process without timeout"; return wait_child(pid); } diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index cbfbdc9223..af85666276 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -35,5 +35,6 @@ cc_library_static { ], export_include_dirs: [ "include", - ] + ], + min_sdk_version: "30", } diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 32922ca24c..3b753c6ca4 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -44,6 +44,7 @@ cc_binary { defaults: ["servicemanager_defaults"], init_rc: ["servicemanager.rc"], srcs: ["main.cpp"], + bootstrap: true, } cc_binary { diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 2fb9c2bc9a..1d458b736a 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -121,6 +121,8 @@ int main(int argc, char** argv) { const char* driver = argc == 2 ? argv[1] : "/dev/binder"; + LOG(INFO) << "Starting sm instance on " << driver; + sp<ProcessState> ps = ProcessState::initWithDriver(driver); ps->setThreadPoolMaxThreadCount(0); ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY); diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7f0324a4a8..22b9faaf7a 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -295,6 +295,8 @@ enum class InputDeviceConfigurationFileType : int32_t { /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. + * The optional suffix is appended to the end of the file name (before the + * extension). * * The device identifier is used to construct several default configuration file * names to try based on the device name, vendor, product, and version. @@ -302,8 +304,8 @@ enum class InputDeviceConfigurationFileType : int32_t { * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type); + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix = ""); /* * Gets the path of an input device configuration file, if one is available. diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index b2bd535cbf..1da78aa0c1 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -20,9 +20,8 @@ #include <android-base/result.h> #include <stdint.h> #include <utils/Errors.h> -#include <utils/KeyedVector.h> -#include <utils/RefBase.h> #include <utils/Tokenizer.h> +#include <set> #include <input/InputDevice.h> @@ -65,18 +64,18 @@ struct AxisInfo { */ class KeyLayoutMap { public: - static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename); - static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename, + const char* contents = nullptr); static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename, const char* contents); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const; - status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; - status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; + std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const; + std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const; + std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const; - status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + std::optional<AxisInfo> mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode); @@ -84,6 +83,8 @@ public: virtual ~KeyLayoutMap(); private: + static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + struct Key { int32_t keyCode; uint32_t flags; @@ -98,12 +99,13 @@ private: int32_t sensorDataIndex; }; - KeyedVector<int32_t, Key> mKeysByScanCode; - KeyedVector<int32_t, Key> mKeysByUsageCode; - KeyedVector<int32_t, AxisInfo> mAxes; - KeyedVector<int32_t, Led> mLedsByScanCode; - KeyedVector<int32_t, Led> mLedsByUsageCode; + std::unordered_map<int32_t, Key> mKeysByScanCode; + std::unordered_map<int32_t, Key> mKeysByUsageCode; + std::unordered_map<int32_t, AxisInfo> mAxes; + std::unordered_map<int32_t, Led> mLedsByScanCode; + std::unordered_map<int32_t, Led> mLedsByUsageCode; std::unordered_map<int32_t, Sensor> mSensorsByAbsCode; + std::set<std::string> mRequiredKernelConfigs; std::string mLoadFileName; KeyLayoutMap(); @@ -124,6 +126,7 @@ private: status_t parseAxis(); status_t parseLed(); status_t parseSensor(); + status_t parseRequiredKernelConfig(); }; }; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 08ad8c6e5a..9a3e15f1cd 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -61,9 +61,7 @@ private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name); - std::string getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type); + const std::string& name); }; /** diff --git a/include/input/OWNERS b/include/input/OWNERS new file mode 100644 index 0000000000..c88bfe97ca --- /dev/null +++ b/include/input/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/INPUT_OWNERS diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt index 7584ca3f53..f9f042ef5d 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -1,17 +1,17 @@ LIBADBD_AUTH { global: - adbd_auth_new; # apex introduced=30 - adbd_auth_delete; # apex introduced=30 - adbd_auth_run; # apex introduced=30 - adbd_auth_get_public_keys; #apex introduced=30 - adbd_auth_notify_auth; # apex introduced=30 - adbd_auth_notify_disconnect; # apex introduced=30 - adbd_auth_prompt_user; # apex introduced=30 - adbd_auth_prompt_user_with_id; # apex introduced=30 - adbd_auth_tls_device_connected; # apex introduced=30 - adbd_auth_tls_device_disconnected; # apex introduced=30 - adbd_auth_get_max_version; # apex introduced=30 - adbd_auth_supports_feature; # apex introduced=30 + adbd_auth_new; # systemapi introduced=30 + adbd_auth_delete; # systemapi introduced=30 + adbd_auth_run; # systemapi introduced=30 + adbd_auth_get_public_keys; # systemapi introduced=30 + adbd_auth_notify_auth; # systemapi introduced=30 + adbd_auth_notify_disconnect; # systemapi introduced=30 + adbd_auth_prompt_user; # systemapi introduced=30 + adbd_auth_prompt_user_with_id; # systemapi introduced=30 + adbd_auth_tls_device_connected; # systemapi introduced=30 + adbd_auth_tls_device_disconnected; # systemapi introduced=30 + adbd_auth_get_max_version; # systemapi introduced=30 + adbd_auth_supports_feature; # systemapi introduced=30 local: *; }; diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 21d98e0a47..858600e199 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -60,27 +60,19 @@ cc_library_headers { // // Currently, these are only on system android (not vendor, not host) // TODO(b/183654927) - move these into separate libraries -libbinder_device_interface_sources = [ - "IPermissionController.cpp", - "PermissionCache.cpp", - "PermissionController.cpp", -] -cc_library { - name: "libbinder", - - version_script: "libbinder.map", +filegroup { + name: "libbinder_device_interface_sources", + srcs: [ + "IPermissionController.cpp", + "PermissionCache.cpp", + "PermissionController.cpp", + ], +} - // for vndbinder - vendor_available: true, - vndk: { - enabled: true, - }, - recovery_available: true, - double_loadable: true, +cc_defaults { + name: "libbinder_defaults", host_supported: true, - // TODO(b/153609531): remove when no longer needed. - native_bridge_supported: true, // TODO(b/31559095): get headers from bionic on host include_dirs: [ @@ -88,72 +80,32 @@ cc_library { "bionic/libc/kernel/uapi/", ], - // libbinder does not offer a stable wire protocol. - // if a second copy of it is installed, then it may break after security - // or dessert updates. Instead, apex users should use libbinder_ndk. - apex_available: [ - "//apex_available:platform", - ], - srcs: [ "Binder.cpp", "BpBinder.cpp", - "BufferedTextOutput.cpp", "Debug.cpp", "FdTrigger.cpp", "IInterface.cpp", - "IMemory.cpp", - "IPCThreadState.cpp", "IResultReceiver.cpp", - "IServiceManager.cpp", - "IShellCallback.cpp", - "LazyServiceRegistrar.cpp", - "MemoryBase.cpp", - "MemoryDealer.cpp", - "MemoryHeapBase.cpp", + "OS.cpp", "Parcel.cpp", - "ParcelableHolder.cpp", "ParcelFileDescriptor.cpp", - "PersistableBundle.cpp", - "ProcessState.cpp", "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", "RpcTransportRaw.cpp", - "Static.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", "Utils.cpp", - ":libbinder_aidl", ], target: { - android: { - srcs: libbinder_device_interface_sources, - - // NOT static to keep the wire protocol unfrozen - static: { - enabled: false, - }, - }, - vendor: { - exclude_srcs: libbinder_device_interface_sources, - }, - darwin: { - enabled: false, - }, host: { srcs: [ - "ServiceManagerHost.cpp", "UtilsHost.cpp", ], }, - recovery: { - exclude_header_libs: [ - "libandroid_runtime_vm_headers", - ], - }, }, aidl: { @@ -161,7 +113,6 @@ cc_library { }, cflags: [ - "-Wall", "-Wextra", "-Wextra-semi", "-Werror", @@ -216,6 +167,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "google-*", @@ -223,10 +175,126 @@ cc_library { "performance*", "portability*", ], +} + +cc_defaults { + name: "libbinder_kernel_defaults", + srcs: [ + "BufferedTextOutput.cpp", + "IPCThreadState.cpp", + "IServiceManager.cpp", + "ProcessState.cpp", + "Static.cpp", + ":libbinder_aidl", + ":libbinder_device_interface_sources", + ], + target: { + vendor: { + exclude_srcs: [ + ":libbinder_device_interface_sources", + ], + }, + host: { + srcs: [ + "ServiceManagerHost.cpp", + ], + }, + }, + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], +} + +cc_library { + name: "libbinder", + defaults: [ + "libbinder_defaults", + "libbinder_kernel_defaults", + ], + + version_script: "libbinder.map", + + // for vndbinder + vendor_available: true, + vndk: { + enabled: true, + }, + recovery_available: true, + double_loadable: true, + // TODO(b/153609531): remove when no longer needed. + native_bridge_supported: true, + + // libbinder does not offer a stable wire protocol. + // if a second copy of it is installed, then it may break after security + // or dessert updates. Instead, apex users should use libbinder_ndk. + apex_available: [ + "//apex_available:platform", + ], + + srcs: [ + "IMemory.cpp", + "IShellCallback.cpp", + "LazyServiceRegistrar.cpp", + "MemoryBase.cpp", + "MemoryDealer.cpp", + "MemoryHeapBase.cpp", + "ParcelableHolder.cpp", + "PersistableBundle.cpp", + ], + + target: { + android: { + // NOT static to keep the wire protocol unfrozen + static: { + enabled: false, + }, + }, + darwin: { + enabled: false, + }, + recovery: { + exclude_header_libs: [ + "libandroid_runtime_vm_headers", + ], + }, + }, afdo: true, } +cc_library_static { + name: "libbinder_rpc_no_kernel", + defaults: ["libbinder_defaults"], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded", + defaults: [ + "libbinder_defaults", + "libbinder_kernel_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded_no_kernel", + defaults: ["libbinder_defaults"], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + cc_defaults { name: "libbinder_tls_shared_deps", shared_libs: [ @@ -365,6 +433,7 @@ filegroup { cc_library { name: "libbatterystats_aidl", + host_supported: true, srcs: [ "IBatteryStats.cpp", ], @@ -377,6 +446,7 @@ cc_library { cc_library { name: "libprocessinfoservice_aidl", + host_supported: true, srcs: [ "IProcessInfoService.cpp", "ProcessInfoService.cpp", diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 6a12e65fc0..532bacbdfe 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -32,9 +32,13 @@ #include <utils/misc.h> #include <inttypes.h> -#include <linux/sched.h> #include <stdio.h> +#ifdef __linux__ +#include <linux/sched.h> +#endif + +#include "BuildFlags.h" #include "RpcState.h" namespace android { @@ -161,6 +165,10 @@ status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, ALOGW("setRpcClientDebug disallowed because RPC is not enabled"); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } BBinder* local = this->localBinder(); if (local != nullptr) { @@ -234,11 +242,13 @@ class BBinder::Extras { public: // unlocked objects - bool mRequestingSid = false; - bool mInheritRt = false; sp<IBinder> mExtension; +#ifdef __linux__ int mPolicy = SCHED_NORMAL; int mPriority = 0; +#endif + bool mRequestingSid = false; + bool mInheritRt = false; // for below objects Mutex mLock; @@ -407,6 +417,7 @@ sp<IBinder> BBinder::getExtension() { return e->mExtension; } +#ifdef __linux__ void BBinder::setMinSchedulerPolicy(int policy, int priority) { LOG_ALWAYS_FATAL_IF(mParceled, "setMinSchedulerPolicy() should not be called after a binder object " @@ -451,6 +462,7 @@ int BBinder::getMinSchedulerPriority() { if (e == nullptr) return 0; return e->mPriority; } +#endif // __linux__ bool BBinder::isInheritRt() { Extras* e = mExtras.load(std::memory_order_acquire); @@ -478,7 +490,12 @@ void BBinder::setInheritRt(bool inheritRt) { } pid_t BBinder::getDebugPid() { +#ifdef __linux__ return getpid(); +#else + // TODO: handle other OSes + return 0; +#endif // __linux__ } void BBinder::setExtension(const sp<IBinder>& extension) { @@ -503,6 +520,10 @@ status_t BBinder::setRpcClientDebug(const Parcel& data) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid != AID_ROOT) { ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid); @@ -528,6 +549,10 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } const int socketFdForPrint = socketFd.get(); LOG_RPC_DETAIL("%s(fd=%d)", __PRETTY_FUNCTION__, socketFdForPrint); @@ -623,13 +648,14 @@ status_t BBinder::onTransact( for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } - sp<IShellCallback> shellCallback = IShellCallback::asInterface( - data.readStrongBinder()); + sp<IBinder> shellCallbackBinder = data.readStrongBinder(); sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface( data.readStrongBinder()); // XXX can't add virtuals until binaries are updated. - //return shellCommand(in, out, err, args, resultReceiver); + // sp<IShellCallback> shellCallback = IShellCallback::asInterface( + // shellCallbackBinder); + // return shellCommand(in, out, err, args, resultReceiver); (void)in; (void)out; (void)err; diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 1eb2ffd22d..b6d35ef3ef 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -28,6 +28,8 @@ #include <stdio.h> +#include "BuildFlags.h" + //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) @@ -115,6 +117,11 @@ void BpBinder::ObjectManager::kill() // --------------------------------------------------------------------------- sp<BpBinder> BpBinder::create(int32_t handle) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return nullptr; + } + int32_t trackedUid = -1; if (sCountByUidEnabled) { trackedUid = IPCThreadState::self()->getCallingUid(); @@ -177,6 +184,11 @@ BpBinder::BpBinder(Handle&& handle) } BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + mTrackedUid = trackedUid; ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle()); @@ -303,6 +315,11 @@ status_t BpBinder::transact( status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply, flags); } else { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } + status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { @@ -326,7 +343,24 @@ status_t BpBinder::transact( status_t BpBinder::linkToDeath( const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (isRpcBinder()) { + if (rpcSession()->getMaxIncomingThreads() < 1) { + LOG_ALWAYS_FATAL("Cannot register a DeathRecipient without any incoming connections."); + return INVALID_OPERATION; + } + } else if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } else { + if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) { + ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming " + "transactions. See ProcessState::startThreadPool and " + "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the " + "binder " + "threadpool before other initialization steps.", + String8(getInterfaceDescriptor()).c_str()); + } + } Obituary ob; ob.recipient = recipient; @@ -346,10 +380,14 @@ status_t BpBinder::linkToDeath( return NO_MEMORY; } ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle()); - getWeakRefs()->incWeak(this); - IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } } ssize_t res = mObituaries->add(ob); return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; @@ -364,7 +402,10 @@ status_t BpBinder::unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, wp<DeathRecipient>* outRecipient) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } AutoMutex _l(mLock); @@ -384,9 +425,13 @@ status_t BpBinder::unlinkToDeath( mObituaries->removeAt(i); if (mObituaries->size() == 0) { ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } delete mObituaries; mObituaries = nullptr; } @@ -399,7 +444,10 @@ status_t BpBinder::unlinkToDeath( void BpBinder::sendObituary() { - LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder."); + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(), mObitsSent ? "true" : "false"); @@ -411,9 +459,13 @@ void BpBinder::sendObituary() Vector<Obituary>* obits = mObituaries; if(obits != nullptr) { ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } mObituaries = nullptr; } mObitsSent = 1; @@ -469,12 +521,16 @@ BpBinder* BpBinder::remoteBinder() return this; } -BpBinder::~BpBinder() -{ - ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); - +BpBinder::~BpBinder() { if (CC_UNLIKELY(isRpcBinder())) return; + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); + IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { @@ -505,21 +561,31 @@ BpBinder::~BpBinder() } } -void BpBinder::onFirstRef() -{ - ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onFirstRef() { if (CC_UNLIKELY(isRpcBinder())) return; + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->incStrongHandle(binderHandle(), this); } -void BpBinder::onLastStrongRef(const void* /*id*/) -{ - ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onLastStrongRef(const void* /*id*/) { if (CC_UNLIKELY(isRpcBinder())) { (void)rpcSession()->sendDecStrong(this); return; } + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); IF_ALOGV() { printRefs(); } @@ -552,6 +618,11 @@ bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) // RPC binder doesn't currently support inc from weak binders if (CC_UNLIKELY(isRpcBinder())) return false; + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return false; + } + ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false; diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h new file mode 100644 index 0000000000..3e9d1c2b4a --- /dev/null +++ b/libs/binder/BuildFlags.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +constexpr bool kEnableRpcThreads = false; +#else +constexpr bool kEnableRpcThreads = true; +#endif + +#ifdef BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = true; +#else // BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = false; +#endif // BINDER_WITH_KERNEL_IPC + +} // namespace android diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index e4ac4b49a4..c6e4fb378d 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -15,6 +15,7 @@ */ #include "Debug.h" +#include "BuildFlags.h" #include <binder/ProcessState.h> @@ -301,6 +302,11 @@ void printHexData(int32_t indent, const void *buf, size_t length, } ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return 0; + } + sp<ProcessState> proc = ProcessState::selfOrNull(); if (proc.get() == nullptr) { return 0; diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 5e22593f69..d123fd1f2b 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -28,25 +28,45 @@ namespace android { std::unique_ptr<FdTrigger> FdTrigger::make() { auto ret = std::make_unique<FdTrigger>(); +#ifndef BINDER_RPC_SINGLE_THREADED if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) { ALOGE("Could not create pipe %s", strerror(errno)); return nullptr; } +#endif return ret; } void FdTrigger::trigger() { +#ifdef BINDER_RPC_SINGLE_THREADED + mTriggered = true; +#else mWrite.reset(); +#endif } bool FdTrigger::isTriggered() { +#ifdef BINDER_RPC_SINGLE_THREADED + return mTriggered; +#else return mWrite == -1; +#endif } status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { +#ifdef BINDER_RPC_SINGLE_THREADED + if (mTriggered) { + return DEAD_OBJECT; + } +#endif + LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get()); - pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, - {.fd = mRead.get(), .events = 0, .revents = 0}}; + pollfd pfd[]{ + {.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, +#ifndef BINDER_RPC_SINGLE_THREADED + {.fd = mRead.get(), .events = 0, .revents = 0}, +#endif + }; int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); if (ret < 0) { return -errno; @@ -55,6 +75,7 @@ status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { // At least one FD has events. Check them. +#ifndef BINDER_RPC_SINGLE_THREADED // Detect explicit trigger(): DEAD_OBJECT if (pfd[1].revents & POLLHUP) { return DEAD_OBJECT; @@ -68,6 +89,7 @@ status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { // pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are // a subset of event | POLLHUP | POLLERR | POLLNVAL. +#endif // POLLNVAL: invalid FD number, e.g. not opened. if (pfd[0].revents & POLLNVAL) { diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index a545d6cbea..5c7102e22e 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -55,7 +55,11 @@ public: [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event); private: +#ifdef BINDER_RPC_SINGLE_THREADED + bool mTriggered = false; +#else base::unique_fd mWrite; base::unique_fd mRead; +#endif }; } // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index d53621946a..b50cfb3d19 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -972,18 +972,15 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) freeBuffer); } else { err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer); - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); } } else { - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); continue; } } @@ -1473,17 +1470,13 @@ void IPCThreadState::logExtendedError() { ee.id, ee.command, ee.param); } -void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, - size_t /*dataSize*/, - const binder_size_t* /*objects*/, - size_t /*objectsSize*/) -{ +void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/, + const binder_size_t* /*objects*/, size_t /*objectsSize*/) { //ALOGI("Freeing parcel %p", &parcel); IF_LOG_COMMANDS() { alog << "Writing BC_FREE_BUFFER for " << data << endl; } ALOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != nullptr) parcel->closeFileDescriptors(); IPCThreadState* state = self(); state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writePointer((uintptr_t)data); diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index ea2f8d2274..fd47783acd 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -99,6 +99,8 @@ public: status_t unregisterForNotifications(const String16& service, const sp<AidlRegistrationCallback>& cb) override; + + std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; // for legacy ABI const String16& getInterfaceDescriptor() const override { return mTheRealServiceManager->getInterfaceDescriptor(); @@ -165,7 +167,7 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) { } } -#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) +#if !defined(__ANDROID_VNDK__) // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) @@ -543,6 +545,23 @@ status_t ServiceManagerShim::unregisterForNotifications(const String16& name, return OK; } +std::vector<IServiceManager::ServiceDebugInfo> ServiceManagerShim::getServiceDebugInfo() { + std::vector<os::ServiceDebugInfo> serviceDebugInfos; + std::vector<IServiceManager::ServiceDebugInfo> ret; + if (Status status = mTheRealServiceManager->getServiceDebugInfo(&serviceDebugInfos); + !status.isOk()) { + ALOGW("%s Failed to get ServiceDebugInfo", __FUNCTION__); + return ret; + } + for (const auto& serviceDebugInfo : serviceDebugInfos) { + IServiceManager::ServiceDebugInfo retInfo; + retInfo.pid = serviceDebugInfo.debugPid; + retInfo.name = serviceDebugInfo.name; + ret.emplace_back(retInfo); + } + return ret; +} + #ifndef __ANDROID__ // ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp new file mode 100644 index 0000000000..6eb72726f5 --- /dev/null +++ b/libs/binder/OS.cpp @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#include "OS.h" + +#include <android-base/file.h> +#include <string.h> + +using android::base::ErrnoError; +using android::base::Result; + +namespace android { + +Result<void> setNonBlocking(android::base::borrowed_fd fd) { + int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); + if (flags == -1) { + return ErrnoError() << "Could not get flags for fd"; + } + if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { + return ErrnoError() << "Could not set non-blocking flag for fd"; + } + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { + int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ret == -1) { + return -errno; + } + + base::unique_fd fd(ret); + if (!base::ReadFully(fd, data, size)) { + return -errno; + } + return OK; +} + +} // namespace android diff --git a/libs/binder/OS.h b/libs/binder/OS.h new file mode 100644 index 0000000000..e802e9cede --- /dev/null +++ b/libs/binder/OS.h @@ -0,0 +1,31 @@ +/* + * 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 <stddef.h> +#include <cstdint> + +#include <android-base/result.h> +#include <android-base/unique_fd.h> +#include <utils/Errors.h> + +namespace android { + +android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); + +status_t getRandomBytes(uint8_t* data, size_t size); + +} // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 038ce381e2..8b5d11800f 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -20,15 +20,14 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> -#include <linux/sched.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> -#include <sys/resource.h> #include <unistd.h> #include <binder/Binder.h> @@ -40,6 +39,7 @@ #include <binder/Status.h> #include <binder/TextOutput.h> +#include <android-base/scopeguard.h> #include <cutils/ashmem.h> #include <cutils/compiler.h> #include <utils/Flattenable.h> @@ -51,12 +51,27 @@ #include "RpcState.h" #include "Static.h" #include "Utils.h" + +// A lot of code in this file uses definitions from the +// Linux kernel header for Binder <linux/android/binder.h> +// which is included indirectly via "binder_module.h". +// Non-Linux OSes do not have that header, so libbinder should be +// built for those targets without kernel binder support, i.e., +// without BINDER_WITH_KERNEL_IPC. For this reason, all code in this +// file that depends on kernel binder, including the header itself, +// is conditional on BINDER_WITH_KERNEL_IPC. +#ifdef BINDER_WITH_KERNEL_IPC +#include <linux/sched.h> #include "binder_module.h" +#else // BINDER_WITH_KERNEL_IPC +// Needed by {read,write}Pointer +typedef uintptr_t binder_uintptr_t; +#endif // BINDER_WITH_KERNEL_IPC #define LOG_REFS(...) -//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) -//#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) // --------------------------------------------------------------------------- @@ -87,7 +102,8 @@ static_assert(sizeof(Parcel) == 60); static std::atomic<size_t> gParcelGlobalAllocCount; static std::atomic<size_t> gParcelGlobalAllocSize; -static size_t gMaxFds = 0; +// Maximum number of file descriptors per Parcel. +constexpr size_t kMaxFds = 1024; // Maximum size of a blob to transfer in-place. static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; @@ -98,6 +114,7 @@ enum { BLOB_ASHMEM_MUTABLE = 2, }; +#ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { switch (obj.hdr.type) { @@ -150,6 +167,11 @@ static void release_object(const sp<ProcessState>& proc, const flat_binder_objec ALOGE("Invalid object type 0x%08x", obj.hdr.type); } +#endif // BINDER_WITH_KERNEL_IPC + +static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) { + return std::visit([](const auto& fd) { return fd.get(); }, v); +} Parcel::RpcFields::RpcFields(const sp<RpcSession>& session) : mSession(session) { LOG_ALWAYS_FATAL_IF(mSession == nullptr); @@ -177,9 +199,11 @@ status_t Parcel::finishUnflattenBinder( return OK; } +#ifdef BINDER_WITH_KERNEL_IPC static constexpr inline int schedPolicyMask(int policy, int priority) { return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT); } +#endif // BINDER_WITH_KERNEL_IPC status_t Parcel::flattenBinder(const sp<IBinder>& binder) { BBinder* local = nullptr; @@ -204,6 +228,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { return finishFlattenBinder(binder); } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; int schedBits = 0; @@ -260,6 +285,10 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { if (status != OK) return status; return finishFlattenBinder(binder); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::unflattenBinder(sp<IBinder>* out) const @@ -289,6 +318,7 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const return finishUnflattenBinder(binder, out); } +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(false); if (flat) { @@ -306,6 +336,10 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const } } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // --------------------------------------------------------------------------- @@ -470,6 +504,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { err = NO_ERROR; if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC auto* otherKernelFields = parcel->maybeKernelFields(); LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr); @@ -529,6 +564,67 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } } } +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else { + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + auto* otherRpcFields = parcel->maybeRpcFields(); + if (otherRpcFields == nullptr) { + return BAD_TYPE; + } + if (rpcFields->mSession != otherRpcFields->mSession) { + return BAD_TYPE; + } + + const size_t savedDataPos = mDataPos; + base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + + rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); + if (otherRpcFields->mFds != nullptr) { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + rpcFields->mFds->reserve(otherRpcFields->mFds->size()); + } + for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) { + const binder_size_t objPos = otherRpcFields->mObjectPositions[i]; + if (offset <= objPos && objPos < offset + len) { + size_t newDataPos = objPos - offset + startPos; + rpcFields->mObjectPositions.push_back(newDataPos); + + mDataPos = newDataPos; + int32_t objectType; + if (status_t status = readInt32(&objectType); status != OK) { + return status; + } + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + continue; + } + + if (!mAllowFds) { + return FDS_NOT_ALLOWED; + } + + // Read FD, duplicate, and add to list. + int32_t fdIndex; + if (status_t status = readInt32(&fdIndex); status != OK) { + return status; + } + const auto& oldFd = otherRpcFields->mFds->at(fdIndex); + // To match kernel binder behavior, we always dup, even if the + // FD was unowned in the source parcel. + rpcFields->mFds->emplace_back( + base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0))); + // Fixup the index in the data. + mDataPos = newDataPos + 4; + if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) { + return status; + } + } + } } return err; @@ -583,7 +679,7 @@ void Parcel::restoreAllowFds(bool lastValue) bool Parcel::hasFileDescriptors() const { if (const auto* rpcFields = maybeRpcFields()) { - return false; + return rpcFields->mFds != nullptr && !rpcFields->mFds->empty(); } auto* kernelFields = maybeKernelFields(); if (!kernelFields->mFdsKnown) { @@ -595,6 +691,7 @@ bool Parcel::hasFileDescriptors() const std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { std::vector<sp<IBinder>> ret; +#ifdef BINDER_WITH_KERNEL_IPC const auto* kernelFields = maybeKernelFields(); if (kernelFields == nullptr) { return ret; @@ -614,40 +711,43 @@ std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { } setDataPosition(initPosition); +#endif // BINDER_WITH_KERNEL_IPC + return ret; } std::vector<int> Parcel::debugReadAllFileDescriptors() const { std::vector<int> ret; - const auto* kernelFields = maybeKernelFields(); - if (kernelFields == nullptr) { - return ret; - } - - size_t initPosition = dataPosition(); - for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { - binder_size_t offset = kernelFields->mObjects[i]; - const flat_binder_object* flat = - reinterpret_cast<const flat_binder_object*>(mData + offset); - if (flat->hdr.type != BINDER_TYPE_FD) continue; - - setDataPosition(offset); - - int fd = readFileDescriptor(); - LOG_ALWAYS_FATAL_IF(fd == -1); - ret.push_back(fd); + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t initPosition = dataPosition(); + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; + + setDataPosition(offset); + + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + setDataPosition(initPosition); +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif + } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { + for (const auto& fd : *rpcFields->mFds) { + ret.push_back(toRawFd(fd)); + } } - setDataPosition(initPosition); return ret; } status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const { - const auto* kernelFields = maybeKernelFields(); - if (kernelFields == nullptr) { - return BAD_TYPE; - } if (len > INT32_MAX || offset > INT32_MAX) { // Don't accept size_t values which may have come from an inadvertent conversion from a // negative int. @@ -658,20 +758,38 @@ status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* resu return BAD_VALUE; } *result = false; - for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { - size_t pos = kernelFields->mObjects[i]; - if (pos < offset) continue; - if (pos + sizeof(flat_binder_object) > offset + len) { - if (kernelFields->mObjectsSorted) { + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + size_t pos = kernelFields->mObjects[i]; + if (pos < offset) continue; + if (pos + sizeof(flat_binder_object) > offset + len) { + if (kernelFields->mObjectsSorted) { + break; + } else { + continue; + } + } + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + pos); + if (flat->hdr.type == BINDER_TYPE_FD) { + *result = true; break; - } else { - continue; } } - const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos); - if (flat->hdr.type == BINDER_TYPE_FD) { - *result = true; - break; +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else if (const auto* rpcFields = maybeRpcFields()) { + for (uint32_t pos : rpcFields->mObjectPositions) { + if (offset <= pos && pos < limit) { + const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + *result = true; + break; + } + } } } return NO_ERROR; @@ -715,6 +833,7 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { } } +#ifdef BINDER_WITH_KERNEL_IPC #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #elif defined(__ANDROID_RECOVERY__) @@ -722,6 +841,7 @@ constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif +#endif // BINDER_WITH_KERNEL_IPC // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) @@ -731,12 +851,17 @@ status_t Parcel::writeInterfaceToken(const String16& interface) status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC const IPCThreadState* threadState = IPCThreadState::self(); writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); updateWorkSourceRequestHeaderPosition(); writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); writeInt32(kHeader); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // currently the interface identification token is just its name as a string @@ -793,6 +918,7 @@ bool Parcel::enforceInterface(const char16_t* interface, IPCThreadState* threadState) const { if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { @@ -818,6 +944,11 @@ bool Parcel::enforceInterface(const char16_t* interface, header); return false; } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)threadState; + return false; +#endif // BINDER_WITH_KERNEL_IPC } // Interface descriptor. @@ -1292,13 +1423,43 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) return err; } -status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) -{ - if (isForRpc()) { - ALOGE("Cannot write file descriptor to remote binder."); - return BAD_TYPE; +status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { + if (auto* rpcFields = maybeRpcFields()) { + std::variant<base::unique_fd, base::borrowed_fd> fdVariant; + if (takeOwnership) { + fdVariant = base::unique_fd(fd); + } else { + fdVariant = base::borrowed_fd(fd); + } + if (!mAllowFds) { + return FDS_NOT_ALLOWED; + } + switch (rpcFields->mSession->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: { + return FDS_NOT_ALLOWED; + } + case RpcSession::FileDescriptorTransportMode::UNIX: { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + size_t dataPos = mDataPos; + if (dataPos > UINT32_MAX) { + return NO_MEMORY; + } + if (status_t err = writeInt32(RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR); err != OK) { + return err; + } + if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) { + return err; + } + rpcFields->mObjectPositions.push_back(dataPos); + rpcFields->mFds->push_back(std::move(fdVariant)); + return OK; + } + } } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; obj.hdr.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; @@ -1306,6 +1467,12 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) obj.handle = fd; obj.cookie = takeOwnership ? 1 : 0; return writeObject(obj, true); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)fd; + (void)takeOwnership; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeDupFileDescriptor(int fd) @@ -1416,7 +1583,7 @@ status_t Parcel::write(const FlattenableHelperInterface& val) const size_t len = val.getFlattenedSize(); const size_t fd_count = val.getFdCount(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -1459,6 +1626,7 @@ status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) auto* kernelFields = maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kernelFields == nullptr, "Can't write flat_binder_object to RPC Parcel"); +#ifdef BINDER_WITH_KERNEL_IPC const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity; if (enoughData && enoughObjects) { @@ -1501,6 +1669,12 @@ restart_write: } goto restart_write; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)val; + (void)nullMetaData; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeNoException() @@ -1518,6 +1692,7 @@ status_t Parcel::validateReadData(size_t upperBound) const return OK; } +#ifdef BINDER_WITH_KERNEL_IPC // Don't allow non-object reads on object data if (kernelFields->mObjectsSorted || kernelFields->mObjectsSize <= 1) { data_sorted: @@ -1570,6 +1745,10 @@ data_unsorted: kernelFields->mNextObjectHint = 0; kernelFields->mObjectsSorted = true; goto data_sorted; +#else // BINDER_WITH_KERNEL_IPC + (void)upperBound; + return NO_ERROR; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::read(void* outData, size_t len) const @@ -2037,8 +2216,32 @@ native_handle* Parcel::readNativeHandle() const return h; } -int Parcel::readFileDescriptor() const -{ +int Parcel::readFileDescriptor() const { + if (const auto* rpcFields = maybeRpcFields()) { + if (!std::binary_search(rpcFields->mObjectPositions.begin(), + rpcFields->mObjectPositions.end(), mDataPos)) { + ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the " + "object list", + this, mDataPos); + return BAD_TYPE; + } + + int32_t objectType = readInt32(); + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + return BAD_TYPE; + } + + int32_t fdIndex = readInt32(); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + return toRawFd(rpcFields->mFds->at(fdIndex)); + } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(true); if (flat && flat->hdr.type == BINDER_TYPE_FD) { @@ -2046,10 +2249,13 @@ int Parcel::readFileDescriptor() const } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } -int Parcel::readParcelFileDescriptor() const -{ +int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { @@ -2158,7 +2364,7 @@ status_t Parcel::read(FlattenableHelperInterface& val) const const size_t len = this->readInt32(); const size_t fd_count = this->readInt32(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -2202,6 +2408,8 @@ status_t Parcel::read(FlattenableHelperInterface& val) const return err; } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* Parcel::readObject(bool nullMetaData) const { const auto* kernelFields = maybeKernelFields(); @@ -2267,24 +2475,29 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const } return nullptr; } +#endif // BINDER_WITH_KERNEL_IPC void Parcel::closeFileDescriptors() { - auto* kernelFields = maybeKernelFields(); - if (kernelFields == nullptr) { - return; - } - size_t i = kernelFields->mObjectsSize; - if (i > 0) { - //ALOGI("Closing file descriptors for %zu objects...", i); - } - while (i > 0) { - i--; - const flat_binder_object* flat = - reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); - if (flat->hdr.type == BINDER_TYPE_FD) { - //ALOGI("Closing fd: %ld", flat->handle); - close(flat->handle); + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; + if (i > 0) { + // ALOGI("Closing file descriptors for %zu objects...", i); + } + while (i > 0) { + i--; + const flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); + if (flat->hdr.type == BINDER_TYPE_FD) { + // ALOGI("Closing fd: %ld", flat->handle); + close(flat->handle); + } } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif // BINDER_WITH_KERNEL_IPC + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mFds.reset(); } } @@ -2330,6 +2543,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsCount; mOwner = relFunc; +#ifdef BINDER_WITH_KERNEL_IPC binder_size_t minOffset = 0; for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { binder_size_t offset = kernelFields->mObjects[i]; @@ -2360,21 +2574,58 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin minOffset = offset + sizeof(flat_binder_object); } scanForFds(); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL_IF(objectsCount != 0, + "Non-zero objects count passed to Parcel with kernel driver disabled"); +#endif // BINDER_WITH_KERNEL_IPC } -void Parcel::rpcSetDataReference(const sp<RpcSession>& session, const uint8_t* data, - size_t dataSize, release_func relFunc) { +status_t Parcel::rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function"); LOG_ALWAYS_FATAL_IF(session == nullptr); + if (objectTableSize != ancillaryFds.size()) { + ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size()); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + for (size_t i = 0; i < objectTableSize; i++) { + uint32_t minObjectEnd; + if (__builtin_add_overflow(objectTable[i], sizeof(RpcFields::ObjectType), &minObjectEnd) || + minObjectEnd >= dataSize) { + ALOGE("received out of range object position: %" PRIu32 " (parcel size is %zu)", + objectTable[i], dataSize); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + } + freeData(); markForRpc(session); + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. + mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; mOwner = relFunc; + + rpcFields->mObjectPositions.reserve(objectTableSize); + for (size_t i = 0; i < objectTableSize; i++) { + rpcFields->mObjectPositions.push_back(objectTable[i]); + } + if (!ancillaryFds.empty()) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + *rpcFields->mFds = std::move(ancillaryFds); + } + + return OK; } void Parcel::print(TextOutput& to, uint32_t /*flags*/) const @@ -2387,6 +2638,7 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const } else if (dataSize() > 0) { const uint8_t* DATA = data(); to << indent << HexDump(DATA, dataSize()) << dedent; +#ifdef BINDER_WITH_KERNEL_IPC if (const auto* kernelFields = maybeKernelFields()) { const binder_size_t* OBJS = kernelFields->mObjects; const size_t N = objectsCount(); @@ -2398,6 +2650,7 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder; } } +#endif // BINDER_WITH_KERNEL_IPC } else { to << "NULL"; } @@ -2412,6 +2665,7 @@ void Parcel::releaseObjects() return; } +#ifdef BINDER_WITH_KERNEL_IPC size_t i = kernelFields->mObjectsSize; if (i == 0) { return; @@ -2424,6 +2678,7 @@ void Parcel::releaseObjects() const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); release_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::acquireObjects() @@ -2433,6 +2688,7 @@ void Parcel::acquireObjects() return; } +#ifdef BINDER_WITH_KERNEL_IPC size_t i = kernelFields->mObjectsSize; if (i == 0) { return; @@ -2445,6 +2701,7 @@ void Parcel::acquireObjects() const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); acquire_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::freeData() @@ -2459,7 +2716,9 @@ void Parcel::freeDataNoInit() LOG_ALLOC("Parcel %p: freeing other owner data", this); //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); auto* kernelFields = maybeKernelFields(); - mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + // Close FDs before freeing, otherwise they will leak for kernel binder. + closeFileDescriptors(); + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, kernelFields ? kernelFields->mObjectsSize : 0); } else { LOG_ALLOC("Parcel %p: freeing allocated data", this); @@ -2557,6 +2816,9 @@ status_t Parcel::restartWrite(size_t desired) kernelFields->mObjectsSorted = false; kernelFields->mHasFds = false; kernelFields->mFdsKnown = true; + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mObjectPositions.clear(); + rpcFields->mFds.reset(); } mAllowFds = true; @@ -2572,17 +2834,26 @@ status_t Parcel::continueWrite(size_t desired) } auto* kernelFields = maybeKernelFields(); + auto* rpcFields = maybeRpcFields(); // If shrinking, first adjust for any objects that appear // after the new data size. - size_t objectsSize = kernelFields ? kernelFields->mObjectsSize : 0; - if (kernelFields && desired < mDataSize) { + size_t objectsSize = + kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size(); + if (desired < mDataSize) { if (desired == 0) { objectsSize = 0; } else { - while (objectsSize > 0) { - if (kernelFields->mObjects[objectsSize - 1] < desired) break; - objectsSize--; + if (kernelFields) { + while (objectsSize > 0) { + if (kernelFields->mObjects[objectsSize - 1] < desired) break; + objectsSize--; + } + } else { + while (objectsSize > 0) { + if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break; + objectsSize--; + } } } } @@ -2603,7 +2874,7 @@ status_t Parcel::continueWrite(size_t desired) } binder_size_t* objects = nullptr; - if (objectsSize) { + if (kernelFields && objectsSize) { objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)); if (!objects) { free(data); @@ -2619,6 +2890,12 @@ status_t Parcel::continueWrite(size_t desired) acquireObjects(); kernelFields->mObjectsSize = oldObjectsSize; } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + free(data); + return status; + } + } if (mData) { memcpy(data, mData, mDataSize < desired ? mDataSize : desired); @@ -2626,8 +2903,13 @@ status_t Parcel::continueWrite(size_t desired) if (objects && kernelFields && kernelFields->mObjects) { memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t)); } - //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); - mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + // ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); + if (kernelFields) { + // TODO(b/239222407): This seems wrong. We should only free FDs when + // they are in a truncated section of the parcel. + closeFileDescriptors(); + } + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, kernelFields ? kernelFields->mObjectsSize : 0); mOwner = nullptr; @@ -2648,6 +2930,7 @@ status_t Parcel::continueWrite(size_t desired) } else if (mData) { if (kernelFields && objectsSize < kernelFields->mObjectsSize) { +#ifdef BINDER_WITH_KERNEL_IPC // Need to release refs on any objects we are dropping. const sp<ProcessState> proc(ProcessState::self()); for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) { @@ -2676,6 +2959,14 @@ status_t Parcel::continueWrite(size_t desired) kernelFields->mObjectsSize = objectsSize; kernelFields->mNextObjectHint = 0; kernelFields->mObjectsSorted = false; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel"); +#endif // BINDER_WITH_KERNEL_IPC + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + return status; + } } // We own the data, so we can just do a realloc(). @@ -2733,6 +3024,35 @@ status_t Parcel::continueWrite(size_t desired) return NO_ERROR; } +status_t Parcel::truncateRpcObjects(size_t newObjectsSize) { + auto* rpcFields = maybeRpcFields(); + if (newObjectsSize == 0) { + rpcFields->mObjectPositions.clear(); + if (rpcFields->mFds) { + rpcFields->mFds->clear(); + } + return OK; + } + while (rpcFields->mObjectPositions.size() > newObjectsSize) { + uint32_t pos = rpcFields->mObjectPositions.back(); + rpcFields->mObjectPositions.pop_back(); + const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + const auto fdIndex = + *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType)); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + // In practice, this always removes the last element. + rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex); + } + } + return OK; +} + void Parcel::initState() { LOG_ALLOC("Parcel %p: initState", this); @@ -2747,18 +3067,6 @@ void Parcel::initState() mAllowFds = true; mDeallocZero = false; mOwner = nullptr; - - // racing multiple init leads only to multiple identical write - if (gMaxFds == 0) { - struct rlimit result; - if (!getrlimit(RLIMIT_NOFILE, &result)) { - gMaxFds = (size_t)result.rlim_cur; - //ALOGI("parcel fd limit set to %zu", gMaxFds); - } else { - ALOGW("Unable to getrlimit: %s", strerror(errno)); - gMaxFds = 1024; - } - } } void Parcel::scanForFds() const { @@ -2771,6 +3079,7 @@ void Parcel::scanForFds() const { kernelFields->mFdsKnown = true; } +#ifdef BINDER_WITH_KERNEL_IPC size_t Parcel::getBlobAshmemSize() const { // This used to return the size of all blobs that were written to ashmem, now we're returning @@ -2803,6 +3112,7 @@ size_t Parcel::getOpenAshmemSize() const } return openAshmemSize; } +#endif // BINDER_WITH_KERNEL_IPC // --- Parcel::Blob --- diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 7faff47627..1f311acb20 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -19,6 +19,7 @@ #include <binder/ProcessState.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> @@ -420,6 +421,9 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { } size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { + pthread_mutex_lock(&mThreadCountLock); + base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); }; + // may actually be one more than this, if join is called if (mThreadPoolStarted) { return mCurrentThreads < mKernelStartedThreads diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index c67b70abc0..49be4dd9eb 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -32,7 +32,9 @@ #include <log/log.h> #include <utils/Compat.h> +#include "BuildFlags.h" #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" #include "RpcWireFormat.h" @@ -122,28 +124,36 @@ void RpcServer::setProtocolVersion(uint32_t version) { mProtocolVersion = version; } +void RpcServer::setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes) { + mSupportedFileDescriptorTransportModes.reset(); + for (RpcSession::FileDescriptorTransportMode mode : modes) { + mSupportedFileDescriptorTransportModes.set(static_cast<size_t>(mode)); + } +} + void RpcServer::setRootObject(const sp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObjectFactory = nullptr; mRootObjectWeak = mRootObject = binder; } void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectFactory = nullptr; mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) { - std::lock_guard<std::mutex> _l(mLock); + std::function<sp<IBinder>(const void*, size_t)>&& makeObject) { + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); mRootObjectFactory = std::move(makeObject); } sp<IBinder> RpcServer::getRootObject() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); bool hasWeak = mRootObjectWeak.unsafe_get(); sp<IBinder> ret = mRootObjectWeak.promote(); ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr"); @@ -151,7 +161,7 @@ sp<IBinder> RpcServer::getRootObject() { } std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mCtx->getCertificate(format); } @@ -160,15 +170,17 @@ static void joinRpcServer(sp<RpcServer>&& thiz) { } void RpcServer::start() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!"); - mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + mJoinThread = + std::make_unique<RpcMaybeThread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + rpcJoinIfSingleThreaded(*mJoinThread); } void RpcServer::join() { { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join."); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined"); mJoinThreadRunning = true; @@ -178,14 +190,16 @@ void RpcServer::join() { status_t status; while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) { - sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); + std::array<uint8_t, kRpcAddressSize> addr; + static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small"); + socklen_t addrLen = addr.size(); unique_fd clientFd( - TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr), + TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(addr.data()), &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK))); - LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address"); + LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)), + "Truncated address"); if (clientFd < 0) { ALOGE("Could not accept4 socket: %s", strerror(errno)); @@ -194,24 +208,32 @@ void RpcServer::join() { LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); { - std::lock_guard<std::mutex> _l(mLock); - std::thread thread = - std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this), - std::move(clientFd), addr, addrLen); - mConnectingThreads[thread.get_id()] = std::move(thread); + RpcMutexLockGuard _l(mLock); + RpcMaybeThread thread = + RpcMaybeThread(&RpcServer::establishConnection, + sp<RpcServer>::fromExisting(this), std::move(clientFd), addr, + addrLen, RpcSession::join); + + auto& threadRef = mConnectingThreads[thread.get_id()]; + threadRef = std::move(thread); + rpcJoinIfSingleThreaded(threadRef); } } LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str()); - { - std::lock_guard<std::mutex> _l(mLock); + if constexpr (kEnableRpcThreads) { + RpcMutexLockGuard _l(mLock); mJoinThreadRunning = false; + } else { + // Multi-threaded builds clear this in shutdown(), but we need it valid + // so the loop above exits cleanly + mShutdownTrigger = nullptr; } mShutdownCv.notify_all(); } bool RpcServer::shutdown() { - std::unique_lock<std::mutex> _l(mLock); + RpcMutexUniqueLock _l(mLock); if (mShutdownTrigger == nullptr) { LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)"); return false; @@ -222,10 +244,16 @@ bool RpcServer::shutdown() { for (auto& [id, session] : mSessions) { (void)id; // server lock is a more general lock - std::lock_guard<std::mutex> _lSession(session->mMutex); + RpcMutexLockGuard _lSession(session->mMutex); session->mShutdownTrigger->trigger(); } + if constexpr (!kEnableRpcThreads) { + // In single-threaded mode we're done here, everything else that + // needs to happen should be at the end of RpcServer::join() + return true; + } + while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) { if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) { ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, " @@ -253,7 +281,7 @@ bool RpcServer::shutdown() { } std::vector<sp<RpcSession>> RpcServer::listSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); std::vector<sp<RpcSession>> sessions; for (auto& [id, session] : mSessions) { (void)id; @@ -263,12 +291,14 @@ std::vector<sp<RpcSession>> RpcServer::listSessions() { } size_t RpcServer::numUninitializedSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mConnectingThreads.size(); } -void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen) { +void RpcServer::establishConnection( + sp<RpcServer>&& server, base::unique_fd clientFd, std::array<uint8_t, kRpcAddressSize> addr, + size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) { // mShutdownTrigger can only be cleared once connection threads have joined. // It must be set before this thread is started LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr); @@ -290,7 +320,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie if (status == OK) { iovec iov{&header, sizeof(header)}; status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, - std::nullopt); + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -305,7 +335,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie sessionId.resize(header.sessionIdSize); iovec iov{sessionId.data(), sessionId.size()}; status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, - std::nullopt); + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read session ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -336,7 +366,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie iovec iov{&response, sizeof(response)}; status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, - std::nullopt); + std::nullopt, nullptr); if (status != OK) { ALOGE("Failed to send new session response: %s", statusToString(status).c_str()); // still need to cleanup before we can return @@ -344,12 +374,12 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie } } - std::thread thisThread; + RpcMaybeThread thisThread; sp<RpcSession> session; { - std::unique_lock<std::mutex> _l(server->mLock); + RpcMutexUniqueLock _l(server->mLock); - auto threadId = server->mConnectingThreads.find(std::this_thread::get_id()); + auto threadId = server->mConnectingThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); @@ -390,16 +420,27 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie } } while (server->mSessions.end() != server->mSessions.find(sessionId)); - session = RpcSession::make(); + session = sp<RpcSession>::make(nullptr); session->setMaxIncomingThreads(server->mMaxThreads); if (!session->setProtocolVersion(protocolVersion)) return; + if (header.fileDescriptorTransportMode < + server->mSupportedFileDescriptorTransportModes.size() && + server->mSupportedFileDescriptorTransportModes.test( + header.fileDescriptorTransportMode)) { + session->setFileDescriptorTransportMode( + static_cast<RpcSession::FileDescriptorTransportMode>( + header.fileDescriptorTransportMode)); + } else { + ALOGE("Rejecting connection: FileDescriptorTransportMode is not supported: %hhu", + header.fileDescriptorTransportMode); + return; + } + // if null, falls back to server root sp<IBinder> sessionSpecificRoot; if (server->mRootObjectFactory != nullptr) { - sessionSpecificRoot = - server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr), - addrLen); + sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen); if (sessionSpecificRoot == nullptr) { ALOGE("Warning: server returned null from root object factory"); } @@ -440,7 +481,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie // avoid strong cycle server = nullptr; - RpcSession::join(std::move(session), std::move(setupResult)); + joinFn(std::move(session), std::move(setupResult)); } status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { @@ -486,7 +527,7 @@ void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) LOG_RPC_DETAIL("Dropping session with address %s", base::HexString(id.data(), id.size()).c_str()); - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); auto it = mSessions.find(id); LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s", base::HexString(id.data(), id.size()).c_str()); @@ -500,17 +541,17 @@ void RpcServer::onSessionIncomingThreadEnded() { } bool RpcServer::hasServer() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mServer.ok(); } unique_fd RpcServer::releaseServer() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return std::move(mServer); } status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); if (mServer.ok()) { ALOGE("Each RpcServer can only have one server."); return INVALID_OPERATION; diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 5c35dd0ac1..e6dbd79ffb 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -21,7 +21,6 @@ #include <dlfcn.h> #include <inttypes.h> #include <poll.h> -#include <pthread.h> #include <unistd.h> #include <string_view> @@ -38,6 +37,7 @@ #include <utils/String8.h> #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" #include "RpcWireFormat.h" @@ -60,7 +60,7 @@ RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ct RpcSession::~RpcSession() { LOG_RPC_DETAIL("RpcSession destroyed %p", this); - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0, "Should not be able to destroy a session with servers in use."); } @@ -77,7 +77,7 @@ sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTrans } void RpcSession::setMaxIncomingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), "Must set max incoming threads before setting up connections, but has %zu " "client(s) and %zu server(s)", @@ -86,12 +86,12 @@ void RpcSession::setMaxIncomingThreads(size_t threads) { } size_t RpcSession::getMaxIncomingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxIncomingThreads; } void RpcSession::setMaxOutgoingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), "Must set max outgoing threads before setting up connections, but has %zu " "client(s) and %zu server(s)", @@ -100,7 +100,7 @@ void RpcSession::setMaxOutgoingThreads(size_t threads) { } size_t RpcSession::getMaxOutgoingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxOutgoingThreads; } @@ -113,7 +113,7 @@ bool RpcSession::setProtocolVersion(uint32_t version) { return false; } - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); if (mProtocolVersion && version > *mProtocolVersion) { ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u", *mProtocolVersion, version); @@ -125,10 +125,18 @@ bool RpcSession::setProtocolVersion(uint32_t version) { } std::optional<uint32_t> RpcSession::getProtocolVersion() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mProtocolVersion; } +void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) { + mFileDescriptorTransportMode = mode; +} + +RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() { + return mFileDescriptorTransportMode; +} + status_t RpcSession::setupUnixDomainClient(const char* path) { return setupSocketClient(UnixSocketAddress(path)); } @@ -201,7 +209,7 @@ status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) { } bool RpcSession::shutdownAndWait(bool wait) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed"); mShutdownTrigger->trigger(); @@ -214,6 +222,12 @@ bool RpcSession::shutdownAndWait(bool wait) { } _l.unlock(); + + if (status_t res = state()->sendObituaries(sp<RpcSession>::fromExisting(this)); res != OK) { + ALOGE("Failed to send obituaries as the RpcSession is shutting down: %s", + statusToString(res).c_str()); + } + mRpcBinderState->clear(); return true; @@ -248,7 +262,7 @@ status_t RpcSession::sendDecStrongToTarget(uint64_t address, size_t target) { status_t RpcSession::readId() { { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client."); } @@ -274,7 +288,7 @@ void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() { mCv.notify_all(); } -void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock, +void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session) { while (session->mConnections.mIncoming.size() > 0) { if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) { @@ -285,11 +299,11 @@ void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std:: } } -void RpcSession::preJoinThreadOwnership(std::thread thread) { - LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread"); +void RpcSession::preJoinThreadOwnership(RpcMaybeThread thread) { + LOG_ALWAYS_FATAL_IF(thread.get_id() != rpc_this_thread::get_id(), "Must own this thread"); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); mConnections.mThreads[thread.get_id()] = std::move(thread); } } @@ -396,8 +410,8 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult sp<RpcSession::EventListener> listener; { - std::lock_guard<std::mutex> _l(session->mMutex); - auto it = session->mConnections.mThreads.find(std::this_thread::get_id()); + RpcMutexLockGuard _l(session->mMutex); + auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end()); it->second.detach(); session->mConnections.mThreads.erase(it); @@ -430,7 +444,7 @@ sp<RpcServer> RpcSession::server() { status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>& connectAndInit) { { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0, "Must only setup session once, but already has %zu clients", mConnections.mOutgoing.size()); @@ -492,7 +506,11 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< return status; } +#ifdef BINDER_RPC_SINGLE_THREADED + constexpr size_t outgoingThreads = 1; +#else // BINDER_RPC_SINGLE_THREADED size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads); +#endif // BINDER_RPC_SINGLE_THREADED ALOGI_IF(outgoingThreads != numThreadsAvailable, "Server hints client to start %zu outgoing threads, but client will only start %zu " "because it is preconfigured to start at most %zu outgoing threads.", @@ -606,6 +624,7 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ RpcConnectionHeader header{ .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION), .options = 0, + .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode), .sessionIdSize = static_cast<uint16_t>(sessionId.size()), }; @@ -614,8 +633,8 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } iovec headerIov{&header, sizeof(header)}; - auto sendHeaderStatus = - server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, std::nullopt); + auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, + std::nullopt, nullptr); if (sendHeaderStatus != OK) { ALOGE("Could not write connection header to socket: %s", statusToString(sendHeaderStatus).c_str()); @@ -625,8 +644,9 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ if (sessionId.size() > 0) { iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())), sessionId.size()}; - auto sendSessionIdStatus = server->interruptableWriteFully(mShutdownTrigger.get(), - &sessionIov, 1, std::nullopt); + auto sendSessionIdStatus = + server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, + std::nullopt, nullptr); if (sendSessionIdStatus != OK) { ALOGE("Could not write session ID ('%s') to socket: %s", base::HexString(sessionId.data(), sessionId.size()).c_str(), @@ -645,14 +665,14 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) { - std::mutex mutex; - std::condition_variable joinCv; - std::unique_lock<std::mutex> lock(mutex); - std::thread thread; + RpcMutex mutex; + RpcConditionVariable joinCv; + RpcMutexUniqueLock lock(mutex); + RpcMaybeThread thread; sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this); bool ownershipTransferred = false; - thread = std::thread([&]() { - std::unique_lock<std::mutex> threadLock(mutex); + thread = RpcMaybeThread([&]() { + RpcMutexUniqueLock threadLock(mutex); std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) sp<RpcSession> session = thiz; @@ -668,6 +688,7 @@ status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTran RpcSession::join(std::move(session), std::move(setupResult)); }); + rpcJoinIfSingleThreaded(thread); joinCv.wait(lock, [&] { return ownershipTransferred; }); LOG_ALWAYS_FATAL_IF(!ownershipTransferred); return OK; @@ -687,9 +708,9 @@ status_t RpcSession::initShutdownTrigger() { status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) { sp<RpcConnection> connection = sp<RpcConnection>::make(); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); connection->rpcTransport = std::move(rpcTransport); - connection->exclusiveTid = base::GetThreadId(); + connection->exclusiveTid = rpcGetThreadId(); mConnections.mOutgoing.push_back(connection); } @@ -699,10 +720,7 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this)); } - { - std::lock_guard<std::mutex> _l(mMutex); - connection->exclusiveTid = std::nullopt; - } + clearConnectionTid(connection); return status; } @@ -715,6 +733,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene LOG_ALWAYS_FATAL_IF(mEventListener != nullptr); LOG_ALWAYS_FATAL_IF(eventListener == nullptr); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr); + LOG_ALWAYS_FATAL_IF(mCtx != nullptr); mShutdownTrigger = FdTrigger::make(); if (mShutdownTrigger == nullptr) return false; @@ -728,7 +747,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport) { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); if (mConnections.mIncoming.size() >= mMaxIncomingThreads) { ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)", @@ -746,7 +765,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( sp<RpcConnection> session = sp<RpcConnection>::make(); session->rpcTransport = std::move(rpcTransport); - session->exclusiveTid = base::GetThreadId(); + session->exclusiveTid = rpcGetThreadId(); mConnections.mIncoming.push_back(session); mConnections.mMaxIncoming = mConnections.mIncoming.size(); @@ -755,7 +774,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( } bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); if (auto it = std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection); it != mConnections.mIncoming.end()) { @@ -772,6 +791,15 @@ bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { return false; } +void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) { + RpcMutexUniqueLock _l(mMutex); + connection->exclusiveTid = std::nullopt; + if (mConnections.mWaitingThreads > 0) { + _l.unlock(); + mAvailableConnectionCv.notify_one(); + } +} + std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) { return mCtx->getCertificate(format); } @@ -782,8 +810,8 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co connection->mConnection = nullptr; connection->mReentrant = false; - uint64_t tid = base::GetThreadId(); - std::unique_lock<std::mutex> _l(session->mMutex); + uint64_t tid = rpcGetThreadId(); + RpcMutexUniqueLock _l(session->mMutex); session->mConnections.mWaitingThreads++; while (true) { @@ -901,12 +929,7 @@ RpcSession::ExclusiveConnection::~ExclusiveConnection() { // is using this fd, and it retains the right to it. So, we don't give up // exclusive ownership, and no thread is freed. if (!mReentrant && mConnection != nullptr) { - std::unique_lock<std::mutex> _l(mSession->mMutex); - mConnection->exclusiveTid = std::nullopt; - if (mSession->mConnections.mWaitingThreads > 0) { - _l.unlock(); - mSession->mAvailableConnectionCv.notify_one(); - } + mSession->clearConnectionTid(mConnection); } } diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 7ec8e0738d..c0e36c4322 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -21,12 +21,14 @@ #include <android-base/hex.h> #include <android-base/macros.h> #include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> #include "Debug.h" #include "RpcWireFormat.h" +#include "Utils.h" #include <random> @@ -34,7 +36,7 @@ namespace android { -using base::ScopeGuard; +using base::StringPrintf; #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { @@ -42,13 +44,22 @@ void rpcMaybeWaitToFlake() { [[clang::no_destroy]] static std::mutex m; unsigned num; { - std::lock_guard<std::mutex> lock(m); + RpcMutexLockGuard lock(m); num = r(); } if (num % 10 == 0) usleep(num % 1000); } #endif +static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) { + switch (mode) { + case RpcSession::FileDescriptorTransportMode::NONE: + return false; + case RpcSession::FileDescriptorTransportMode::UNIX: + return true; + } +} + RpcState::RpcState() {} RpcState::~RpcState() {} @@ -77,7 +88,7 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind return INVALID_OPERATION; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map @@ -153,7 +164,7 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t addr return BAD_VALUE; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) { @@ -188,7 +199,7 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t // extra reference counting packets now. if (binder->remoteBinder()) return OK; - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; auto it = mNodeForAddress.find(address); @@ -215,78 +226,105 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t return OK; } +status_t RpcState::sendObituaries(const sp<RpcSession>& session) { + RpcMutexUniqueLock _l(mNodeMutex); + + // Gather strong pointers to all of the remote binders for this session so + // we hold the strong references. remoteBinder() returns a raw pointer. + // Send the obituaries and drop the strong pointers outside of the lock so + // the destructors and the onBinderDied calls are not done while locked. + std::vector<sp<IBinder>> remoteBinders; + for (const auto& [_, binderNode] : mNodeForAddress) { + if (auto binder = binderNode.binder.promote()) { + remoteBinders.push_back(std::move(binder)); + } + } + _l.unlock(); + + for (const auto& binder : remoteBinders) { + if (binder->remoteBinder() && + binder->remoteBinder()->getPrivateAccessor().rpcSession() == session) { + binder->remoteBinder()->sendObituary(); + } + } + return OK; +} + size_t RpcState::countBinders() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); return mNodeForAddress.size(); } void RpcState::dump() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); dumpLocked(); } void RpcState::clear() { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) { LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(), "New state should be impossible after terminating!"); return; } + mTerminated = true; if (SHOULD_LOG_RPC_DETAIL) { ALOGE("RpcState::clear()"); dumpLocked(); } - // if the destructor of a binder object makes another RPC call, then calling - // decStrong could deadlock. So, we must hold onto these binders until - // mNodeMutex is no longer taken. - std::vector<sp<IBinder>> tempHoldBinder; - - mTerminated = true; + // invariants for (auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get()); - - if (node.sentRef != nullptr) { - tempHoldBinder.push_back(node.sentRef); + bool guaranteedHaveBinder = node.timesSent > 0; + if (guaranteedHaveBinder) { + LOG_ALWAYS_FATAL_IF(node.sentRef == nullptr, + "Binder expected to be owned with address: %" PRIu64 " %s", address, + node.toString().c_str()); } } - mNodeForAddress.clear(); + // if the destructor of a binder object makes another RPC call, then calling + // decStrong could deadlock. So, we must hold onto these binders until + // mNodeMutex is no longer taken. + auto temp = std::move(mNodeForAddress); + mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit _l.unlock(); - tempHoldBinder.clear(); // explicit + temp.clear(); // explicit } void RpcState::dumpLocked() { ALOGE("DUMP OF RpcState %p", this); ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size()); for (const auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - - const char* desc; - if (binder) { - if (binder->remoteBinder()) { - if (binder->remoteBinder()->isRpcBinder()) { - desc = "(rpc binder proxy)"; - } else { - desc = "(binder proxy)"; - } + ALOGE("- address: %" PRIu64 " %s", address, node.toString().c_str()); + } + ALOGE("END DUMP OF RpcState"); +} + +std::string RpcState::BinderNode::toString() const { + sp<IBinder> strongBinder = this->binder.promote(); + + const char* desc; + if (strongBinder) { + if (strongBinder->remoteBinder()) { + if (strongBinder->remoteBinder()->isRpcBinder()) { + desc = "(rpc binder proxy)"; } else { - desc = "(local binder)"; + desc = "(binder proxy)"; } } else { - desc = "(null)"; + desc = "(local binder)"; } - - ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s", - node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc); + } else { + desc = "(not promotable)"; } - ALOGE("END DUMP OF RpcState"); -} + return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}", + this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc); +} RpcState::CommandData::CommandData(size_t size) : mSize(size) { // The maximum size for regular binder is 1MB for all concurrent @@ -309,9 +347,11 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { mData.reset(new (std::nothrow) uint8_t[size]); } -status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { +status_t RpcState::rpcSend( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, 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) { for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", what, i + 1, niovs, connection->rpcTransport.get(), @@ -320,7 +360,8 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, if (status_t status = connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(), - iovs, niovs, altPoll); + iovs, niovs, altPoll, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -331,11 +372,14 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, return OK; } -status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) { +status_t RpcState::rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { if (status_t status = connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), - iovs, niovs, std::nullopt); + iovs, niovs, std::nullopt, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -355,7 +399,7 @@ status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& c const sp<RpcSession>& session, uint32_t* version) { RpcNewSessionResponse response; iovec iov{&response, sizeof(response)}; - if (status_t status = rpcRec(connection, session, "new session response", &iov, 1); + if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr); status != OK) { return status; } @@ -376,7 +420,8 @@ status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& conne const sp<RpcSession>& session) { RpcOutgoingConnectionInit init; iovec iov{&init, sizeof(init)}; - if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr); + status != OK) return status; static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY)); @@ -448,20 +493,12 @@ status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection, status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection, const sp<IBinder>& binder, uint32_t code, const Parcel& data, const sp<RpcSession>& session, Parcel* reply, uint32_t flags) { - if (!data.isForRpc()) { - ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code " - "%" PRIu32, - binder.get(), code); - return BAD_TYPE; - } - - if (data.objectsCount() != 0) { - ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p " - "code %" PRIu32, - &data, binder.get(), code); - return BAD_TYPE; + std::string errorMsg; + if (status_t status = validateParcel(session, data, &errorMsg); status != OK) { + ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s", + binder.get(), code, &data, errorMsg.c_str()); + return status; } - uint64_t address; if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status; @@ -477,7 +514,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti uint64_t asyncNumber = 0; if (address != 0) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(address); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -493,9 +530,17 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti } } + auto* rpcFields = data.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + uint32_t bodySize; LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(), - &bodySize), + &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), "Too much data %zu", data.dataSize()); RpcWireHeader command{ .command = RPC_COMMAND_TRANSACT, @@ -507,6 +552,8 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .code = code, .flags = flags, .asyncNumber = asyncNumber, + // bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(data.dataSize()), }; constexpr size_t kWaitMaxUs = 1000000; @@ -521,26 +568,27 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti {&command, sizeof(RpcWireHeader)}, {&transaction, sizeof(RpcWireTransaction)}, {const_cast<uint8_t*>(data.data()), data.dataSize()}, + objectTableSpan.toIovec(), }; - if (status_t status = rpcSend(connection, session, "transaction", iovs, arraysize(iovs), - [&] { - if (waitUs > kWaitLogUs) { - ALOGE("Cannot send command, trying to process pending " - "refcounts. Waiting %zuus. Too " - "many oneway calls?", - waitUs); - } - - if (waitUs > 0) { - usleep(waitUs); - waitUs = std::min(kWaitMaxUs, waitUs * 2); - } else { - waitUs = 1; - } - - return drainCommands(connection, session, - CommandType::CONTROL_ONLY); - }); + if (status_t status = rpcSend( + connection, session, "transaction", iovs, arraysize(iovs), + [&] { + if (waitUs > kWaitLogUs) { + ALOGE("Cannot send command, trying to process pending refcounts. Waiting " + "%zuus. Too many oneway calls?", + waitUs); + } + + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } + + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }, + rpcFields->mFds.get()); status != OK) { // TODO(b/167966510): need to undo onBinderLeaving - we know the // refcount isn't successfully transferred. @@ -560,52 +608,91 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti return waitForReply(connection, session, reply); } -static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount) { - (void)p; - delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data)); +static void cleanup_reply_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount) { + delete[] const_cast<uint8_t*>(data); (void)dataSize; LOG_ALWAYS_FATAL_IF(objects != nullptr); - LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount); + (void)objectsCount; } status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply) { + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; while (true) { iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1); + if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) + ? &ancillaryFds + : nullptr); status != OK) return status; if (command.command == RPC_COMMAND_REPLY) break; - if (status_t status = processCommand(connection, session, command, CommandType::ANY); + if (status_t status = processCommand(connection, session, command, CommandType::ANY, + std::move(ancillaryFds)); status != OK) return status; - } - CommandData data(command.bodySize); - if (!data.valid()) return NO_MEMORY; + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = decltype(ancillaryFds)(); + } - iovec iov{data.data(), command.bodySize}; - if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK) - return status; + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); - if (command.bodySize < sizeof(RpcWireReply)) { + if (command.bodySize < rpcReplyWireSize) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", sizeof(RpcWireReply), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data()); - if (rpcReply->status != OK) return rpcReply->status; - data.release(); - reply->rpcSetDataReference(session, rpcReply->data, - command.bodySize - offsetof(RpcWireReply, data), cleanup_reply_data); + RpcWireReply rpcReply; + memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read - return OK; + CommandData data(command.bodySize - rpcReplyWireSize); + if (!data.valid()) return NO_MEMORY; + + iovec iovs[]{ + {&rpcReply, rpcReplyWireSize}, + {data.data(), data.size()}, + }; + if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + status != OK) + return status; + + if (rpcReply.status != OK) return rpcReply.status; + + Span<const uint8_t> parcelSpan = {data.data(), data.size()}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() >= + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(rpcReply.parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size larger than available bytes: %" PRId32 " vs %zu. Terminating!", + rpcReply.parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32 + " sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!", + command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize, + objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + + data.release(); + return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), cleanup_reply_data); } status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection, @@ -616,7 +703,7 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co }; { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(addr); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -648,13 +735,17 @@ status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& con const sp<RpcSession>& session, CommandType type) { LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get()); + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1); + if (status_t status = + rpcRec(connection, session, "command header (for server)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds + : nullptr); status != OK) return status; - return processCommand(connection, session, command, type); + return processCommand(connection, session, command, type, std::move(ancillaryFds)); } status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection, @@ -670,28 +761,33 @@ status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection return OK; } -status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command, - CommandType type) { +status_t RpcState::processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { +#ifdef BINDER_WITH_KERNEL_IPC IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull(); IPCThreadState::SpGuard spGuard{ .address = __builtin_frame_address(0), - .context = "processing binder RPC command", + .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is " + "used to distinguish callers)", }; const IPCThreadState::SpGuard* origGuard; if (kernelBinderState != nullptr) { origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - ScopeGuard guardUnguard = [&]() { + + base::ScopeGuard guardUnguard = [&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } }; +#endif // BINDER_WITH_KERNEL_IPC switch (command.command) { case RPC_COMMAND_TRANSACT: if (type != CommandType::ANY) return BAD_TYPE; - return processTransact(connection, session, command); + return processTransact(connection, session, command, std::move(ancillaryFds)); case RPC_COMMAND_DEC_STRONG: return processDecStrong(connection, session, command); } @@ -705,8 +801,10 @@ status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connectio (void)session->shutdownAndWait(false); return DEAD_OBJECT; } -status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command) { +status_t RpcState::processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); CommandData transactionData(command.bodySize); @@ -714,24 +812,26 @@ status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connecti return NO_MEMORY; } iovec iov{transactionData.data(), transactionData.size()}; - if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1, nullptr); + status != OK) return status; - return processTransactInternal(connection, session, std::move(transactionData)); + return processTransactInternal(connection, session, std::move(transactionData), + std::move(ancillaryFds)); } -static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize, +static void do_nothing_to_transact_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { - (void)p; (void)data; (void)dataSize; (void)objects; (void)objectsCount; } -status_t RpcState::processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData) { +status_t RpcState::processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { // for 'recursive' calls to this, we have already read and processed the // binder from the transaction data and taken reference counts into account, // so it is cached here. @@ -775,7 +875,7 @@ processTransactInternalTailCall: (void)session->shutdownAndWait(false); replyStatus = BAD_VALUE; } else if (oneway) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it->second.binder.promote() != target) { ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr); @@ -819,52 +919,85 @@ processTransactInternalTailCall: reply.markForRpc(session); if (replyStatus == OK) { + Span<const uint8_t> parcelSpan = {transaction->data, + transactionData.size() - + offsetof(RpcWireTransaction, data)}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() > + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(transaction->parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size (%" PRId32 ") greater than available bytes (%zu). Terminating!", + transaction->parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu " + "sizeofHeader=%zu parcelSize=%" PRId32 + " objectTableBytesSize=%zu. Terminating!", + transactionData.size(), sizeof(RpcWireTransaction), + transaction->parcelDataSize, objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + Parcel data; // transaction->data is owned by this function. Parcel borrows this data and // only holds onto it for the duration of this function call. Parcel will be // deleted before the 'transactionData' object. - data.rpcSetDataReference(session, transaction->data, - transactionData.size() - offsetof(RpcWireTransaction, data), - do_nothing_to_transact_data); - if (target) { - bool origAllowNested = connection->allowNested; - connection->allowNested = !oneway; + replyStatus = + data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), do_nothing_to_transact_data); + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = std::remove_reference<decltype(ancillaryFds)>::type(); - replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); + if (replyStatus == OK) { + if (target) { + bool origAllowNested = connection->allowNested; + connection->allowNested = !oneway; - connection->allowNested = origAllowNested; - } else { - LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { - replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); - break; - } - case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { - // for client connections, this should always report the value - // originally returned from the server, so this is asserting - // that it exists - replyStatus = reply.writeByteVector(session->mId); - break; - } - default: { - sp<RpcServer> server = session->server(); - if (server) { - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_ROOT: { - sp<IBinder> root = session->mSessionSpecificRootObject - ?: server->getRootObject(); - replyStatus = reply.writeStrongBinder(root); - break; - } - default: { - replyStatus = UNKNOWN_TRANSACTION; + connection->allowNested = origAllowNested; + } else { + LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { + replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); + break; + } + case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { + // for client connections, this should always report the value + // originally returned from the server, so this is asserting + // that it exists + replyStatus = reply.writeByteVector(session->mId); + break; + } + default: { + sp<RpcServer> server = session->server(); + if (server) { + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_ROOT: { + sp<IBinder> root = session->mSessionSpecificRootObject + ?: server->getRootObject(); + replyStatus = reply.writeStrongBinder(root); + break; + } + default: { + replyStatus = UNKNOWN_TRANSACTION; + } } + } else { + ALOGE("Special command sent, but no server object attached."); } - } else { - ALOGE("Special command sent, but no server object attached."); } } } @@ -890,7 +1023,7 @@ processTransactInternalTailCall: // downside: asynchronous transactions may drown out synchronous // transactions. { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); // last refcount dropped after this transaction happened if (it == mNodeForAddress.end()) return OK; @@ -936,8 +1069,27 @@ processTransactInternalTailCall: replyStatus = flushExcessBinderRefs(session, addr, target); } + std::string errorMsg; + if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) { + ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str()); + // Forward the error to the client of the transaction. + reply.freeData(); + reply.markForRpc(session); + replyStatus = status; + } + + auto* rpcFields = reply.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + uint32_t bodySize; - LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireReply), reply.dataSize(), &bodySize), + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), "Too much data for reply %zu", reply.dataSize()); RpcWireHeader cmdReply{ .command = RPC_COMMAND_REPLY, @@ -945,38 +1097,41 @@ processTransactInternalTailCall: }; RpcWireReply rpcReply{ .status = replyStatus, + // NOTE: Not necessarily written to socket depending on session + // version. + // NOTE: bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(reply.dataSize()), + .reserved = {0, 0, 0}, }; - iovec iovs[]{ {&cmdReply, sizeof(RpcWireHeader)}, - {&rpcReply, sizeof(RpcWireReply)}, + {&rpcReply, rpcReplyWireSize}, {const_cast<uint8_t*>(reply.data()), reply.dataSize()}, + objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt); + return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + rpcFields->mFds.get()); } status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command); - CommandData commandData(command.bodySize); - if (!commandData.valid()) { - return NO_MEMORY; - } - iovec iov{commandData.data(), commandData.size()}; - if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK) - return status; - if (command.bodySize != sizeof(RpcDecStrong)) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!", sizeof(RpcDecStrong), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data()); - uint64_t addr = RpcWireAddress::toRaw(body->address); - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcDecStrong body; + iovec iov{&body, sizeof(RpcDecStrong)}; + if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr); + status != OK) + return status; + + uint64_t addr = RpcWireAddress::toRaw(body.address); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr); @@ -993,19 +1148,19 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return BAD_VALUE; } - if (it->second.timesSent < body->amount) { + if (it->second.timesSent < body.amount) { ALOGE("Record of sending binder %zu times, but requested decStrong for %" PRIu64 " of %u", - it->second.timesSent, addr, body->amount); + it->second.timesSent, addr, body.amount); return OK; } LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64, addr); - LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body->amount, + LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body.amount, it->second.timesSent); - it->second.timesSent -= body->amount; + it->second.timesSent -= body.amount; sp<IBinder> tempHold = tryEraseNode(it); _l.unlock(); tempHold = nullptr; // destructor may make binder calls on this session @@ -1013,6 +1168,50 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return OK; } +status_t RpcState::validateParcel(const sp<RpcSession>& session, const Parcel& parcel, + std::string* errorMsg) { + auto* rpcFields = parcel.maybeRpcFields(); + if (rpcFields == nullptr) { + *errorMsg = "Parcel not crafted for RPC call"; + return BAD_TYPE; + } + + if (rpcFields->mSession != session) { + *errorMsg = "Parcel's session doesn't match"; + return BAD_TYPE; + } + + uint32_t protocolVersion = session->getProtocolVersion().value(); + if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE && + !rpcFields->mObjectPositions.empty()) { + *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version " + "(%" PRIu32 ") is too old, must be at least %" PRIu32, + protocolVersion, + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE); + return BAD_VALUE; + } + + if (rpcFields->mFds && !rpcFields->mFds->empty()) { + switch (session->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: + *errorMsg = + "Parcel has file descriptors, but no file descriptor transport is enabled"; + return FDS_NOT_ALLOWED; + case RpcSession::FileDescriptorTransportMode::UNIX: { + constexpr size_t kMaxFdsPerMsg = 253; + if (rpcFields->mFds->size() > kMaxFdsPerMsg) { + *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix " + "domain socket: %zu (max is %zu)", + rpcFields->mFds->size(), kMaxFdsPerMsg); + return BAD_VALUE; + } + } + } + } + + return OK; +} + sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) { sp<IBinder> ref; diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 9cbe187444..7aab5eeae7 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -19,6 +19,7 @@ #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <map> #include <optional> @@ -139,6 +140,11 @@ public: */ [[nodiscard]] status_t flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t address, const sp<IBinder>& binder); + /** + * Called when the RpcSession is shutdown. + * Send obituaries for each known remote binder with this session. + */ + [[nodiscard]] status_t sendObituaries(const sp<RpcSession>& session); size_t countBinders(); void dump(); @@ -181,26 +187,36 @@ private: [[nodiscard]] status_t rpcSend( const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll); - [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, 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 = + nullptr); + [[nodiscard]] status_t rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr); [[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply); - [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command, CommandType type); - [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command); - [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData); + [[nodiscard]] status_t processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); [[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command); + // Whether `parcel` is compatible with `session`. + [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session, + const Parcel& parcel, std::string* errorMsg); + struct BinderNode { // Two cases: // A - local binder we are serving @@ -247,6 +263,8 @@ private: // // (no additional data specific to remote binders) + + std::string toString() const; }; // checks if there is any reference left to a node and erases it. If erase @@ -258,7 +276,7 @@ private: // false - session shutdown, halt [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node); - std::mutex mNodeMutex; + RpcMutex mNodeMutex; bool mTerminated = false; uint32_t mNextId = 0; // binders known by both sides of a session diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index f9b73fce21..7cc58cd776 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -18,6 +18,7 @@ #include <log/log.h> #include <poll.h> +#include <stddef.h> #include <binder/RpcTransportRaw.h> @@ -28,6 +29,9 @@ namespace android { namespace { +// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. +constexpr size_t kMaxFdsPerMsg = 253; + // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: @@ -85,15 +89,7 @@ public: bool havePolled = false; while (true) { - msghdr msg{ - .msg_iov = iovs, - // posix uses int, glibc uses size_t. niovs is a - // non-negative int and can be cast to either. - .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), - }; - ssize_t processSize = - TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL)); - + ssize_t processSize = sendOrReceiveFun(iovs, niovs); if (processSize < 0) { int savedErrno = errno; @@ -145,16 +141,121 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT, - altPoll); + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + bool sentFds = false; + auto send = [&](iovec* iovs, int niovs) -> ssize_t { + if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) { + if (ancillaryFds->size() > kMaxFdsPerMsg) { + // This shouldn't happen because we check the FD count in RpcState. + ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). " + "Aborting session.", + ancillaryFds->size(), kMaxFdsPerMsg); + errno = EINVAL; + return -1; + } + + // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then + // use memcpy. + int fds[kMaxFdsPerMsg]; + for (size_t i = 0; i < ancillaryFds->size(); i++) { + fds[i] = std::visit([](const auto& fd) { return fd.get(); }, + ancillaryFds->at(i)); + } + const size_t fdsByteSize = sizeof(int) * ancillaryFds->size(); + + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(fdsByteSize); + memcpy(CMSG_DATA(cmsg), fds, fdsByteSize); + + msg.msg_controllen = CMSG_SPACE(fdsByteSize); + + ssize_t processedSize = TEMP_FAILURE_RETRY( + sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC)); + if (processedSize > 0) { + sentFds = true; + } + return processedSize; + } + + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, altPoll); } status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN, - altPoll); + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { + auto recv = [&](iovec* iovs, int niovs) -> ssize_t { + if (ancillaryFds != nullptr) { + int fdBuffer[kMaxFdsPerMsg]; + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + ssize_t processSize = + TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + if (processSize < 0) { + return -1; + } + + for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks + // application devs to memcpy the data to ensure memory alignment. + size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0); + LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check + memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen); + size_t fdCount = dataLen / sizeof(int); + ancillaryFds->reserve(ancillaryFds->size() + fdCount); + for (size_t i = 0; i < fdCount; i++) { + ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i])); + } + break; + } + } + + if (msg.msg_flags & MSG_CTRUNC) { + ALOGE("msg was truncated. Aborting session."); + errno = EPIPE; + return -1; + } + + return processSize; + } + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll); } private: diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index ad5cb0fc56..09b5c17152 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -282,10 +282,13 @@ public: status_t pollRead(void) override; status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) override; + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override; status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) override; + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; private: android::base::unique_fd mSocket; @@ -313,7 +316,10 @@ status_t RpcTransportTls::pollRead(void) { status_t RpcTransportTls::interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -356,7 +362,10 @@ status_t RpcTransportTls::interruptableWriteFully( status_t RpcTransportTls::interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h index 171550e620..ff1b01a213 100644 --- a/libs/binder/RpcWireFormat.h +++ b/libs/binder/RpcWireFormat.h @@ -45,7 +45,8 @@ static_assert(sizeof(RpcWireAddress) == sizeof(uint64_t)); struct RpcConnectionHeader { uint32_t version; // maximum supported by caller uint8_t options; - uint8_t reservered[9]; + uint8_t fileDescriptorTransportMode; + uint8_t reservered[8]; // Follows is sessionIdSize bytes. // if size is 0, this is requesting a new session. uint16_t sessionIdSize; @@ -108,6 +109,10 @@ enum : uint32_t { // serialization is like: // |RpcWireHeader|struct desginated by 'command'| (over and over again) +// +// When file descriptors are included in out-of-band data (e.g. in unix domain +// sockets), they are always paired with the RpcWireHeader bytes of the +// transaction or reply the file descriptors belong to. struct RpcWireHeader { uint32_t command; // RPC_COMMAND_* @@ -131,7 +136,10 @@ struct RpcWireTransaction { uint64_t asyncNumber; - uint32_t reserved[4]; + // The size of the Parcel data directly following RpcWireTransaction. + uint32_t parcelDataSize; + + uint32_t reserved[3]; uint8_t data[]; }; @@ -139,9 +147,23 @@ static_assert(sizeof(RpcWireTransaction) == 40); struct RpcWireReply { int32_t status; // transact return - uint8_t data[]; + + // -- Fields below only transmitted starting at protocol version 1 -- + + // The size of the Parcel data directly following RpcWireReply. + uint32_t parcelDataSize; + + uint32_t reserved[3]; + + // Byte size of RpcWireReply in the wire protocol. + static size_t wireSize(uint32_t protocolVersion) { + if (protocolVersion == 0) { + return sizeof(int32_t); + } + return sizeof(RpcWireReply); + } }; -static_assert(sizeof(RpcWireReply) == 4); +static_assert(sizeof(RpcWireReply) == 20); #pragma clang diagnostic pop diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index 83b97d04c6..dba65878fb 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -139,6 +139,9 @@ status_t Status::readFromParcel(const Parcel& parcel) { mMessage = String8(message.value_or(String16())); // Skip over the remote stack trace data + const size_t remote_start = parcel.dataPosition(); + // Get available size before reading more + const size_t remote_avail = parcel.dataAvail(); int32_t remote_stack_trace_header_size; status = parcel.readInt32(&remote_stack_trace_header_size); if (status != OK) { @@ -146,13 +149,16 @@ status_t Status::readFromParcel(const Parcel& parcel) { return status; } if (remote_stack_trace_header_size < 0 || - static_cast<size_t>(remote_stack_trace_header_size) > parcel.dataAvail()) { + static_cast<size_t>(remote_stack_trace_header_size) > remote_avail) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } - parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size); + + if (remote_stack_trace_header_size != 0) { + parcel.setDataPosition(remote_start + remote_stack_trace_header_size); + } if (mException == EX_SERVICE_SPECIFIC) { status = parcel.readInt32(&mErrorCode); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 0232f509a2..c91d56c95e 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -28,9 +28,6 @@ "name": "binderLibTest" }, { - "name": "binderRpcTest" - }, - { "name": "binderStabilityTest" }, { @@ -84,7 +81,21 @@ "name": "rustBinderSerializationTest" } ], - "hwasan-presubmit": [ + "presubmit-large": [ + { + "name": "binderRpcTest" + }, + { + "name": "binderRpcTestNoKernel" + }, + { + "name": "binderRpcTestSingleThreaded" + }, + { + "name": "binderRpcTestSingleThreadedNoKernel" + } + ], + "hwasan-presubmit": [ { "name": "binderLibTest" } diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index b0289a7196..0314b0fea7 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -16,40 +16,12 @@ #include "Utils.h" -#include <android-base/file.h> #include <string.h> -using android::base::ErrnoError; -using android::base::Result; - namespace android { void zeroMemory(uint8_t* data, size_t size) { memset(data, 0, size); } -Result<void> setNonBlocking(android::base::borrowed_fd fd) { - int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); - if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; - } - if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { - return ErrnoError() << "Could not set non-blocking flag for fd"; - } - return {}; -} - -status_t getRandomBytes(uint8_t* data, size_t size) { - int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); - if (ret == -1) { - return -errno; - } - - base::unique_fd fd(ret); - if (!base::ReadFully(fd, data, size)) { - return -errno; - } - return OK; -} - } // namespace android diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index 150d520492..e04199c75a 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,11 +14,11 @@ * limitations under the License. */ -#include <cstdint> #include <stddef.h> +#include <sys/uio.h> +#include <cstdint> +#include <optional> -#include <android-base/result.h> -#include <android-base/unique_fd.h> #include <log/log.h> #include <utils/Errors.h> @@ -35,8 +35,39 @@ namespace android { // avoid optimizations void zeroMemory(uint8_t* data, size_t size); -android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +// View of contiguous sequence. Similar to std::span. +template <typename T> +struct Span { + T* data = nullptr; + size_t size = 0; + + size_t byteSize() { return size * sizeof(T); } + + iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; } + + // Truncates `this` to a length of `offset` and returns a span with the + // remainder. + // + // `std::nullopt` iff offset > size. + std::optional<Span<T>> splitOff(size_t offset) { + if (offset > size) { + return std::nullopt; + } + Span<T> rest = {data + offset, size - offset}; + size = offset; + return rest; + } -status_t getRandomBytes(uint8_t* data, size_t size); + // Returns nullopt if the byte size of `this` isn't evenly divisible by sizeof(U). + template <typename U> + std::optional<Span<U>> reinterpret() const { + // Only allow casting from bytes for simplicity. + static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>); + if (size % sizeof(U) != 0) { + return std::nullopt; + } + return Span<U>{reinterpret_cast<U*>(data), size / sizeof(U)}; + } +}; } // namespace android diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index f295417ab2..e864f9e6a3 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -72,9 +72,9 @@ class BnInterface : public INTERFACE, public BBinder public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; @@ -85,9 +85,9 @@ class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp<IBinder>& remote); + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index cd6a27414d..c01e92f043 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -28,6 +28,10 @@ typedef int uid_t; // --------------------------------------------------------------------------- namespace android { +/** + * Kernel binder thread state. All operations here refer to kernel binder. This + * object is allocated per-thread. + */ class IPCThreadState { public: @@ -213,9 +217,8 @@ private: void clearCaller(); static void threadDestructor(void *st); - static void freeBuffer(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + static void freeBuffer(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); static void logExtendedError(); const sp<ProcessState> mProcess; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index bb55831ec2..413c97f349 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -134,6 +134,12 @@ public: virtual status_t unregisterForNotifications(const String16& name, const sp<LocalRegistrationCallback>& callback) = 0; + + struct ServiceDebugInfo { + std::string name; + int pid; + }; + virtual std::vector<ServiceDebugInfo> getServiceDebugInfo() = 0; }; sp<IServiceManager> defaultServiceManager(); diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 0345a5d8e1..54692398c6 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -598,9 +598,9 @@ public: void print(TextOutput& to, uint32_t flags = 0) const; private: - typedef void (*release_func)(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + // `objects` and `objectsSize` always 0 for RPC Parcels. + typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); uintptr_t ipcData() const; size_t ipcDataSize() const; @@ -608,8 +608,12 @@ private: size_t ipcObjectsCount() const; void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc); - void rpcSetDataReference(const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, - release_func relFunc); + // Takes ownership even when an error is returned. + status_t rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc); status_t finishWrite(size_t len); void releaseObjects(); @@ -620,6 +624,7 @@ private: status_t restartWrite(size_t desired); // Set the capacity to `desired`, truncating the Parcel if necessary. status_t continueWrite(size_t desired); + status_t truncateRpcObjects(size_t newObjectsSize); status_t writePointer(uintptr_t val); status_t readPointer(uintptr_t *pArg) const; uintptr_t readPointer() const; @@ -1179,10 +1184,20 @@ private: c->clear(); // must clear before resizing/reserving otherwise move ctors may be called. if constexpr (is_pointer_equivalent_array_v<T>) { // could consider POD without gaps and alignment of 4. - auto data = reinterpret_cast<const T*>( - readInplace(static_cast<size_t>(size) * sizeof(T))); + size_t dataLen; + if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) { + return -EOVERFLOW; + } + auto data = reinterpret_cast<const T*>(readInplace(dataLen)); if (data == nullptr) return BAD_VALUE; - c->insert(c->begin(), data, data + size); // insert should do a reserve(). + // std::vector::insert and similar methods will require type-dependent + // byte alignment when inserting from a const iterator such as `data`, + // e.g. 8 byte alignment for int64_t, and so will not work if `data` + // is 4 byte aligned (which is all Parcel guarantees). Copying + // the contents into the vector directly, where possible, circumvents + // this. + c->resize(size); + memcpy(c->data(), data, dataLen); } else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char16_t>) { c->reserve(size); // avoids default initialization @@ -1279,6 +1294,23 @@ private: // Should always be non-null. const sp<RpcSession> mSession; + + enum ObjectType : int32_t { + TYPE_BINDER_NULL = 0, + TYPE_BINDER = 1, + // FD to be passed via native transport (Trusty IPC or UNIX domain socket). + TYPE_NATIVE_FILE_DESCRIPTOR = 2, + }; + + // Sorted. + std::vector<uint32_t> mObjectPositions; + + // File descriptors referenced by the parcel data. Should be indexed + // using the offsets in the parcel data. Don't assume the list is in the + // same order as `mObjectPositions`. + // + // Boxed to save space. Lazy allocated. + std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds; }; std::variant<KernelFields, RpcFields> mVariantFields; diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index e17a76cf23..9679a5f477 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -29,6 +29,10 @@ namespace android { class IPCThreadState; +/** + * Kernel binder process state. All operations here refer to kernel binder. This + * object is allocated per process. + */ class ProcessState : public virtual RefBase { public: static sp<ProcessState> self(); @@ -126,7 +130,7 @@ private: void* mVMStart; // Protects thread count and wait variables below. - pthread_mutex_t mThreadCountLock; + mutable pthread_mutex_t mThreadCountLock; // Broadcast whenever mWaitingForThreads > 0 pthread_cond_t mThreadCountDecrement; // Number of binder threads current executing a command. diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 6b31812d17..52bda0e8ed 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -18,6 +18,7 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -28,6 +29,7 @@ namespace android { class FdTrigger; +class RpcServerTrusty; class RpcSocketAddress; /** @@ -114,6 +116,15 @@ public: void setProtocolVersion(uint32_t version); /** + * Set the supported transports for sending and receiving file descriptors. + * + * Clients will propose a mode when connecting. If the mode is not in the + * provided list, the connection will be rejected. + */ + void setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes); + + /** * The root object can be retrieved by any client, without any * authentication. TODO(b/183988761) * @@ -125,9 +136,17 @@ public: */ void setRootObjectWeak(const wp<IBinder>& binder); /** - * Allows a root object to be created for each session - */ - void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object); + * Allows a root object to be created for each session. + * + * Takes one argument: a callable that is invoked once per new session. + * The callable takes two arguments: a type-erased pointer to an OS- and + * transport-specific address structure, e.g., sockaddr_vm for vsock, and + * an integer representing the size in bytes of that structure. The + * callable should validate the size, then cast the type-erased pointer + * to a pointer to the actual type of the address, e.g., const void* to + * const sockaddr_vm*. + */ + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object); sp<IBinder> getRootObject(); /** @@ -171,31 +190,39 @@ public: ~RpcServer(); private: + friend RpcServerTrusty; friend sp<RpcServer>; explicit RpcServer(std::unique_ptr<RpcTransportCtx> ctx); void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen); + static constexpr size_t kRpcAddressSize = 128; + static void establishConnection( + sp<RpcServer>&& server, base::unique_fd clientFd, + std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn); [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address); const std::unique_ptr<RpcTransportCtx> mCtx; size_t mMaxThreads = 1; std::optional<uint32_t> mProtocolVersion; + // A mode is supported if the N'th bit is on, where N is the mode enum's value. + std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set( + static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE)); base::unique_fd mServer; // socket we are accepting sessions on - std::mutex mLock; // for below - std::unique_ptr<std::thread> mJoinThread; + RpcMutex mLock; // for below + std::unique_ptr<RpcMaybeThread> mJoinThread; bool mJoinThreadRunning = false; - std::map<std::thread::id, std::thread> mConnectingThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mConnectingThreads; + sp<IBinder> mRootObject; wp<IBinder> mRootObjectWeak; - std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory; + std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory; std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions; std::unique_ptr<FdTrigger> mShutdownTrigger; - std::condition_variable mShutdownCv; + RpcConditionVariable mShutdownCv; }; } // namespace android diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index cb8158489f..9d94e005c3 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -18,19 +18,20 @@ #include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <map> #include <optional> -#include <thread> #include <vector> namespace android { class Parcel; class RpcServer; +class RpcServerTrusty; class RpcSocketAddress; class RpcState; class RpcTransport; @@ -38,7 +39,13 @@ class FdTrigger; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000; -constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0; +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL; + +// Starting with this version: +// +// * RpcWireReply is larger (4 bytes -> 20). +// * RpcWireTransaction and RpcWireReplyV1 include the parcel data size. +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1; /** * This represents a session (group of connections) between a client @@ -89,6 +96,18 @@ public: [[nodiscard]] bool setProtocolVersion(uint32_t version); std::optional<uint32_t> getProtocolVersion(); + enum class FileDescriptorTransportMode : uint8_t { + NONE = 0, + // Send file descriptors via unix domain socket ancillary data. + UNIX = 1, + }; + + /** + * Set the transport for sending and receiving file descriptors. + */ + void setFileDescriptorTransportMode(FileDescriptorTransportMode mode); + FileDescriptorTransportMode getFileDescriptorTransportMode(); + /** * This should be called once per thread, matching 'join' in the remote * process. @@ -184,6 +203,7 @@ public: private: friend sp<RpcSession>; friend RpcServer; + friend RpcServerTrusty; friend RpcState; explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx); @@ -200,10 +220,10 @@ private: public: void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session); + void waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session); private: - std::condition_variable mCv; + RpcConditionVariable mCv; }; friend WaitForShutdownListener; @@ -226,7 +246,7 @@ private: // // transfer ownership of thread (usually done while a lock is taken on the // structure which originally owns the thread) - void preJoinThreadOwnership(std::thread thread); + void preJoinThreadOwnership(RpcMaybeThread thread); // pass FD to thread and read initial connection information struct PreJoinSetupResult { // Server connection object associated with this @@ -258,6 +278,7 @@ private: sp<RpcConnection> assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport); [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection); + void clearConnectionTid(const sp<RpcConnection>& connection); [[nodiscard]] status_t initShutdownTrigger(); @@ -307,7 +328,7 @@ private: // For a more complicated case, the client might itself open up a thread to // serve calls to the server at all times (e.g. if it hosts a callback) - wp<RpcServer> mForServer; // maybe null, for client sessions + wp<RpcServer> mForServer; // maybe null, for client sessions sp<WaitForShutdownListener> mShutdownListener; // used for client sessions wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client @@ -321,13 +342,14 @@ private: std::unique_ptr<RpcState> mRpcBinderState; - std::mutex mMutex; // for all below + RpcMutex mMutex; // for all below size_t mMaxIncomingThreads = 0; size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; std::optional<uint32_t> mProtocolVersion; + FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE; - std::condition_variable mAvailableConnectionCv; // for mWaitingThreads + RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads struct ThreadState { size_t mWaitingThreads = 0; @@ -336,7 +358,7 @@ private: std::vector<sp<RpcConnection>> mOutgoing; size_t mMaxIncoming = 0; std::vector<sp<RpcConnection>> mIncoming; - std::map<std::thread::id, std::thread> mThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads; } mConnections; }; diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h new file mode 100644 index 0000000000..8abf04eaf0 --- /dev/null +++ b/libs/binder/include/binder/RpcThreads.h @@ -0,0 +1,145 @@ +/* + * 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 <pthread.h> + +#include <android-base/threads.h> + +#include <functional> +#include <memory> +#include <thread> + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +class RpcMutex { +public: + void lock() {} + void unlock() {} +}; + +class RpcMutexUniqueLock { +public: + RpcMutexUniqueLock(RpcMutex&) {} + void unlock() {} +}; + +class RpcMutexLockGuard { +public: + RpcMutexLockGuard(RpcMutex&) {} +}; + +class RpcConditionVariable { +public: + void notify_one() {} + void notify_all() {} + + void wait(RpcMutexUniqueLock&) {} + + template <typename Predicate> + void wait(RpcMutexUniqueLock&, Predicate stop_waiting) { + LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met"); + } + + template <typename Duration> + std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) { + return std::cv_status::no_timeout; + } + + template <typename Duration, typename Predicate> + bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) { + return stop_waiting(); + } +}; + +class RpcMaybeThread { +public: + RpcMaybeThread() = default; + + template <typename Function, typename... Args> + RpcMaybeThread(Function&& f, Args&&... args) { + // std::function requires a copy-constructible closure, + // so we need to wrap both the function and its arguments + // in a shared pointer that std::function can copy internally + struct Vars { + std::decay_t<Function> f; + std::tuple<std::decay_t<Args>...> args; + + explicit Vars(Function&& f, Args&&... args) + : f(std::move(f)), args(std::move(args)...) {} + }; + auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...); + mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); }; + } + + void join() { + if (mFunc) { + // Move mFunc into a temporary so we can clear mFunc before + // executing the callback. This avoids infinite recursion if + // the callee then calls join() again directly or indirectly. + decltype(mFunc) func = nullptr; + mFunc.swap(func); + func(); + } + } + void detach() { join(); } + + class id { + public: + bool operator==(const id&) const { return true; } + bool operator!=(const id&) const { return false; } + bool operator<(const id&) const { return false; } + bool operator<=(const id&) const { return true; } + bool operator>(const id&) const { return false; } + bool operator>=(const id&) const { return true; } + }; + + id get_id() const { return id(); } + +private: + std::function<void(void)> mFunc; +}; + +namespace rpc_this_thread { +static inline RpcMaybeThread::id get_id() { + return RpcMaybeThread::id(); +} +} // namespace rpc_this_thread + +static inline uint64_t rpcGetThreadId() { + return 0; +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) { + t.join(); +} +#else // BINDER_RPC_SINGLE_THREADED +using RpcMutex = std::mutex; +using RpcMutexUniqueLock = std::unique_lock<std::mutex>; +using RpcMutexLockGuard = std::lock_guard<std::mutex>; +using RpcConditionVariable = std::condition_variable; +using RpcMaybeThread = std::thread; +namespace rpc_this_thread = std::this_thread; + +static inline uint64_t rpcGetThreadId() { + return base::GetThreadId(); +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {} +#endif // BINDER_RPC_SINGLE_THREADED + +} // namespace android diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index ee4b5483be..5197ef9f4b 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -22,6 +22,8 @@ #include <memory> #include <optional> #include <string> +#include <variant> +#include <vector> #include <android-base/function_ref.h> #include <android-base/unique_fd.h> @@ -61,16 +63,23 @@ public: * to read/write data. If this returns an error, that error is returned from * this function. * + * ancillaryFds - FDs to be sent via UNIX domain dockets or Trusty IPC. When + * reading, if `ancillaryFds` is null, any received FDs will be silently + * dropped and closed (by the OS). Appended values will always be unique_fd, + * the variant type is used to avoid extra copies elsewhere. + * * Return: * OK - succeeded in completely processing 'size' * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; protected: RpcTransport() = default; diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index bf2b25b265..a3d42b76fe 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -38,10 +38,10 @@ bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* contex << " error: " << statusToString(status).c_str(); return false; } - server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) { - LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock"); + 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)); }); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 79c8c8f88b..32e018d597 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -113,6 +113,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 28e3ff43ff..b21a7e9584 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -36,6 +36,7 @@ using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; using ::android::wp; @@ -133,7 +134,8 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder."; + << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) + << "'."; } return false; } @@ -458,7 +460,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { LOG(ERROR) << __func__ - << ": removed reference to death recipient but unlink failed."; + << ": removed reference to death recipient but unlink failed: " + << statusToString(status); } return PruneStatusT(status); } @@ -539,7 +542,8 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -550,7 +554,8 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -625,7 +630,8 @@ void* AIBinder_getUserData(AIBinder* binder) { binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder + << ") and in (" << in << ")."; return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); @@ -671,7 +677,9 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { - LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK."; + LOG(ERROR) << __func__ + << ": Only user-defined transactions can be made from the NDK, but requested: " + << code; return STATUS_UNKNOWN_TRANSACTION; } @@ -682,7 +690,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" + << in << "), and out (" << out << ")."; return STATUS_UNEXPECTED_NULL; } diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 3824a1b44c..6bc9814048 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -89,12 +89,12 @@ LIBBINDER_NDK { # introduced=29 AStatus_getStatus; AStatus_isOk; AStatus_newOk; - ABinderProcess_joinThreadPool; # apex llndk - ABinderProcess_setThreadPoolMaxThreadCount; # apex llndk - ABinderProcess_startThreadPool; # apex llndk - AServiceManager_addService; # apex llndk - AServiceManager_checkService; # apex llndk - AServiceManager_getService; # apex llndk + ABinderProcess_joinThreadPool; # systemapi llndk + ABinderProcess_setThreadPoolMaxThreadCount; # systemapi llndk + ABinderProcess_startThreadPool; # systemapi llndk + AServiceManager_addService; # systemapi llndk + AServiceManager_checkService; # systemapi llndk + AServiceManager_getService; # systemapi llndk }; LIBBINDER_NDK30 { # introduced=30 @@ -105,30 +105,30 @@ LIBBINDER_NDK30 { # introduced=30 AStatus_deleteDescription; AParcel_fromJavaParcel; - AIBinder_markSystemStability; # apex + AIBinder_markSystemStability; # systemapi AIBinder_markVendorStability; # llndk - AIBinder_markVintfStability; # apex llndk - AIBinder_Class_setHandleShellCommand; # apex llndk + AIBinder_markVintfStability; # systemapi llndk + AIBinder_Class_setHandleShellCommand; # systemapi llndk }; LIBBINDER_NDK31 { # introduced=31 global: - ABinderProcess_handlePolledCommands; # apex - ABinderProcess_setupPolling; # apex - AIBinder_getCallingSid; # apex - AIBinder_setRequestingSid; # apex + ABinderProcess_handlePolledCommands; # systemapi + ABinderProcess_setupPolling; # systemapi + AIBinder_getCallingSid; # systemapi + AIBinder_setRequestingSid; # systemapi AParcel_markSensitive; # systemapi llndk - AServiceManager_forEachDeclaredInstance; # apex llndk - AServiceManager_forceLazyServicesPersist; # apex llndk - AServiceManager_isDeclared; # apex llndk - AServiceManager_isUpdatableViaApex; # apex + AServiceManager_forEachDeclaredInstance; # systemapi llndk + AServiceManager_forceLazyServicesPersist; # systemapi llndk + AServiceManager_isDeclared; # systemapi llndk + AServiceManager_isUpdatableViaApex; # systemapi AServiceManager_reRegister; # llndk - AServiceManager_registerLazyService; # apex llndk + AServiceManager_registerLazyService; # systemapi llndk AServiceManager_setActiveServicesCallback; # llndk AServiceManager_tryUnregister; # llndk - AServiceManager_waitForService; # apex llndk + AServiceManager_waitForService; # systemapi llndk - AIBinder_forceDowngradeToSystemStability; # apex + AIBinder_forceDowngradeToSystemStability; # systemapi AIBinder_forceDowngradeToVendorStability; # llndk AIBinder_Class_getDescriptor; @@ -146,8 +146,8 @@ LIBBINDER_NDK33 { # introduced=33 AIBinder_Class_disableInterfaceTokenHeader; AIBinder_DeathRecipient_setOnUnlinked; AIBinder_isHandlingTransaction; - AIBinder_setInheritRt; # llndk - AIBinder_setMinSchedulerPolicy; # llndk + AIBinder_setInheritRt; # systemapi llndk + AIBinder_setMinSchedulerPolicy; # systemapi llndk AParcel_marshal; AParcel_unmarshal; }; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index b7c7ae4b54..3edeebf892 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -335,11 +335,16 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // We don't own this file, so we need to be careful not to drop it. let file = ManuallyDrop::new(File::from_raw_fd(fd)); - if args.is_null() { + if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; } - let args = slice::from_raw_parts(args, num_args as usize); - let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); + + let args = if args.is_null() || num_args == 0 { + vec![] + } else { + slice::from_raw_parts(args, num_args as usize) + .iter().map(|s| CStr::from_ptr(*s)).collect() + }; let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 2f96d0e0e0..d7c6d4966e 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -141,6 +141,7 @@ aidl_interface { unstable: true, srcs: [ "BinderRpcTestClientInfo.aidl", + "BinderRpcTestServerConfig.aidl", "BinderRpcTestServerInfo.aidl", "IBinderRpcCallback.aidl", "IBinderRpcSession.aidl", @@ -167,7 +168,6 @@ cc_library_static { "libbinder_tls_shared_deps", ], shared_libs: [ - "libbinder", "libbase", "liblog", ], @@ -185,25 +185,72 @@ cc_library_static { ], } -cc_test { - name: "binderRpcTest", +cc_defaults { + name: "binderRpcTest_common_defaults", host_supported: true, target: { darwin: { enabled: false, }, + }, + defaults: [ + "binder_test_defaults", + ], + + static_libs: [ + "libbinder_tls_static", + "libbinder_tls_test_utils", + "binderRpcTestIface-cpp", + "binderRpcTestIface-ndk", + ], +} + +cc_defaults { + name: "binderRpcTest_service_defaults", + defaults: [ + "binderRpcTest_common_defaults", + ], + gtest: false, + auto_gen_config: false, + srcs: [ + "binderRpcTestCommon.cpp", + "binderRpcTestService.cpp", + ], +} + +cc_defaults { + name: "binderRpcTest_defaults", + target: { android: { test_suites: ["vts"], }, }, defaults: [ - "binder_test_defaults", - "libbinder_tls_shared_deps", + "binderRpcTest_common_defaults", ], srcs: [ "binderRpcTest.cpp", + "binderRpcTestCommon.cpp", + ], + + test_suites: ["general-tests"], + require_root: true, + + data_bins: [ + "binder_rpc_test_service", + "binder_rpc_test_service_no_kernel", + "binder_rpc_test_service_single_threaded", + "binder_rpc_test_service_single_threaded_no_kernel", + ], +} + +cc_defaults { + name: "binderRpcTest_shared_defaults", + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", ], + shared_libs: [ "libbinder", "libbinder_ndk", @@ -212,14 +259,128 @@ cc_test { "libcutils", "liblog", ], +} + +cc_defaults { + name: "binderRpcTest_static_defaults", + + shared_libs: [ + "libutils", + // libcrypto_static is not visible to this module + "libcrypto", + ], static_libs: [ - "libbinder_tls_static", - "libbinder_tls_test_utils", - "binderRpcTestIface-cpp", - "binderRpcTestIface-ndk", + "libbase", + "libcutils", + "liblog", + "libssl", + ], + + cflags: [ + // Disable tests that require shared libraries, + // e.g., libbinder.so or libbinder_ndk.so + "-DBINDER_TEST_NO_SHARED_LIBS", + ], +} + +cc_test { + // The module name cannot start with "binderRpcTest" because + // then atest tries to execute it as part of binderRpcTest + name: "binder_rpc_test_service", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], +} + +cc_test { + name: "binder_rpc_test_service_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", + ], +} + +cc_test { + name: "binderRpcTest", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], +} + +cc_test { + name: "binderRpcTestNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binderRpcTestSingleThreaded", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binderRpcTestSingleThreadedNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", ], - test_suites: ["general-tests"], - require_root: true, } cc_test { diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl new file mode 100644 index 0000000000..34d74bee0d --- /dev/null +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -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. + */ + +parcelable BinderRpcTestServerConfig { + int numThreads; + int[] serverSupportedFileDescriptorTransportModes; + int socketType; + int rpcSecurity; + int serverVersion; + int vsockPort; + @utf8InCpp String addr; +} diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index fdd02a4435..b15a2251ef 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -24,6 +24,9 @@ interface IBinderRpcTest { // number of known RPC binders to process, RpcState::countBinders by session int[] countBinders(); + // Return a null binder with a non-nullable return type. + IBinder getNullBinder(); + // Caller sends server, callee pings caller's server and returns error code. int pingMe(IBinder binder); @nullable IBinder repeatBinder(@nullable IBinder binder); @@ -64,4 +67,8 @@ interface IBinderRpcTest { void scheduleShutdown(); void useKernelBinderCallingId(); + + ParcelFileDescriptor echoAsFile(@utf8InCpp String content); + + ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files); } diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index dd1a8c341d..60b3c94f5e 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -203,13 +203,15 @@ TEST(RpcBinderAllocation, SetupRpcServer) { auto remoteBinder = session->getRootObject(); size_t mallocs = 0, totalBytes = 0; - const auto on_malloc = OnMalloc([&](size_t bytes) { - mallocs++; - totalBytes += bytes; - }); - CHECK_EQ(OK, remoteBinder->pingBinder()); - EXPECT_EQ(mallocs, 3); - EXPECT_EQ(totalBytes, 60); + { + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + totalBytes += bytes; + }); + CHECK_EQ(OK, remoteBinder->pingBinder()); + } + EXPECT_EQ(mallocs, 1); + EXPECT_EQ(totalBytes, 40); } int main(int argc, char** argv) { diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 3e907263fb..e72f39c24a 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -30,6 +30,7 @@ #include <android-base/properties.h> #include <android-base/result-gmock.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <binder/Binder.h> @@ -1232,6 +1233,53 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> { + TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {} + + // Flattenable protocol + size_t getFlattenedSize() const { + // Return a valid non-zero size here so we don't get an unintended + // BAD_VALUE from Parcel::write + return 16; + } + size_t getFdCount() const { return mFdCount; } + status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const { + for (size_t i = 0; i < count; i++) { + fds[i] = STDIN_FILENO; + } + return NO_ERROR; + } + status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/, + size_t & /*count*/) { + /* This doesn't get called */ + return NO_ERROR; + } + + size_t mFdCount; +}; + +TEST_F(BinderLibTest, TooManyFdsFlattenable) { + rlimit origNofile; + int ret = getrlimit(RLIMIT_NOFILE, &origNofile); + ASSERT_EQ(0, ret); + + // Restore the original file limits when the test finishes + base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + + rlimit testNofile = {1024, 1024}; + ret = setrlimit(RLIMIT_NOFILE, &testNofile); + ASSERT_EQ(0, ret); + + Parcel parcel; + // Try to write more file descriptors than supported by the OS + TooManyFdsFlattenable tooManyFds1(1024); + EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE)); + + // Try to write more file descriptors than the internal limit + TooManyFdsFlattenable tooManyFds2(1025); + EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE)); +} + TEST(ServiceNotifications, Unregister) { auto sm = defaultServiceManager(); using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; @@ -1262,13 +1310,15 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { * not exceed 16 (15 Max + pool thread). */ std::vector<std::thread> ts; - for (size_t i = 0; i < kKernelThreads - 1; i++) { + for (size_t i = 0; i < kKernelThreads; i++) { ts.push_back(std::thread([&] { - EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &reply), NO_ERROR); + Parcel local_reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), + NO_ERROR); })); } - data.writeInt32(1); + data.writeInt32(100); // Give a chance for all threads to be used EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); @@ -1279,8 +1329,7 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), StatusEq(NO_ERROR)); replyi = reply.readInt32(); - // No more than 16 threads should exist. - EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1); + EXPECT_EQ(replyi, kKernelThreads + 1); } size_t epochMillis() { @@ -1302,7 +1351,9 @@ TEST_F(BinderLibTest, HangingServices) { size_t epochMsBefore = epochMillis(); for (size_t i = 0; i < kKernelThreads + 1; i++) { ts.push_back(std::thread([&] { - EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &reply), NO_ERROR); + Parcel local_reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), + NO_ERROR); })); } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 4161a7a25b..501a604026 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -14,29 +14,7 @@ * limitations under the License. */ -#include <BinderRpcTestClientInfo.h> -#include <BinderRpcTestServerInfo.h> -#include <BnBinderRpcCallback.h> -#include <BnBinderRpcSession.h> -#include <BnBinderRpcTest.h> -#include <aidl/IBinderRpcTest.h> -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android/binder_auto_utils.h> -#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> -#include <binder/RpcSession.h> -#include <binder/RpcTlsTestUtils.h> -#include <binder/RpcTlsUtils.h> -#include <binder/RpcTransport.h> -#include <binder/RpcTransportRaw.h> -#include <binder/RpcTransportTls.h> +#include <android-base/stringprintf.h> #include <gtest/gtest.h> #include <chrono> @@ -45,14 +23,12 @@ #include <thread> #include <type_traits> +#include <dlfcn.h> #include <poll.h> #include <sys/prctl.h> -#include <unistd.h> +#include <sys/socket.h> -#include "../FdTrigger.h" -#include "../RpcSocketAddress.h" // for testing preconnected clients -#include "../RpcState.h" // for debugging -#include "../vm_sockets.h" // for VMADDR_* +#include "binderRpcTestCommon.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -62,54 +38,35 @@ using testing::AssertionSuccess; namespace android { +#ifdef BINDER_TEST_NO_SHARED_LIBS +constexpr bool kEnableSharedLibs = false; +#else +constexpr bool kEnableSharedLibs = true; +#endif + static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); -const char* kLocalInetAddress = "127.0.0.1"; - -enum class RpcSecurity { RAW, TLS }; - -static inline std::vector<RpcSecurity> RpcSecurityValues() { - return {RpcSecurity::RAW, RpcSecurity::TLS}; -} - -static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( - RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, - std::unique_ptr<RpcAuth> auth = nullptr) { - switch (rpcSecurity) { - case RpcSecurity::RAW: - return RpcTransportCtxFactoryRaw::make(); - case RpcSecurity::TLS: { - if (verifier == nullptr) { - verifier = std::make_shared<RpcCertificateVerifierSimple>(); - } - if (auth == nullptr) { - auth = std::make_unique<RpcAuthSelfSigned>(); - } - return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); - } - default: - LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); - } -} TEST(BinderRpcParcel, EntireParcelFormatted) { Parcel p; p.writeInt32(3); - EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); + EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written"); } -class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { +class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { public: static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { - return newFactory(info.param)->toCString(); + return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + + std::to_string(std::get<1>(info.param)); } }; -TEST_P(BinderRpcSimple, SetExternalServerTest) { +TEST_P(BinderRpcServerOnly, SetExternalServerTest) { base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); - auto server = RpcServer::make(newFactory(GetParam())); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); @@ -139,191 +96,15 @@ using android::binder::Status; EXPECT_TRUE(stat.isOk()) << stat; \ } while (false) -class MyBinderRpcSession : public BnBinderRpcSession { -public: - static std::atomic<int32_t> gNum; - - MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } - Status getName(std::string* name) override { - *name = mName; - return Status::ok(); +static std::string WaitStatusToString(int wstatus) { + if (WIFEXITED(wstatus)) { + return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); } - ~MyBinderRpcSession() { gNum--; } - -private: - std::string mName; -}; -std::atomic<int32_t> MyBinderRpcSession::gNum; - -class MyBinderRpcCallback : public BnBinderRpcCallback { - Status sendCallback(const std::string& value) { - std::unique_lock _l(mMutex); - mValues.push_back(value); - _l.unlock(); - mCv.notify_one(); - return Status::ok(); + if (WIFSIGNALED(wstatus)) { + return base::StringPrintf("term signal %d", WTERMSIG(wstatus)); } - Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } - -public: - std::mutex mMutex; - std::condition_variable mCv; - std::vector<std::string> mValues; -}; - -class MyBinderRpcTest : public BnBinderRpcTest { -public: - wp<RpcServer> server; - int port = 0; - - Status sendString(const std::string& str) override { - (void)str; - return Status::ok(); - } - Status doubleString(const std::string& str, std::string* strstr) override { - *strstr = str + str; - return Status::ok(); - } - Status getClientPort(int* out) override { - *out = port; - return Status::ok(); - } - Status countBinders(std::vector<int32_t>* out) override { - sp<RpcServer> spServer = server.promote(); - if (spServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - out->clear(); - for (auto session : spServer->listSessions()) { - size_t count = session->state()->countBinders(); - out->push_back(count); - } - return Status::ok(); - } - Status pingMe(const sp<IBinder>& binder, int32_t* out) override { - if (binder == nullptr) { - std::cout << "Received null binder!" << std::endl; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - *out = binder->pingBinder(); - return Status::ok(); - } - Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { - *out = binder; - return Status::ok(); - } - static sp<IBinder> mHeldBinder; - Status holdBinder(const sp<IBinder>& binder) override { - mHeldBinder = binder; - return Status::ok(); - } - Status getHeldBinder(sp<IBinder>* held) override { - *held = mHeldBinder; - return Status::ok(); - } - Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { - if (count <= 0) return Status::ok(); - return binder->nestMe(this, count - 1); - } - Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { - static sp<IBinder> binder = new BBinder; - *out = binder; - return Status::ok(); - } - Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { - *out = new MyBinderRpcSession(name); - return Status::ok(); - } - Status getNumOpenSessions(int32_t* out) override { - *out = MyBinderRpcSession::gNum; - return Status::ok(); - } - - std::mutex blockMutex; - Status lock() override { - blockMutex.lock(); - return Status::ok(); - } - Status unlockInMsAsync(int32_t ms) override { - usleep(ms * 1000); - blockMutex.unlock(); - return Status::ok(); - } - Status lockUnlock() override { - std::lock_guard<std::mutex> _l(blockMutex); - return Status::ok(); - } - - Status sleepMs(int32_t ms) override { - usleep(ms * 1000); - return Status::ok(); - } - - Status sleepMsAsync(int32_t ms) override { - // In-process binder calls are asynchronous, but the call to this method - // is synchronous wrt its client. This in/out-process threading model - // diffentiation is a classic binder leaky abstraction (for better or - // worse) and is preserved here the way binder sockets plugs itself - // into BpBinder, as nothing is changed at the higher levels - // (IInterface) which result in this behavior. - return sleepMs(ms); - } - - Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - if (callback == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - - if (delayed) { - std::thread([=]() { - ALOGE("Executing delayed callback: '%s'", value.c_str()); - Status status = doCallback(callback, oneway, false, value); - ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); - }).detach(); - return Status::ok(); - } - - if (oneway) { - return callback->sendOnewayCallback(value); - } - - return callback->sendCallback(value); - } - - Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - return doCallback(callback, oneway, delayed, value); - } - - Status die(bool cleanup) override { - if (cleanup) { - exit(1); - } else { - _exit(1); - } - } - - Status scheduleShutdown() override { - sp<RpcServer> strongServer = server.promote(); - if (strongServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - std::thread([=] { - LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); - }).detach(); - return Status::ok(); - } - - 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; + return base::StringPrintf("unexpected state %d", wstatus); +} class Process { public: @@ -332,8 +113,8 @@ public: android::base::borrowed_fd /* readEnd */)>& f) { android::base::unique_fd childWriteEnd; android::base::unique_fd childReadEnd; - CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno); - CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno); + CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno); + CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -345,13 +126,28 @@ public: } ~Process() { if (mPid != 0) { - waitpid(mPid, nullptr, 0); + int wstatus; + waitpid(mPid, &wstatus, 0); + if (mCustomExitStatusCheck) { + mCustomExitStatusCheck(wstatus); + } else { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + << "server process failed: " << WaitStatusToString(wstatus); + } } } android::base::borrowed_fd readEnd() { return mReadEnd; } android::base::borrowed_fd writeEnd() { return mWriteEnd; } + void setCustomExitStatusCheck(std::function<void(int wstatus)> f) { + mCustomExitStatusCheck = std::move(f); + } + + // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead. + void terminate() { kill(mPid, SIGTERM); } + private: + std::function<void(int wstatus)> mCustomExitStatusCheck; pid_t mPid = 0; android::base::unique_fd mReadEnd; android::base::unique_fd mWriteEnd; @@ -366,7 +162,7 @@ static std::string allocateSocketAddress() { }; static unsigned int allocateVsockPort() { - static unsigned int vsockPort = 3456; + static unsigned int vsockPort = 34567; return vsockPort++; } @@ -419,10 +215,10 @@ struct BinderRpcTestProcessSession { BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; ~BinderRpcTestProcessSession() { - EXPECT_NE(nullptr, rootIface); - if (rootIface == nullptr) return; - if (!expectAlreadyShutdown) { + EXPECT_NE(nullptr, rootIface); + if (rootIface == nullptr) return; + std::vector<int32_t> remoteCounts; // calling over any sessions counts across all sessions EXPECT_OK(rootIface->countBinders(&remoteCounts)); @@ -443,28 +239,6 @@ struct BinderRpcTestProcessSession { } }; -enum class SocketType { - PRECONNECTED, - UNIX, - VSOCK, - INET, -}; -static inline std::string PrintToString(SocketType socketType) { - switch (socketType) { - case SocketType::PRECONNECTED: - return "preconnected_uds"; - case SocketType::UNIX: - return "unix_domain_socket"; - case SocketType::VSOCK: - return "vm_socket"; - case SocketType::INET: - return "inet_socket"; - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - return ""; - } -} - static base::unique_fd connectTo(const RpcSocketAddress& addr) { base::unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); @@ -480,115 +254,79 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } -class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { -public: - struct Options { - size_t numThreads = 1; - size_t numSessions = 1; - size_t numIncomingConnections = 0; - size_t numOutgoingConnections = SIZE_MAX; - }; +using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd, + android::base::borrowed_fd readEnd); - static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [type, security] = info.param; - return PrintToString(type) + "_" + newFactory(security)->toCString(); - } +class BinderRpc : public ::testing::TestWithParam< + std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { +public: + SocketType socketType() const { return std::get<0>(GetParam()); } + RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); } + uint32_t clientVersion() const { return std::get<2>(GetParam()); } + uint32_t serverVersion() const { return std::get<3>(GetParam()); } + bool singleThreaded() const { return std::get<4>(GetParam()); } + bool noKernel() const { return std::get<5>(GetParam()); } - static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { - uint64_t length = str.length(); - CHECK(android::base::WriteFully(fd, &length, sizeof(length))); - CHECK(android::base::WriteFully(fd, str.data(), str.length())); + // Whether the test params support sending FDs in parcels. + bool supportsFdTransport() const { + return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && + (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX); } - static inline std::string readString(android::base::borrowed_fd fd) { - uint64_t length; - CHECK(android::base::ReadFully(fd, &length, sizeof(length))); - std::string ret(length, '\0'); - CHECK(android::base::ReadFully(fd, ret.data(), length)); + static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { + auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param; + auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); + if (singleThreaded) { + ret += "_single_threaded"; + } + if (noKernel) { + ret += "_no_kernel"; + } return ret; } - static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { - Parcel parcel; - CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); - writeString(fd, - std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); - } - - template <typename T> - static inline T readFromFd(android::base::borrowed_fd fd) { - std::string data = readString(fd); - Parcel parcel; - CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); - T object; - CHECK_EQ(OK, object.readFromParcel(&parcel)); - return object; - } - // This creates a new process serving an interface on a certain number of // threads. - ProcessSession createRpcTestSocketServerProcess( - const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) { + ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) { CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; SocketType socketType = std::get<0>(GetParam()); RpcSecurity rpcSecurity = std::get<1>(GetParam()); + uint32_t clientVersion = std::get<2>(GetParam()); + uint32_t serverVersion = std::get<3>(GetParam()); + bool singleThreaded = std::get<4>(GetParam()); + bool noKernel = std::get<5>(GetParam()); - unsigned int vsockPort = allocateVsockPort(); - std::string addr = allocateSocketAddress(); + std::string path = android::base::GetExecutableDirectory(); + auto servicePath = + android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(), + singleThreaded ? "_single_threaded" : "", + noKernel ? "_no_kernel" : ""); auto ret = ProcessSession{ - .host = Process([&](android::base::borrowed_fd writeEnd, + .host = Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { - auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); - sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); - - server->setMaxThreads(options.numThreads); - - unsigned int outPort = 0; - - switch (socketType) { - case SocketType::PRECONNECTED: - [[fallthrough]]; - case SocketType::UNIX: - CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr; - break; - case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(vsockPort)); - break; - case SocketType::INET: { - CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); - CHECK_NE(0, outPort); - break; - } - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - } - - BinderRpcTestServerInfo serverInfo; - serverInfo.port = static_cast<int64_t>(outPort); - serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); - writeToFd(writeEnd, serverInfo); - auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); - - if (rpcSecurity == RpcSecurity::TLS) { - for (const auto& clientCert : clientInfo.certs) { - CHECK_EQ(OK, - certVerifier - ->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - clientCert.data)); - } - } - - configure(server); - - server->join(); - - // Another thread calls shutdown. Wait for it to complete. - (void)server->shutdown(); + auto writeFd = std::to_string(writeEnd.get()); + auto readFd = std::to_string(readEnd.get()); + execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), + NULL); }), }; + BinderRpcTestServerConfig serverConfig; + serverConfig.numThreads = options.numThreads; + serverConfig.socketType = static_cast<int32_t>(socketType); + serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); + serverConfig.serverVersion = serverVersion; + serverConfig.vsockPort = allocateVsockPort(); + serverConfig.addr = allocateSocketAddress(); + for (auto mode : options.serverSupportedFileDescriptorTransportModes) { + serverConfig.serverSupportedFileDescriptorTransportModes.push_back( + static_cast<int32_t>(mode)); + } + writeToFd(ret.host.writeEnd(), serverConfig); + std::vector<sp<RpcSession>> sessions; auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); for (size_t i = 0; i < options.numSessions; i++) { @@ -618,20 +356,22 @@ public: status_t status; for (const auto& session : sessions) { + CHECK(session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(options.numIncomingConnections); session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); switch (socketType) { case SocketType::PRECONNECTED: status = session->setupPreconnectedClient({}, [=]() { - return connectTo(UnixSocketAddress(addr.c_str())); + return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); }); break; case SocketType::UNIX: - status = session->setupUnixDomainClient(addr.c_str()); + status = session->setupUnixDomainClient(serverConfig.addr.c_str()); break; case SocketType::VSOCK: - status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); + status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); break; case SocketType::INET: status = session->setupInetClient("127.0.0.1", serverInfo.port); @@ -639,52 +379,22 @@ public: default: LOG_ALWAYS_FATAL("Unknown socket type"); } + if (options.allowConnectFailure && status != OK) { + ret.sessions.clear(); + break; + } CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); ret.sessions.push_back({session, session->getRootObject()}); } return ret; } - BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) { + BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcess( - options, - [&](const sp<RpcServer>& server) { - server->setPerSessionRootObject([&](const sockaddr* addr, - socklen_t len) { - sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); - switch (addr->sa_family) { - case AF_UNIX: - // nothing to save - break; - case AF_VSOCK: - CHECK_EQ(len, sizeof(sockaddr_vm)); - service->port = reinterpret_cast<const sockaddr_vm*>(addr) - ->svm_port; - break; - case AF_INET: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in*>(addr) - ->sin_port); - break; - case AF_INET6: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in6*>(addr) - ->sin6_port); - break; - default: - LOG_ALWAYS_FATAL("Unrecognized address family %d", - addr->sa_family); - } - service->server = server; - return service; - }); - }), + .proc = createRpcTestSocketServerProcessEtc(options), }; - ret.rootBinder = ret.proc.sessions.at(0).root; + ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root; ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); return ret; @@ -694,6 +404,18 @@ public: size_t sleepMs = 500); }; +// Test fixture for tests that start multiple threads. +// This includes tests with one thread but multiple sessions, +// since a server uses one thread per session. +class BinderRpcThreads : public BinderRpc { +public: + void SetUp() override { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + } +}; + TEST_P(BinderRpc, Ping) { auto proc = createRpcTestSocketServerProcess({}); ASSERT_NE(proc.rootBinder, nullptr); @@ -706,7 +428,7 @@ TEST_P(BinderRpc, GetInterfaceDescriptor) { EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); } -TEST_P(BinderRpc, MultipleSessions) { +TEST_P(BinderRpcThreads, MultipleSessions) { auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); for (auto session : proc.proc.sessions) { ASSERT_NE(nullptr, session.root); @@ -714,7 +436,7 @@ TEST_P(BinderRpc, MultipleSessions) { } } -TEST_P(BinderRpc, SeparateRootObject) { +TEST_P(BinderRpcThreads, SeparateRootObject) { SocketType type = std::get<0>(GetParam()); if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) { // we can't get port numbers for unix sockets @@ -752,7 +474,7 @@ TEST_P(BinderRpc, AppendSeparateFormats) { p1.markForBinder(proc1.rootBinder); p1.writeInt32(3); - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize())); + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize())); EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); Parcel p2; @@ -791,6 +513,13 @@ TEST_P(BinderRpc, SendAndGetResultBackBig) { EXPECT_EQ(single + single, doubled); } +TEST_P(BinderRpc, InvalidNullBinderReturn) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL); +} + TEST_P(BinderRpc, CallMeBack) { auto proc = createRpcTestSocketServerProcess({}); @@ -890,7 +619,7 @@ TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); } -TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { +TEST_P(BinderRpcThreads, CannotMixBindersBetweenTwoSessionsToTheSameServer) { auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); sp<IBinder> outBinder; @@ -900,6 +629,11 @@ TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { } TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + auto proc = createRpcTestSocketServerProcess({}); sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); @@ -909,6 +643,11 @@ TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { } TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + auto proc = createRpcTestSocketServerProcess({}); // for historical reasons, IServiceManager interface only returns the @@ -928,7 +667,13 @@ TEST_P(BinderRpc, RepeatRootObject) { } TEST_P(BinderRpc, NestedTransactions) { - auto proc = createRpcTestSocketServerProcess({}); + auto proc = createRpcTestSocketServerProcess({ + // Enable FD support because it uses more stack space and so represents + // something closer to a worst case scenario. + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); auto nastyNester = sp<MyBinderRpcTest>::make(); EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); @@ -1030,7 +775,7 @@ size_t epochMillis() { return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); } -TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { +TEST_P(BinderRpcThreads, ThreadPoolGreaterThanEqualRequested) { constexpr size_t kNumThreads = 10; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -1081,14 +826,14 @@ void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t num EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs); } -TEST_P(BinderRpc, ThreadPoolOverSaturated) { +TEST_P(BinderRpcThreads, ThreadPoolOverSaturated) { constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = kNumThreads + 3; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); testThreadPoolOverSaturated(proc.rootIface, kNumCalls); } -TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { +TEST_P(BinderRpcThreads, ThreadPoolLimitOutgoing) { constexpr size_t kNumThreads = 20; constexpr size_t kNumOutgoingConnections = 10; constexpr size_t kNumCalls = kNumOutgoingConnections + 3; @@ -1097,7 +842,7 @@ TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { testThreadPoolOverSaturated(proc.rootIface, kNumCalls); } -TEST_P(BinderRpc, ThreadingStressTest) { +TEST_P(BinderRpcThreads, ThreadingStressTest) { constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 100; @@ -1126,7 +871,7 @@ static void saturateThreadPool(size_t threadCount, const sp<IBinderRpcTest>& ifa for (auto& t : threads) t.join(); } -TEST_P(BinderRpc, OnewayStressTest) { +TEST_P(BinderRpcThreads, OnewayStressTest) { constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 1000; @@ -1161,7 +906,7 @@ TEST_P(BinderRpc, OnewayCallDoesNotWait) { EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs); } -TEST_P(BinderRpc, OnewayCallQueueing) { +TEST_P(BinderRpcThreads, OnewayCallQueueing) { constexpr size_t kNumSleeps = 10; constexpr size_t kNumExtraServerThreads = 4; constexpr size_t kSleepMs = 50; @@ -1185,12 +930,12 @@ TEST_P(BinderRpc, OnewayCallQueueing) { size_t epochMsAfter = epochMillis(); - EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); + EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface); } -TEST_P(BinderRpc, OnewayCallExhaustion) { +TEST_P(BinderRpcThreads, OnewayCallExhaustion) { constexpr size_t kNumClients = 2; constexpr size_t kTooLongMs = 1000; @@ -1233,11 +978,21 @@ TEST_P(BinderRpc, OnewayCallExhaustion) { TEST_P(BinderRpc, Callbacks) { const static std::string kTestString = "good afternoon!"; + bool bothSingleThreaded = !kEnableRpcThreads || singleThreaded(); + for (bool callIsOneway : {true, false}) { for (bool callbackIsOneway : {true, false}) { for (bool delayed : {true, false}) { + if (bothSingleThreaded && (callIsOneway || callbackIsOneway || delayed)) { + // we have no incoming connections to receive the callback + continue; + } + + size_t numIncomingConnections = bothSingleThreaded ? 0 : 1; auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + {.numThreads = 1, + .numSessions = 1, + .numIncomingConnections = numIncomingConnections}); auto cb = sp<MyBinderRpcCallback>::make(); if (callIsOneway) { @@ -1253,7 +1008,7 @@ TEST_P(BinderRpc, Callbacks) { // the callback will be processed on another thread. if (callIsOneway || callbackIsOneway || delayed) { using std::literals::chrono_literals::operator""s; - std::unique_lock<std::mutex> _l(cb->mMutex); + RpcMutexUniqueLock _l(cb->mMutex); cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); } @@ -1274,13 +1029,131 @@ TEST_P(BinderRpc, Callbacks) { // since this session has an incoming connection w/ a threadpool, we // need to manually shut it down EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); - proc.expectAlreadyShutdown = true; } } } } +TEST_P(BinderRpc, SingleDeathRecipient) { + if (singleThreaded() || !kEnableRpcThreads) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); + } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + std::unique_lock<std::mutex> lock(dr->mMtx); + if (!dr->dead) { + EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms)); + } + EXPECT_TRUE(dr->dead) << "Failed to receive the death notification."; + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) { + if (singleThreaded() || !kEnableRpcThreads) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); + } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + + // Explicitly calling shutDownAndWait will cause the death recipients + // to be called. + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + + std::unique_lock<std::mutex> lock(dr->mMtx); + if (!dr->dead) { + EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms)); + } + EXPECT_TRUE(dr->dead) << "Failed to receive the death notification."; + + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, DeathRecipientFatalWithoutIncoming) { + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override {} + }; + + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 0}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_DEATH(proc.rootBinder->linkToDeath(dr, (void*)1, 0), + "Cannot register a DeathRecipient without any incoming connections."); +} + +TEST_P(BinderRpc, UnlinkDeathRecipient) { + if (singleThreaded() || !kEnableRpcThreads) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + GTEST_FAIL() << "This should not be called after unlinkToDeath"; + } + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + ASSERT_EQ(OK, proc.rootBinder->unlinkToDeath(dr, (void*)1, 0, nullptr)); + + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; +} + TEST_P(BinderRpc, OnewayCallbackWithNoThread) { auto proc = createRpcTestSocketServerProcess({}); auto cb = sp<MyBinderRpcCallback>::make(); @@ -1304,33 +1177,185 @@ TEST_P(BinderRpc, Die) { EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) << "Do death cleanup: " << doDeathCleanup; + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } } TEST_P(BinderRpc, UseKernelBinderCallingId) { - bool okToFork = ProcessState::selfOrNull() == nullptr; + // This test only works if the current process shared the internal state of + // ProcessState with the service across the call to fork(). Both the static + // libraries and libbinder.so have their own separate copies of all the + // globals, so the test only works when the test client and service both use + // libbinder.so (when using static libraries, even a client and service + // using the same kind of static library should have separate copies of the + // variables). + if (!kEnableSharedLibs || singleThreaded() || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } auto proc = createRpcTestSocketServerProcess({}); - // If this process has used ProcessState already, then the forked process - // cannot use it at all. If this process hasn't used it (depending on the - // order tests are run), then the forked process can use it, and we'll only - // catch the invalid usage the second time. Such is the burden of global - // state! - if (okToFork) { - // we can't allocate IPCThreadState so actually the first time should - // succeed :( - EXPECT_OK(proc.rootIface->useKernelBinderCallingId()); - } + // 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.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } +TEST_P(BinderRpc, FileDescriptorTransportRejectNone) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE, + RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + EXPECT_EQ(status.transactionError(), FDS_NOT_ALLOWED) << status; +} + +TEST_P(BinderRpc, ReceiveFile) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "hello"); +} + +TEST_P(BinderRpc, SendFiles) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("123"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("b"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("cd"))); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "123abcd"); +} + +TEST_P(BinderRpc, SendMaxFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 253; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, std::string(253, 'a')); +} + +TEST_P(BinderRpc, SendTooManyFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 254; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; +} + TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1340,6 +1365,10 @@ TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { } TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1364,7 +1393,7 @@ ssize_t countFds() { return ret; } -TEST_P(BinderRpc, Fds) { +TEST_P(BinderRpcThreads, Fds) { ssize_t beforeFds = countFds(); ASSERT_GE(beforeFds, 0); { @@ -1387,20 +1416,90 @@ TEST_P(BinderRpc, AidlDelegatorTest) { static bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); - sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make()); - if (status_t status = server->setupVsockServer(vsockPort); status != OK) { - if (status == -EAFNOSUPPORT) { - return false; + + android::base::unique_fd serverFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno)); + + sockaddr_vm serverAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_ANY, + }; + int ret = TEMP_FAILURE_RETRY( + bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr))); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort, + strerror(errno)); + + ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/)); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort, + strerror(errno)); + + // Try to connect to the server using the VMADDR_CID_LOCAL cid + // to see if the kernel supports it. It's safe to use a blocking + // connect because vsock sockets have a 2 second connection timeout, + // and they return ETIMEDOUT after that. + android::base::unique_fd connectFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort, + strerror(errno)); + + bool success = false; + sockaddr_vm connectAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_LOCAL, + }; + ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr), + sizeof(connectAddr))); + if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) { + android::base::unique_fd acceptFd; + while (true) { + pollfd pfd[]{ + {.fd = serverFd.get(), .events = POLLIN, .revents = 0}, + {.fd = connectFd.get(), .events = POLLOUT, .revents = 0}, + }; + ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno)); + + if (pfd[0].revents & POLLIN) { + sockaddr_vm acceptAddr; + socklen_t acceptAddrLen = sizeof(acceptAddr); + ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(), + reinterpret_cast<sockaddr*>(&acceptAddr), + &acceptAddrLen, SOCK_CLOEXEC)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno)); + LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)), + "Truncated address"); + + // Store the fd in acceptFd so we keep the connection alive + // while polling connectFd + acceptFd.reset(ret); + } + + if (pfd[1].revents & POLLOUT) { + // Connect either succeeded or timed out + int connectErrno; + socklen_t connectErrnoLen = sizeof(connectErrno); + int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno, + &connectErrnoLen); + LOG_ALWAYS_FATAL_IF(ret == -1, + "Could not getsockopt() after connect() " + "on non-blocking socket: %s.", + strerror(errno)); + + // We're done, this is all we wanted + success = connectErrno == 0; + break; + } } - LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str()); + } else { + success = ret == 0; } - server->start(); - sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); - status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); - while (!server->shutdown()) usleep(10000); - ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str()); - return status == OK; + ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no"); + + return success; } static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { @@ -1417,9 +1516,31 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } +static std::vector<uint32_t> testVersions() { + std::vector<uint32_t> versions; + for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) { + versions.push_back(i); + } + versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + return versions; +} + INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::Combine(::testing::ValuesIn(testSocketTypes()), - ::testing::ValuesIn(RpcSecurityValues())), + ::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions()), + ::testing::ValuesIn(testVersions()), + ::testing::Values(false, true), + ::testing::Values(false, true)), + BinderRpc::PrintParamInfo); + +INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpcThreads, + ::testing::Combine(::testing::ValuesIn(testSocketTypes()), + ::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions()), + ::testing::ValuesIn(testVersions()), + ::testing::Values(false), + ::testing::Values(false, true)), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1473,9 +1594,14 @@ private: bool mValue = false; }; -TEST_P(BinderRpcSimple, Shutdown) { +TEST_P(BinderRpcServerOnly, Shutdown) { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + auto addr = allocateSocketAddress(); - auto server = RpcServer::make(newFactory(GetParam())); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared<OneOffSignal>(); @@ -1504,6 +1630,11 @@ TEST(BinderRpc, Java) { "createRpcDelegateServiceManager() with a device attached, such test belongs " "to binderHostDeviceTest. Hence, just disable this test on host."; #endif // !__ANDROID__ + if constexpr (!kEnableKernelIpc) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + sp<IServiceManager> sm = defaultServiceManager(); ASSERT_NE(nullptr, sm); // Any Java service with non-empty getInterfaceDescriptor() would do. @@ -1545,12 +1676,17 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } -INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), - BinderRpcSimple::PrintTestParam); +INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, + ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions())), + BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: - using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; + // Only parameterized only server version because `RpcSession` is bypassed + // in the client half of the tests. + using Param = + std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. @@ -1562,8 +1698,9 @@ public: [[nodiscard]] AssertionResult setUp( const Param& param, std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); + rpcServer->setProtocolVersion(serverVersion); switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; @@ -1682,7 +1819,7 @@ public: std::string message(kMessage); iovec messageIov{message.data(), message.size()}; auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, - std::nullopt); + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); return AssertionSuccess(); } @@ -1693,7 +1830,8 @@ public: explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} Client(Client&&) = default; [[nodiscard]] AssertionResult setUp(const Param& param) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; + (void)serverVersion; mFdTrigger = FdTrigger::make(); mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; @@ -1716,7 +1854,7 @@ public: iovec readMessageIov{readMessage.data(), readMessage.size()}; status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1, - std::nullopt); + std::nullopt, nullptr); if (readStatus != OK) { return AssertionFailure() << statusToString(readStatus); } @@ -1764,23 +1902,28 @@ public: using Server = RpcTransportTestUtils::Server; using Client = RpcTransportTestUtils::Client; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, rpcSecurity, certificateFormat] = info.param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param; auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString(); if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat); + ret += "_serverV" + std::to_string(serverVersion); return ret; } static std::vector<ParamType> getRpcTranportTestParams() { std::vector<ParamType> ret; - for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { - for (auto rpcSecurity : RpcSecurityValues()) { - switch (rpcSecurity) { - case RpcSecurity::RAW: { - ret.emplace_back(socketType, rpcSecurity, std::nullopt); - } break; - case RpcSecurity::TLS: { - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM); - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER); - } break; + for (auto serverVersion : testVersions()) { + for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { + for (auto rpcSecurity : RpcSecurityValues()) { + switch (rpcSecurity) { + case RpcSecurity::RAW: { + ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion); + } break; + case RpcSecurity::TLS: { + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM, + serverVersion); + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER, + serverVersion); + } break; + } } } } @@ -1788,9 +1931,15 @@ public: } template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b); } + void SetUp() override { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + } }; TEST_P(RpcTransportTest, GoodCertificate) { @@ -1824,7 +1973,8 @@ TEST_P(RpcTransportTest, MultipleClients) { } TEST_P(RpcTransportTest, UntrustedServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; auto untrustedServer = std::make_unique<Server>(); ASSERT_TRUE(untrustedServer->setUp(GetParam())); @@ -1842,7 +1992,9 @@ TEST_P(RpcTransportTest, UntrustedServer) { client.run(handshakeOk); } TEST_P(RpcTransportTest, MaliciousServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto validServer = std::make_unique<Server>(); ASSERT_TRUE(validServer->setUp(GetParam())); @@ -1865,7 +2017,9 @@ TEST_P(RpcTransportTest, MaliciousServer) { } TEST_P(RpcTransportTest, UntrustedClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1884,7 +2038,9 @@ TEST_P(RpcTransportTest, UntrustedClient) { } TEST_P(RpcTransportTest, MaliciousClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1911,8 +2067,8 @@ TEST_P(RpcTransportTest, Trigger) { auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) { std::string message(RpcTransportTestUtils::kMessage); iovec messageIov{message.data(), message.size()}; - auto status = - serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, std::nullopt); + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); { @@ -1923,7 +2079,8 @@ TEST_P(RpcTransportTest, Trigger) { } iovec msg2Iov{msg2.data(), msg2.size()}; - status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt); + status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt, + nullptr); if (status != DEAD_OBJECT) return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully " "should return DEAD_OBJECT, but it is " @@ -1970,23 +2127,28 @@ INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest - : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { + : public testing::TestWithParam< + std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, certificateFormat, keyFormat] = info.param; - auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + - "_key_" + PrintToString(keyFormat); - return ret; + auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; + return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { @@ -2001,8 +2163,8 @@ TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat); auto desCert = deserializeCertificate(certData, certificateFormat); auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert)); - auto utilsParam = - std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat)); + auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, + std::make_optional(certificateFormat), serverVersion); auto server = std::make_unique<RpcTransportTestUtils::Server>(); ASSERT_TRUE(server->setUp(utilsParam, std::move(auth))); @@ -2021,7 +2183,8 @@ INSTANTIATE_TEST_CASE_P( BinderRpc, RpcTransportTlsKeyTest, testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)), testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER), - testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)), + testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER), + testing::ValuesIn(testVersions())), RpcTransportTlsKeyTest::PrintParamInfo); } // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp new file mode 100644 index 0000000000..0d9aa952eb --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.cpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#include "binderRpcTestCommon.h" + +namespace android { + +std::atomic<int32_t> MyBinderRpcSession::gNum; +sp<IBinder> MyBinderRpcTest::mHeldBinder; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h new file mode 100644 index 0000000000..4513d36a65 --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -0,0 +1,379 @@ +/* + * 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 <BinderRpcTestClientInfo.h> +#include <BinderRpcTestServerConfig.h> +#include <BinderRpcTestServerInfo.h> +#include <BnBinderRpcCallback.h> +#include <BnBinderRpcSession.h> +#include <BnBinderRpcTest.h> +#include <aidl/IBinderRpcTest.h> +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/binder_auto_utils.h> +#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> +#include <binder/RpcSession.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTlsTestUtils.h> +#include <binder/RpcTlsUtils.h> +#include <binder/RpcTransport.h> +#include <binder/RpcTransportRaw.h> +#include <binder/RpcTransportTls.h> +#include <unistd.h> +#include <string> +#include <vector> + +#include <signal.h> + +#include "../BuildFlags.h" +#include "../FdTrigger.h" +#include "../RpcSocketAddress.h" // for testing preconnected clients +#include "../RpcState.h" // for debugging +#include "../vm_sockets.h" // for VMADDR_* +#include "utils/Errors.h" + +namespace android { + +constexpr char kLocalInetAddress[] = "127.0.0.1"; + +enum class RpcSecurity { RAW, TLS }; + +static inline std::vector<RpcSecurity> RpcSecurityValues() { + return {RpcSecurity::RAW, RpcSecurity::TLS}; +} + +enum class SocketType { + PRECONNECTED, + UNIX, + VSOCK, + INET, +}; +static inline std::string PrintToString(SocketType socketType) { + switch (socketType) { + case SocketType::PRECONNECTED: + return "preconnected_uds"; + case SocketType::UNIX: + return "unix_domain_socket"; + case SocketType::VSOCK: + return "vm_socket"; + case SocketType::INET: + return "inet_socket"; + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + return ""; + } +} + +struct BinderRpcOptions { + size_t numThreads = 1; + size_t numSessions = 1; + size_t numIncomingConnections = 0; + size_t numOutgoingConnections = SIZE_MAX; + RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode = + RpcSession::FileDescriptorTransportMode::NONE; + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes = { + RpcSession::FileDescriptorTransportMode::NONE}; + + // If true, connection failures will result in `ProcessSession::sessions` being empty + // instead of a fatal error. + bool allowConnectFailure = false; +}; + +static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { + uint64_t length = str.length(); + CHECK(android::base::WriteFully(fd, &length, sizeof(length))); + CHECK(android::base::WriteFully(fd, str.data(), str.length())); +} + +static inline std::string readString(android::base::borrowed_fd fd) { + uint64_t length; + CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + std::string ret(length, '\0'); + CHECK(android::base::ReadFully(fd, ret.data(), length)); + return ret; +} + +static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { + Parcel parcel; + CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); +} + +template <typename T> +static inline T readFromFd(android::base::borrowed_fd fd) { + std::string data = readString(fd); + Parcel parcel; + CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); + T object; + CHECK_EQ(OK, object.readFromParcel(&parcel)); + return object; +} + +static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( + RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, + std::unique_ptr<RpcAuth> auth = nullptr) { + switch (rpcSecurity) { + case RpcSecurity::RAW: + return RpcTransportCtxFactoryRaw::make(); + case RpcSecurity::TLS: { + if (verifier == nullptr) { + verifier = std::make_shared<RpcCertificateVerifierSimple>(); + } + if (auth == nullptr) { + auth = std::make_unique<RpcAuthSelfSigned>(); + } + return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); + } + default: + LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); + } +} + +// Create an FD that returns `contents` when read. +static inline base::unique_fd mockFileDescriptor(std::string contents) { + android::base::unique_fd readFd, writeFd; + CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno); + RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() { + signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write + if (!WriteStringToFd(contents, writeFd)) { + int savedErrno = errno; + LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s", + strerror(savedErrno)); + } + }).detach(); + return readFd; +} + +using android::binder::Status; + +class MyBinderRpcSession : public BnBinderRpcSession { +public: + static std::atomic<int32_t> gNum; + + MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } + Status getName(std::string* name) override { + *name = mName; + return Status::ok(); + } + ~MyBinderRpcSession() { gNum--; } + +private: + std::string mName; +}; + +class MyBinderRpcCallback : public BnBinderRpcCallback { + Status sendCallback(const std::string& value) { + RpcMutexUniqueLock _l(mMutex); + mValues.push_back(value); + _l.unlock(); + mCv.notify_one(); + return Status::ok(); + } + Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } + +public: + RpcMutex mMutex; + RpcConditionVariable mCv; + std::vector<std::string> mValues; +}; + +class MyBinderRpcTest : public BnBinderRpcTest { +public: + wp<RpcServer> server; + int port = 0; + + Status sendString(const std::string& str) override { + (void)str; + return Status::ok(); + } + Status doubleString(const std::string& str, std::string* strstr) override { + *strstr = str + str; + return Status::ok(); + } + Status getClientPort(int* out) override { + *out = port; + return Status::ok(); + } + Status countBinders(std::vector<int32_t>* out) override { + sp<RpcServer> spServer = server.promote(); + if (spServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + out->clear(); + for (auto session : spServer->listSessions()) { + size_t count = session->state()->countBinders(); + out->push_back(count); + } + return Status::ok(); + } + Status getNullBinder(sp<IBinder>* out) override { + out->clear(); + return Status::ok(); + } + Status pingMe(const sp<IBinder>& binder, int32_t* out) override { + if (binder == nullptr) { + std::cout << "Received null binder!" << std::endl; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + *out = binder->pingBinder(); + return Status::ok(); + } + Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { + *out = binder; + return Status::ok(); + } + static sp<IBinder> mHeldBinder; + Status holdBinder(const sp<IBinder>& binder) override { + mHeldBinder = binder; + return Status::ok(); + } + Status getHeldBinder(sp<IBinder>* held) override { + *held = mHeldBinder; + return Status::ok(); + } + Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { + if (count <= 0) return Status::ok(); + return binder->nestMe(this, count - 1); + } + Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { + static sp<IBinder> binder = new BBinder; + *out = binder; + return Status::ok(); + } + Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { + *out = new MyBinderRpcSession(name); + return Status::ok(); + } + Status getNumOpenSessions(int32_t* out) override { + *out = MyBinderRpcSession::gNum; + return Status::ok(); + } + + RpcMutex blockMutex; + Status lock() override { + blockMutex.lock(); + return Status::ok(); + } + Status unlockInMsAsync(int32_t ms) override { + usleep(ms * 1000); + blockMutex.unlock(); + return Status::ok(); + } + Status lockUnlock() override { + RpcMutexLockGuard _l(blockMutex); + return Status::ok(); + } + + Status sleepMs(int32_t ms) override { + usleep(ms * 1000); + return Status::ok(); + } + + Status sleepMsAsync(int32_t ms) override { + // In-process binder calls are asynchronous, but the call to this method + // is synchronous wrt its client. This in/out-process threading model + // diffentiation is a classic binder leaky abstraction (for better or + // worse) and is preserved here the way binder sockets plugs itself + // into BpBinder, as nothing is changed at the higher levels + // (IInterface) which result in this behavior. + return sleepMs(ms); + } + + Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + if (callback == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + if (delayed) { + RpcMaybeThread([=]() { + ALOGE("Executing delayed callback: '%s'", value.c_str()); + Status status = doCallback(callback, oneway, false, value); + ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); + }).detach(); + return Status::ok(); + } + + if (oneway) { + return callback->sendOnewayCallback(value); + } + + return callback->sendCallback(value); + } + + Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + return doCallback(callback, oneway, delayed, value); + } + + Status die(bool cleanup) override { + if (cleanup) { + exit(1); + } else { + _exit(1); + } + } + + Status scheduleShutdown() override { + sp<RpcServer> strongServer = server.promote(); + if (strongServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + RpcMaybeThread([=] { + LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); + }).detach(); + return Status::ok(); + } + + 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! + + if constexpr (kEnableKernelIpc) { + (void)IPCThreadState::self()->getCallingPid(); + } + return Status::ok(); + } + + Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override { + out->reset(mockFileDescriptor(content)); + return Status::ok(); + } + + Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files, + android::os::ParcelFileDescriptor* out) override { + std::string acc; + for (const auto& file : files) { + std::string result; + CHECK(android::base::ReadFdToString(file.get(), &result)); + acc.append(result); + } + out->reset(mockFileDescriptor(acc)); + return Status::ok(); + } +}; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp new file mode 100644 index 0000000000..31eb5dadf1 --- /dev/null +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#include "binderRpcTestCommon.h" + +using namespace android; + +int main(int argc, const char* argv[]) { + LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); + base::unique_fd writeEnd(atoi(argv[1])); + base::unique_fd readEnd(atoi(argv[2])); + + auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd); + auto socketType = static_cast<SocketType>(serverConfig.socketType); + auto rpcSecurity = static_cast<RpcSecurity>(serverConfig.rpcSecurity); + + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes; + for (auto mode : serverConfig.serverSupportedFileDescriptorTransportModes) { + serverSupportedFileDescriptorTransportModes.push_back( + static_cast<RpcSession::FileDescriptorTransportMode>(mode)); + } + + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); + + server->setProtocolVersion(serverConfig.serverVersion); + server->setMaxThreads(serverConfig.numThreads); + server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); + + unsigned int outPort = 0; + + switch (socketType) { + case SocketType::PRECONNECTED: + [[fallthrough]]; + case SocketType::UNIX: + CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str())) + << serverConfig.addr; + break; + case SocketType::VSOCK: + CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort)); + break; + case SocketType::INET: { + CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); + CHECK_NE(0, outPort); + break; + } + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + } + + BinderRpcTestServerInfo serverInfo; + serverInfo.port = static_cast<int64_t>(outPort); + serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); + writeToFd(writeEnd, serverInfo); + auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); + + if (rpcSecurity == RpcSecurity::TLS) { + for (const auto& clientCert : clientInfo.certs) { + CHECK_EQ(OK, + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); + } + } + + server->setPerSessionRootObject([&](const void* addrPtr, size_t len) { + // UNIX sockets with abstract addresses return + // sizeof(sa_family_t)==2 in addrlen + CHECK_GE(len, sizeof(sa_family_t)); + const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr); + sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); + switch (addr->sa_family) { + case AF_UNIX: + // nothing to save + break; + case AF_VSOCK: + CHECK_EQ(len, sizeof(sockaddr_vm)); + service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port; + break; + case AF_INET: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port); + break; + case AF_INET6: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized address family %d", addr->sa_family); + } + service->server = server; + return service; + }); + + server->join(); + + // Another thread calls shutdown. Wait for it to complete. + (void)server->shutdown(); + + return 0; +} diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 4fcf42d6e5..3dab2c748b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -233,11 +233,15 @@ const std::string kCurrentRepr = "0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|" "07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000"; +TEST(RpcWire, V0) { + checkRepr(kCurrentRepr, 0); +} + TEST(RpcWire, CurrentVersion) { checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION); } -static_assert(RPC_WIRE_PROTOCOL_VERSION == 0, +static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL, "If the binder wire protocol is updated, this test should test additional versions. " "The binder wire protocol should only be updated on upstream AOSP."); diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 2ca6ebdbd2..0210237ed8 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -7,6 +7,22 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "binderReadParcelIface", + host_supported: true, + unstable: true, + srcs: [ + "EmptyParcelable.aidl", + "SingleDataParcelable.aidl", + "GenericDataParcelable.aidl", + ], + backend: { + java: { + enabled: false, + }, + }, +} + cc_fuzz { name: "binder_parcel_fuzzer", host_supported: true, @@ -29,6 +45,8 @@ cc_fuzz { "libcutils", "libhidlbase", "liblog", + "binderReadParcelIface-cpp", + "binderReadParcelIface-ndk", ], target: { diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl new file mode 100644 index 0000000000..96d6223d3d --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl @@ -0,0 +1,18 @@ +/* + * 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. + */ + +parcelable EmptyParcelable{ +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl new file mode 100644 index 0000000000..fc2542b36c --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl @@ -0,0 +1,24 @@ +/* + * 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. + */ + +parcelable GenericDataParcelable { + int data; + float majorVersion; + float minorVersion; + IBinder binder; + ParcelFileDescriptor fileDescriptor; + int[] array; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl new file mode 100644 index 0000000000..d62891b26a --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl @@ -0,0 +1,19 @@ +/* + * 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. + */ + +parcelable SingleDataParcelable{ + int data; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 7059d30bb4..9dac2c98a7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder" #include "binder.h" +#include "EmptyParcelable.h" +#include "GenericDataParcelable.h" +#include "SingleDataParcelable.h" #include "util.h" #include <android-base/hex.h> @@ -354,6 +357,24 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable"; + EmptyParcelable emptyParcelable{}; + status_t status = emptyParcelable.readFromParcel(&p); + FUZZ_LOG() << " status: " << status; + }, + [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable"; + SingleDataParcelable singleDataParcelable; + status_t status = singleDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable"; + GenericDataParcelable genericDataParcelable; + status_t status = genericDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, }; // clang-format on #pragma clang diagnostic pop diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 26d67704b2..af773a02f7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder_ndk" #include "binder_ndk.h" +#include "aidl/EmptyParcelable.h" +#include "aidl/GenericDataParcelable.h" +#include "aidl/SingleDataParcelable.h" #include <android/binder_parcel_utils.h> #include <android/binder_parcelable_utils.h> @@ -177,5 +180,24 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData), PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData), #undef COMMA + + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable"; + aidl::EmptyParcelable emptyParcelable; + binder_status_t status = emptyParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable"; + aidl::SingleDataParcelable singleDataParcelable; + binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable"; + aidl::GenericDataParcelable genericDataParcelable; + binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, }; // clang-format on diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 843b6e3011..6ea970825b 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h @@ -19,10 +19,14 @@ #include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> +#include <vector> + namespace android { // always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -base::unique_fd getRandomFd(FuzzedDataProvider* provider); +// +// may return multiple FDs (e.g. pipe), but will always return at least one +std::vector<base::unique_fd> getRandomFds(FuzzedDataProvider* provider); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 459fb127c2..27587a9162 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -33,10 +33,13 @@ struct RandomParcelOptions { /** * Fill parcel data, including some random binder objects and FDs * + * May insert additional FDs/binders if they own data related to the Parcel (e.g. the other + * end of a pipe). + * * p - the Parcel to fill * provider - takes ownership and completely consumes provider * writeHeader - optional function to write a specific header once the format of the parcel is * picked (for instance, to write an interface header) */ -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index d5aa353af0..32494e3f77 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -44,7 +44,7 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); - fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options); Parcel reply; (void)target->transact(code, data, &reply, flags); diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index 180a177689..bef4ab6e99 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -35,17 +35,22 @@ #include <sys/time.h> using android::fillRandomParcel; +using android::RandomParcelOptions; using android::sp; using android::base::HexString; -void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) { +void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // TODO: functionality to create random parcels for libhwbinder parcels + (void)options; + std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>(); p->setData(input.data(), input.size()); } -static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) { +static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // fill underlying parcel using functions to fill random libbinder parcel - fillRandomParcel(p->parcel(), std::move(provider)); + fillRandomParcel(p->parcel(), std::move(provider), options); } template <typename P, typename B> @@ -55,9 +60,11 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider FUZZ_LOG() << "backend: " << backend; + RandomParcelOptions options; + P reply; P data; - fillRandomParcel(&data, std::move(provider)); + fillRandomParcel(&data, std::move(provider), &options); (void)binder->transact(code, data, &reply, flag); } @@ -73,8 +80,10 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, maxInstructions)); + RandomParcelOptions options; + P p; - fillRandomParcel(&p, std::move(provider)); + fillRandomParcel(&p, std::move(provider), &options); // since we are only using a byte to index CHECK(reads.size() <= 255) << reads.size(); @@ -103,9 +112,12 @@ void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) { std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + // same options so that FDs and binders could be shared in both Parcels + RandomParcelOptions options; + P p0, p1; - fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size())); - fillRandomParcel(&p1, std::move(provider)); + fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options); + fillRandomParcel(&p1, std::move(provider), &options); FUZZ_LOG() << "backend: " << backend; FUZZ_LOG() << "start: " << start << " len: " << len; diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index ab0b7e306f..3fcf104e81 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,44 @@ namespace android { -base::unique_fd getRandomFd(FuzzedDataProvider* provider) { - int fd = provider->PickValueInArray<std::function<int()>>({ - []() { return ashmem_create_region("binder test region", 1024); }, - []() { return open("/dev/null", O_RDWR); }, +using base::unique_fd; + +std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) { + std::vector<unique_fd> fds = provider->PickValueInArray< + std::function<std::vector<unique_fd>()>>({ + [&]() { + std::vector<unique_fd> ret; + ret.push_back(unique_fd( + ashmem_create_region("binder test region", + provider->ConsumeIntegralInRange<size_t>(0, 4096)))); + return ret; + }, + [&]() { + std::vector<unique_fd> ret; + ret.push_back(unique_fd(open("/dev/null", O_RDWR))); + return ret; + }, + [&]() { + int pipefds[2]; + + int flags = O_CLOEXEC; + if (provider->ConsumeBool()) flags |= O_DIRECT; + if (provider->ConsumeBool()) flags |= O_NONBLOCK; + + CHECK_EQ(0, pipe2(pipefds, flags)); + + if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]); + + std::vector<unique_fd> ret; + ret.push_back(unique_fd(pipefds[0])); + ret.push_back(unique_fd(pipefds[1])); + return ret; + }, })(); - CHECK(fd >= 0); - return base::unique_fd(fd); + + for (const auto& fd : fds) CHECK(fd.ok()) << fd.get(); + + return fds; } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 0204f5ed61..51cb768d3d 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -39,23 +39,24 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { CHECK(OK == p->write(data.data(), data.size())); } -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, - const RandomParcelOptions& options) { +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { + CHECK_NE(options, nullptr); + if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } fillRandomParcelData(p, std::move(provider)); return; } - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } while (provider.remaining_bytes() > 0) { @@ -69,15 +70,21 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, }, // write FD [&]() { - if (options.extraFds.size() > 0 && provider.ConsumeBool()) { - const base::unique_fd& fd = options.extraFds.at( + if (options->extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange<size_t>(0, - options.extraFds.size() - + options->extraFds.size() - 1)); CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/)); } else { - base::unique_fd fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/)); + std::vector<base::unique_fd> fds = getRandomFds(&provider); + CHECK(OK == + p->writeFileDescriptor(fds.begin()->release(), + true /*takeOwnership*/)); + + options->extraFds.insert(options->extraFds.end(), + std::make_move_iterator(fds.begin() + 1), + std::make_move_iterator(fds.end())); } }, // write binder @@ -98,10 +105,10 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, return IInterface::asBinder(defaultServiceManager()); }, [&]() -> sp<IBinder> { - if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { - return options.extraBinders.at( + if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { + return options->extraBinders.at( provider.ConsumeIntegralInRange< - size_t>(0, options.extraBinders.size() - 1)); + size_t>(0, options->extraBinders.size() - 1)); } else { return nullptr; } diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp new file mode 100644 index 0000000000..187add4a7b --- /dev/null +++ b/libs/binder/trusty/OS.cpp @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#include <openssl/rand.h> + +#include "../OS.h" + +using android::base::Result; + +namespace android { + +Result<void> setNonBlocking(android::base::borrowed_fd fd) { + // Trusty IPC syscalls are all non-blocking by default. + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { + int res = RAND_bytes(data, size); + return res == 1 ? OK : UNKNOWN_ERROR; +} + +} // namespace android diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md new file mode 100644 index 0000000000..1a273aab10 --- /dev/null +++ b/libs/binder/trusty/README.md @@ -0,0 +1,39 @@ +# Binder for Trusty + +This is the Trusty port of the libbinder library. +To build it, take the following steps: + +* Check out copies of the Trusty and AOSP repositories. +* Apply the patches from the `trusty_binder` topic on both repositories. +* Build Trusty normally using `build.py`. +* Run the sample AIDL test for Trusty: + ```shell + $ ./build-root/.../run --headless --boot-test com.android.trusty.aidl.test + ``` + +To run the Android-Trusty IPC test, do the following: + +* Build AOSP for the `qemu_trusty_arm64-userdebug` target: + ```shell + $ lunch qemu_trusty_arm64-userdebug + $ m + ``` +* In the Trusty directory, run the emulator with the newly built Android: + ```shell + $ ./build-root/.../run --android /path/to/aosp + ``` +* Using either `adb` or the shell inside the emulator itself, run the Trusty + Binder test as root: + ```shell + # /data/nativetest64/vendor/trusty_binder_test/trusty_binder_test + ``` + +## Running the AIDL compiler +For now, you will need to run the AIDL compiler manually to generate the C++ +source code for Trusty clients and services. The general syntax is: +```shell +$ aidl --lang=cpp -o <output directory> -h <output header directory> <AIDL files...> +``` + +The compiler will emit some `.cpp` files in the output directory and their +corresponding `.h` files in the header directory. diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp new file mode 100644 index 0000000000..e8b91e7a4c --- /dev/null +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -0,0 +1,142 @@ +/* + * 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 "RpcServerTrusty" + +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcServerTrusty.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +using android::base::unexpected; + +namespace android { + +android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { + // Default is without TLS. + if (rpcTransportCtxFactory == nullptr) + rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make(); + auto ctx = rpcTransportCtxFactory->newServerCtx(); + if (ctx == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl), + msgMaxSize); + if (srv == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps); + if (rc != NO_ERROR) { + return unexpected(rc); + } + return srv; +} + +RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize) + : mRpcServer(sp<RpcServer>::make(std::move(ctx))), + mPortName(std::move(portName)), + mPortAcl(std::move(portAcl)) { + mTipcPort.name = mPortName.c_str(); + mTipcPort.msg_max_size = msgMaxSize; + mTipcPort.msg_queue_len = 6; // Three each way + mTipcPort.priv = this; + + if (mPortAcl) { + // Initialize the array of pointers to uuids. + // The pointers in mUuidPtrs should stay valid across moves of + // RpcServerTrusty (the addresses of a std::vector's elements + // shouldn't change when the vector is moved), but would be invalidated + // by a copy which is why we disable the copy constructor and assignment + // operator for RpcServerTrusty. + auto numUuids = mPortAcl->uuids.size(); + mUuidPtrs.resize(numUuids); + for (size_t i = 0; i < numUuids; i++) { + mUuidPtrs[i] = &mPortAcl->uuids[i]; + } + + // Copy the contents of portAcl into the tipc_port_acl structure that we + // pass to tipc_add_service + mTipcPortAcl.flags = mPortAcl->flags; + mTipcPortAcl.uuid_num = numUuids; + mTipcPortAcl.uuids = mUuidPtrs.data(); + mTipcPortAcl.extra_data = mPortAcl->extraData; + + mTipcPort.acl = &mTipcPortAcl; + } else { + mTipcPort.acl = nullptr; + } +} + +int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, + void** ctx_p) { + auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv)); + server->mRpcServer->mShutdownTrigger = FdTrigger::make(); + server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread(); + + int rc = NO_ERROR; + auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) { + if (result.status != OK) { + rc = statusToTrusty(result.status); + return; + } + + /* Save the session for easy access */ + *ctx_p = session.get(); + }; + + base::unique_fd clientFd(chan); + std::array<uint8_t, RpcServer::kRpcAddressSize> addr; + constexpr size_t addrLen = sizeof(*peer); + memcpy(addr.data(), peer, addrLen); + RpcServer::establishConnection(sp(server->mRpcServer), std::move(clientFd), addr, addrLen, + joinFn); + + return rc; +} + +int RpcServerTrusty::handleMessage(const tipc_port* port, handle_t chan, void* ctx) { + auto* session = reinterpret_cast<RpcSession*>(ctx); + status_t status = session->state()->drainCommands(session->mConnections.mIncoming[0], session, + RpcState::CommandType::ANY); + if (status != OK) { + LOG_RPC_DETAIL("Binder connection thread closing w/ status %s", + statusToString(status).c_str()); + } + + return NO_ERROR; +} + +void RpcServerTrusty::handleDisconnect(const tipc_port* port, handle_t chan, void* ctx) {} + +void RpcServerTrusty::handleChannelCleanup(void* ctx) { + auto* session = reinterpret_cast<RpcSession*>(ctx); + auto& connection = session->mConnections.mIncoming.at(0); + LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection), + "bad state: connection object guaranteed to be in list"); +} + +} // namespace android diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp new file mode 100644 index 0000000000..e0d80fbe7b --- /dev/null +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -0,0 +1,248 @@ +/* + * 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 "RpcTransportTipcTrusty" + +#include <trusty_ipc.h> + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +namespace android { + +namespace { + +// RpcTransport for Trusty. +class RpcTransportTipcTrusty : public RpcTransport { +public: + explicit RpcTransportTipcTrusty(android::base::unique_fd socket) : mSocket(std::move(socket)) {} + ~RpcTransportTipcTrusty() { releaseMessage(); } + + status_t pollRead() override { + auto status = ensureMessage(false); + if (status != OK) { + return status; + } + return mHaveMessage ? OK : 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 { + if (niovs < 0) { + return BAD_VALUE; + } + + size_t size = 0; + for (int i = 0; i < niovs; i++) { + size += iovs[i].iov_len; + } + + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = 0, // TODO: add ancillaryFds + .handles = nullptr, + }; + int rc = send_msg(mSocket.get(), &msg); + if (rc == ERR_NOT_ENOUGH_BUFFER) { + // Peer is blocked, wait until it unblocks. + // TODO: when tipc supports a send-unblocked handler, + // save the message here in a queue and retry it asynchronously + // when the handler gets called by the library + uevent uevt; + do { + rc = ::wait(mSocket.get(), &uevt, INFINITE_TIME); + if (rc < 0) { + return statusFromTrusty(rc); + } + if (uevt.event & IPC_HANDLE_POLL_HUP) { + return DEAD_OBJECT; + } + } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); + + // Retry the send, it should go through this time because + // sending is now unblocked + rc = send_msg(mSocket.get(), &msg); + } + if (rc < 0) { + return statusFromTrusty(rc); + } + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size, + "Sent the wrong number of bytes %d!=%zu", rc, size); + + return OK; + } + + 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 { + if (niovs < 0) { + return BAD_VALUE; + } + + // If iovs has one or more empty vectors at the end and + // we somehow advance past all the preceding vectors and + // pass some or all of the empty ones to sendmsg/recvmsg, + // the call will return processSize == 0. In that case + // we should be returning OK but instead return DEAD_OBJECT. + // To avoid this problem, we make sure here that the last + // vector at iovs[niovs - 1] has a non-zero length. + while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { + niovs--; + } + if (niovs == 0) { + // The vectors are all empty, so we have nothing to read. + return OK; + } + + while (true) { + auto status = ensureMessage(true); + if (status != OK) { + return status; + } + + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = 0, // TODO: support ancillaryFds + .handles = nullptr, + }; + int rc = read_msg(mSocket.get(), mMessageInfo.id, mMessageOffset, &msg); + if (rc < 0) { + return statusFromTrusty(rc); + } + + size_t processSize = static_cast<size_t>(rc); + mMessageOffset += processSize; + LOG_ALWAYS_FATAL_IF(mMessageOffset > mMessageInfo.len, + "Message offset exceeds length %zu/%zu", mMessageOffset, + mMessageInfo.len); + + // Release the message if all of it has been read + if (mMessageOffset == mMessageInfo.len) { + releaseMessage(); + } + + while (processSize > 0 && niovs > 0) { + auto& iov = iovs[0]; + if (processSize < iov.iov_len) { + // Advance the base of the current iovec + iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; + iov.iov_len -= processSize; + break; + } + + // The current iovec was fully written + processSize -= iov.iov_len; + iovs++; + niovs--; + } + if (niovs == 0) { + LOG_ALWAYS_FATAL_IF(processSize > 0, + "Reached the end of iovecs " + "with %zd bytes remaining", + processSize); + return OK; + } + } + } + +private: + status_t ensureMessage(bool wait) { + int rc; + if (mHaveMessage) { + LOG_ALWAYS_FATAL_IF(mMessageOffset >= mMessageInfo.len, "No data left in message"); + return OK; + } + + /* TODO: interruptible wait, maybe with a timeout??? */ + uevent uevt; + rc = ::wait(mSocket.get(), &uevt, wait ? INFINITE_TIME : 0); + if (rc < 0) { + if (rc == ERR_TIMED_OUT && !wait) { + // If we timed out with wait==false, then there's no message + return OK; + } + return statusFromTrusty(rc); + } + if (!(uevt.event & IPC_HANDLE_POLL_MSG)) { + /* No message, terminate here and leave mHaveMessage false */ + return OK; + } + + rc = get_msg(mSocket.get(), &mMessageInfo); + if (rc < 0) { + return statusFromTrusty(rc); + } + + mHaveMessage = true; + mMessageOffset = 0; + return OK; + } + + void releaseMessage() { + if (mHaveMessage) { + put_msg(mSocket.get(), mMessageInfo.id); + mHaveMessage = false; + } + } + + base::unique_fd mSocket; + + bool mHaveMessage = false; + ipc_msg_info mMessageInfo; + size_t mMessageOffset; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcTrusty : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcTrusty>(std::move(fd)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +const char* RpcTransportCtxFactoryTipcTrusty::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcTrusty::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcTrusty>( + new RpcTransportCtxFactoryTipcTrusty()); +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.cpp b/libs/binder/trusty/TrustyStatus.cpp new file mode 100644 index 0000000000..b1caf612bc --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.cpp @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include "TrustyStatus.h" +#include "../RpcState.h" + +namespace android { + +status_t statusFromTrusty(int rc) { + LOG_RPC_DETAIL("Trusty error: %d", rc); + switch (rc) { + case NO_ERROR: + return OK; + case ERR_NOT_FOUND: + return NAME_NOT_FOUND; + case ERR_NOT_READY: + // We get this error if we try to perform an IPC operation when the + // channel is not ready + return INVALID_OPERATION; + case ERR_NO_MSG: + return WOULD_BLOCK; + case ERR_NO_MEMORY: + return NO_MEMORY; + case ERR_INVALID_ARGS: + return BAD_VALUE; + case ERR_NOT_ENOUGH_BUFFER: + return WOULD_BLOCK; + case ERR_TIMED_OUT: + return TIMED_OUT; + case ERR_ALREADY_EXISTS: + return ALREADY_EXISTS; + case ERR_CHANNEL_CLOSED: + return DEAD_OBJECT; + case ERR_NOT_ALLOWED: + return INVALID_OPERATION; + case ERR_NOT_SUPPORTED: + return INVALID_OPERATION; + case ERR_TOO_BIG: + return BAD_INDEX; + case ERR_CMD_UNKNOWN: + return UNKNOWN_TRANSACTION; + case ERR_BAD_STATE: + return INVALID_OPERATION; + case ERR_BAD_LEN: + return NOT_ENOUGH_DATA; + case ERR_BAD_HANDLE: + return BAD_VALUE; + case ERR_ACCESS_DENIED: + return PERMISSION_DENIED; + default: + return UNKNOWN_ERROR; + } +} + +int statusToTrusty(status_t status) { + switch (status) { + case OK: + return NO_ERROR; + case NO_MEMORY: + return ERR_NO_MEMORY; + case INVALID_OPERATION: + case BAD_VALUE: + case BAD_TYPE: + return ERR_NOT_VALID; + case NAME_NOT_FOUND: + return ERR_NOT_FOUND; + case PERMISSION_DENIED: + return ERR_ACCESS_DENIED; + case NO_INIT: + return ERR_NOT_CONFIGURED; + case ALREADY_EXISTS: + return ERR_ALREADY_EXISTS; + case DEAD_OBJECT: + return ERR_CHANNEL_CLOSED; + case BAD_INDEX: + return ERR_TOO_BIG; + case NOT_ENOUGH_DATA: + return ERR_BAD_LEN; + case WOULD_BLOCK: + return ERR_NO_MSG; + case TIMED_OUT: + return ERR_TIMED_OUT; + case UNKNOWN_TRANSACTION: + return ERR_CMD_UNKNOWN; + case FDS_NOT_ALLOWED: + return ERR_NOT_SUPPORTED; + case UNEXPECTED_NULL: + return ERR_NOT_VALID; + default: + return ERR_GENERIC; + } +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.h b/libs/binder/trusty/TrustyStatus.h new file mode 100644 index 0000000000..fcb43f8451 --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.h @@ -0,0 +1,26 @@ +/* + * 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 <uapi/err.h> +#include <utils/Errors.h> + +namespace android { + +status_t statusFromTrusty(int rc); +int statusToTrusty(status_t status); + +} // namespace android diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h new file mode 100644 index 0000000000..e8fc9f988d --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -0,0 +1,100 @@ +/* + * 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 <android-base/expected.h> +#include <android-base/macros.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransport.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <map> +#include <vector> + +#include <lib/tipc/tipc_srv.h> + +namespace android { + +/** + * This is the Trusty-specific RPC server code. + */ +class RpcServerTrusty final : public virtual RefBase { +public: + // C++ equivalent to tipc_port_acl that uses safe data structures instead of + // raw pointers, except for |extraData| which doesn't have a good + // equivalent. + struct PortAcl { + uint32_t flags; + std::vector<const uuid> uuids; + const void* extraData; + }; + + /** + * Creates an RPC server listening on the given port and adds it to the + * Trusty handle set at |handleSet|. + * + * The caller is responsible for calling tipc_run_event_loop() to start + * the TIPC event loop after creating one or more services here. + */ + static android::base::expected<sp<RpcServerTrusty>, int> make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, + std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr); + + void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); } + void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); } + void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); } + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) { + mRpcServer->setPerSessionRootObject(std::move(object)); + } + sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); } + +private: + // Both this class and RpcServer have multiple non-copyable fields, + // including mPortAcl below which can't be copied because mUuidPtrs + // holds pointers into it + DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty); + + friend sp<RpcServerTrusty>; + explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize); + + static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p); + static int handleMessage(const tipc_port* port, handle_t chan, void* ctx); + static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx); + static void handleChannelCleanup(void* ctx); + + static constexpr tipc_srv_ops kTipcOps = { + .on_connect = &handleConnect, + .on_message = &handleMessage, + .on_disconnect = &handleDisconnect, + .on_channel_cleanup = &handleChannelCleanup, + }; + + sp<RpcServer> mRpcServer; + std::string mPortName; + std::shared_ptr<const PortAcl> mPortAcl; + std::vector<const uuid*> mUuidPtrs; + tipc_port_acl mTipcPortAcl; + tipc_port mTipcPort; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h new file mode 100644 index 0000000000..8eae8c2ec1 --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h @@ -0,0 +1,41 @@ +/* + * 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 plain sockets. +// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory with TLS disabled. +class RpcTransportCtxFactoryTipcTrusty : 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: + RpcTransportCtxFactoryTipcTrusty() = default; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h new file mode 100644 index 0000000000..bf877a3179 --- /dev/null +++ b/libs/binder/trusty/include/log/log.h @@ -0,0 +1,122 @@ +/* + * 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 + +#define BINDER_LOG_LEVEL_NONE 0 +#define BINDER_LOG_LEVEL_NORMAL 1 +#define BINDER_LOG_LEVEL_VERBOSE 2 + +#ifndef BINDER_LOG_LEVEL +#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL +#endif // BINDER_LOG_LEVEL + +#ifndef TLOG_TAG +#ifdef LOG_TAG +#define TLOG_TAG "libbinder-" LOG_TAG +#else // LOG_TAG +#define TLOG_TAG "libbinder" +#endif // LOG_TAG +#endif // TLOG_TAG + +#include <stdlib.h> +#include <trusty_log.h> + +static inline void __ignore_va_args__(...) {} + +#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL +#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__) +#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) +#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__) +#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__) +#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL +#define ALOGD(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGI(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGW(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGE(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL + +#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE +#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO) +#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) +#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE +#define IF_ALOGV() if (false) +#define ALOGV(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE + +#define ALOGI_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGI(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define ALOGE_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGE(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define ALOGW_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGW(#cond ": " __VA_ARGS__); \ + } \ + } while (0) + +#define LOG_ALWAYS_FATAL(fmt, ...) \ + do { \ + TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ + } while (0) +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + do { \ + if (cond) { \ + LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define LOG_FATAL(fmt, ...) \ + do { \ + TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ + } while (0) +#define LOG_FATAL_IF(cond, ...) \ + do { \ + if (cond) { \ + LOG_FATAL(#cond ": " __VA_ARGS__); \ + } \ + } while (0) + +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) + +#define android_errorWriteLog(tag, subTag) \ + do { \ + TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \ + } while (0) diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp new file mode 100644 index 0000000000..fd54744fdf --- /dev/null +++ b/libs/binder/trusty/logging.cpp @@ -0,0 +1,166 @@ +/* + * 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 TLOG_TAG "libbinder" + +#include "android-base/logging.h" + +#include <trusty_log.h> +#include <iostream> +#include <string> + +#include <android-base/macros.h> +#include <android-base/strings.h> + +namespace android { +namespace base { + +static const char* GetFileBasename(const char* file) { + const char* last_slash = strrchr(file, '/'); + if (last_slash != nullptr) { + return last_slash + 1; + } + return file; +} + +// This splits the message up line by line, by calling log_function with a pointer to the start of +// each line and the size up to the newline character. It sends size = -1 for the final line. +template <typename F, typename... Args> +static void SplitByLines(const char* msg, const F& log_function, Args&&... args) { + const char* newline; + while ((newline = strchr(msg, '\n')) != nullptr) { + log_function(msg, newline - msg, args...); + msg = newline + 1; + } + + log_function(msg, -1, args...); +} + +void DefaultAborter(const char* abort_message) { + TLOGC("aborting: %s\n", abort_message); + abort(); +} + +static void TrustyLogLine(const char* msg, int length, android::base::LogSeverity severity, + const char* tag) { + switch (severity) { + case VERBOSE: + case DEBUG: + TLOGD("%s: %s\n", tag, msg); + break; + case INFO: + TLOGI("%s: %s\n", tag, msg); + break; + case WARNING: + TLOGW("%s: %s\n", tag, msg); + break; + case ERROR: + TLOGE("%s: %s\n", tag, msg); + break; + case FATAL_WITHOUT_ABORT: + case FATAL: + TLOGC("%s: %s\n", tag, msg); + break; + } +} + +void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, + const char*, unsigned int, const char* full_message) { + SplitByLines(full_message, TrustyLogLine, severity, tag); +} + +// This indirection greatly reduces the stack impact of having lots of +// checks/logging in a function. +class LogMessageData { +public: + LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag, + int error) + : file_(GetFileBasename(file)), + line_number_(line), + severity_(severity), + tag_(tag), + error_(error) {} + + const char* GetFile() const { return file_; } + + unsigned int GetLineNumber() const { return line_number_; } + + LogSeverity GetSeverity() const { return severity_; } + + const char* GetTag() const { return tag_; } + + int GetError() const { return error_; } + + std::ostream& GetBuffer() { return buffer_; } + + std::string ToString() const { return buffer_.str(); } + +private: + std::ostringstream buffer_; + const char* const file_; + const unsigned int line_number_; + const LogSeverity severity_; + const char* const tag_; + const int error_; + + DISALLOW_COPY_AND_ASSIGN(LogMessageData); +}; + +LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, + const char* tag, int error) + : LogMessage(file, line, severity, tag, error) {} + +LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, + int error) + : data_(new LogMessageData(file, line, severity, tag, error)) {} + +LogMessage::~LogMessage() { + // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM. + if (!WOULD_LOG(data_->GetSeverity())) { + return; + } + + // Finish constructing the message. + if (data_->GetError() != -1) { + data_->GetBuffer() << ": " << strerror(data_->GetError()); + } + std::string msg(data_->ToString()); + + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), + msg.c_str()); + + // Abort if necessary. + if (data_->GetSeverity() == FATAL) { + DefaultAborter(msg.c_str()); + } +} + +std::ostream& LogMessage::stream() { + return data_->GetBuffer(); +} + +void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag, + const char* message) { + TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message); +} + +bool ShouldLog(LogSeverity severity, const char* tag) { + // This is controlled by Trusty's log level. + return true; +} + +} // namespace base +} // namespace android diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk new file mode 100644 index 0000000000..cd81a09316 --- /dev/null +++ b/libs/binder/trusty/rules.mk @@ -0,0 +1,82 @@ +# 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +LIBBINDER_DIR := frameworks/native/libs/binder +LIBBASE_DIR := system/libbase +LIBCUTILS_DIR := system/core/libcutils +LIBUTILS_DIR := system/core/libutils +FMTLIB_DIR := external/fmtlib + +MODULE_SRCS := \ + $(LOCAL_DIR)/logging.cpp \ + $(LOCAL_DIR)/OS.cpp \ + $(LOCAL_DIR)/RpcServerTrusty.cpp \ + $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \ + $(LOCAL_DIR)/TrustyStatus.cpp \ + $(LOCAL_DIR)/socket.cpp \ + $(LIBBINDER_DIR)/Binder.cpp \ + $(LIBBINDER_DIR)/BpBinder.cpp \ + $(LIBBINDER_DIR)/FdTrigger.cpp \ + $(LIBBINDER_DIR)/IInterface.cpp \ + $(LIBBINDER_DIR)/IResultReceiver.cpp \ + $(LIBBINDER_DIR)/Parcel.cpp \ + $(LIBBINDER_DIR)/RpcServer.cpp \ + $(LIBBINDER_DIR)/RpcSession.cpp \ + $(LIBBINDER_DIR)/RpcState.cpp \ + $(LIBBINDER_DIR)/Stability.cpp \ + $(LIBBINDER_DIR)/Status.cpp \ + $(LIBBINDER_DIR)/Utils.cpp \ + $(LIBBASE_DIR)/hex.cpp \ + $(LIBBASE_DIR)/stringprintf.cpp \ + $(LIBUTILS_DIR)/Errors.cpp \ + $(LIBUTILS_DIR)/misc.cpp \ + $(LIBUTILS_DIR)/RefBase.cpp \ + $(LIBUTILS_DIR)/StrongPointer.cpp \ + $(LIBUTILS_DIR)/Unicode.cpp \ + +# TODO: remove the following when libbinder supports std::string +# instead of String16 and String8 for Status and descriptors +MODULE_SRCS += \ + $(LIBUTILS_DIR)/SharedBuffer.cpp \ + $(LIBUTILS_DIR)/String16.cpp \ + $(LIBUTILS_DIR)/String8.cpp \ + +# TODO: disable dump() transactions to get rid of Vector +MODULE_SRCS += \ + $(LIBUTILS_DIR)/VectorImpl.cpp \ + +MODULE_EXPORT_INCLUDES += \ + $(LOCAL_DIR)/include \ + $(LIBBINDER_DIR)/include \ + $(LIBBASE_DIR)/include \ + $(LIBCUTILS_DIR)/include \ + $(LIBUTILS_DIR)/include \ + $(FMTLIB_DIR)/include \ + +MODULE_EXPORT_COMPILEFLAGS += \ + -DBINDER_NO_KERNEL_IPC \ + -DBINDER_RPC_SINGLE_THREADED \ + -D__ANDROID_VNDK__ \ + +MODULE_LIBRARY_DEPS += \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/tipc \ + external/boringssl \ + +include make/library.mk diff --git a/libs/binder/trusty/socket.cpp b/libs/binder/trusty/socket.cpp new file mode 100644 index 0000000000..02df8afb92 --- /dev/null +++ b/libs/binder/trusty/socket.cpp @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#include <log/log.h> + +// On some versions of clang, RpcServer.cpp refuses to link without accept4 +__attribute__((weak)) extern "C" int accept4(int, void*, void*, int) { + LOG_ALWAYS_FATAL("accept4 called on Trusty"); + return 0; +} + +// Placeholder for poll used by FdTrigger +__attribute__((weak)) extern "C" int poll(void*, long, int) { + LOG_ALWAYS_FATAL("poll called on Trusty"); + return 0; +} diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index d1690437ef..706704ad34 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -131,24 +131,24 @@ static bool initGlobals() { } gTisTotalMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_total_time_in_state_map")}; if (gTisTotalMapFd < 0) return false; - gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; gConcurrentMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; if (gConcurrentMapFd < 0) return false; gUidLastUpdateMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_last_update_map")}; if (gUidLastUpdateMapFd < 0) return false; - gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_time_in_state_pid_time_in_state_map")}; + gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_timeInState_pid_time_in_state_map")}; if (gPidTisMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; gInitialized = true; @@ -156,7 +156,7 @@ static bool initGlobals() { } static int retrieveProgramFd(const std::string &eventType, const std::string &eventName) { - std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", + std::string path = StringPrintf(BPF_FS_PATH "prog_timeInState_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); return retrieveProgram(path.c_str()); } @@ -200,7 +200,7 @@ bool startTrackingUidTimes() { if (!initGlobals()) return false; if (gTracking) return true; - unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_policy_map")); if (cpuPolicyFd < 0) return false; for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { @@ -209,7 +209,7 @@ bool startTrackingUidTimes() { } } - unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_freq_to_idx_map")); if (freqToIdxFd < 0) return false; freq_idx_key_t key; for (uint32_t i = 0; i < gNPolicies; ++i) { @@ -224,23 +224,23 @@ bool startTrackingUidTimes() { } } - unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map")); + unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_last_update_map")); if (cpuLastUpdateFd < 0) return false; std::vector<uint64_t> zeros(get_nprocs_conf(), 0); uint32_t zero = 0; if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false; - unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map")); + unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_nr_active_map")); if (nrActiveFd < 0) return false; if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false; - unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map")); + unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_nr_active_map")); if (policyNrActiveFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false; } - unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map")); + unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_freq_idx_map")); if (policyFreqIdxFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { auto freqIdx = getPolicyFreqIdx(i); @@ -560,10 +560,10 @@ bool startTrackingProcessCpuTimes(pid_t pid) { if (!gInitialized && !initGlobals()) return false; unique_fd trackedPidHashMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_hash_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_hash_map")); if (trackedPidHashMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) { @@ -590,7 +590,7 @@ bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { if (!gInitialized && !initGlobals()) return false; unique_fd taskAggregationMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_task_aggregation_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_task_aggregation_map")); if (taskAggregationMapFd < 0) return false; return writeToMapEntry(taskAggregationMapFd, &pid, &aggregationKey, BPF_ANY) == 0; diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 45a6d47bb4..6ccc6cafb6 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -462,7 +462,7 @@ TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { ++uid; } android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; ASSERT_GE(fd, 0); uint32_t nCpus = get_nprocs_conf(); uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY; @@ -504,7 +504,7 @@ TEST_F(TimeInStateTest, RemoveUid) { { // Add a map entry for our fake UID by copying a real map entry android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; ASSERT_GE(fd, 0); time_key_t k; ASSERT_FALSE(getFirstMapKey(fd, &k)); @@ -515,7 +515,7 @@ TEST_F(TimeInStateTest, RemoveUid) { ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); android::base::unique_fd fd2{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; k.uid = copiedUid; k.bucket = 0; std::vector<concurrent_val_t> cvals(get_nprocs_conf()); diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp index acda402993..09fbdea0e8 100644 --- a/libs/dumputils/Android.bp +++ b/libs/dumputils/Android.bp @@ -26,6 +26,7 @@ cc_library { shared_libs: [ "libbase", + "libbinder", "libhidlbase", "liblog", "libutils", @@ -33,7 +34,10 @@ cc_library { srcs: ["dump_utils.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], export_include_dirs: [ "include", diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 29c788bca3..a6585c5ad2 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -20,6 +20,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android/hidl/manager/1.0/IServiceManager.h> +#include <binder/IServiceManager.h> #include <dumputils/dump_utils.h> #include <log/log.h> @@ -52,8 +53,8 @@ static const char* debuggable_native_processes_to_dump[] = { NULL, }; -/* list of hal interface to dump containing process during native dumps */ -static const char* hal_interfaces_to_dump[] { +/* list of hidl hal interface to dump containing process during native dumps */ +static const char* hidl_hal_interfaces_to_dump[] { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", @@ -82,6 +83,24 @@ static const char* hal_interfaces_to_dump[] { NULL, }; +/* list of hal interface to dump containing process during native dumps */ +static const std::vector<std::string> aidl_interfaces_to_dump { + "android.hardware.automotive.audiocontrol.IAudioControl", + "android.hardware.automotive.evs.IEvsEnumerator", + "android.hardware.biometrics.face.IBiometricsFace", + "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", + "android.hardware.camera.provider.ICameraProvider", + "android.hardware.drm.IDrmFactory", + "android.hardware.graphics.allocator.IAllocator", + "android.hardware.graphics.composer3.IComposer", + "android.hardware.health.IHealth", + "android.hardware.input.processor.IInputProcessor", + "android.hardware.neuralnetworks.IDevice", + "android.hardware.power.IPower", + "android.hardware.power.stats.IPowerStats", + "android.hardware.sensors.ISensors", +}; + /* list of extra hal interfaces to dump containing process during native dumps */ // This is filled when dumpstate is called. static std::set<const std::string> extra_hal_interfaces_to_dump; @@ -104,7 +123,7 @@ static void read_extra_hals_to_dump_from_property() { // check if interface is included in either default hal list or extra hal list bool should_dump_hal_interface(const std::string& interface) { - for (const char** i = hal_interfaces_to_dump; *i; i++) { + for (const char** i = hidl_hal_interfaces_to_dump; *i; i++) { if (interface == *i) { return true; } @@ -130,14 +149,26 @@ bool should_dump_native_traces(const char* path) { return false; } -std::set<int> get_interesting_hal_pids() { +static void get_interesting_aidl_pids(std::set<int> &pids) { + using ServiceDebugInfo = android::IServiceManager::ServiceDebugInfo; + auto sm = android::defaultServiceManager(); + std::vector<ServiceDebugInfo> serviceDebugInfos = sm->getServiceDebugInfo(); + for (const auto & serviceDebugInfo : serviceDebugInfos) { + for (const auto &aidl_prefix : aidl_interfaces_to_dump) { + // Check for prefix match with aidl interface to dump + if (serviceDebugInfo.name.rfind(aidl_prefix, 0) == 0) { + pids.insert(serviceDebugInfo.pid); + } + } + } +} + +static void get_interesting_hidl_pids(std::set<int> &pids) { using android::hidl::manager::V1_0::IServiceManager; using android::sp; using android::hardware::Return; sp<IServiceManager> manager = IServiceManager::getService(); - std::set<int> pids; - read_extra_hals_to_dump_from_property(); Return<void> ret = manager->debugDump([&](auto& hals) { @@ -146,11 +177,9 @@ std::set<int> get_interesting_hal_pids() { continue; } - if (!should_dump_hal_interface(info.interfaceName)) { - continue; + if (should_dump_hal_interface(info.interfaceName)) { + pids.insert(info.pid); } - - pids.insert(info.pid); } }); @@ -158,7 +187,14 @@ std::set<int> get_interesting_hal_pids() { ALOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str()); } - return pids; // whether it was okay or not + return; +} + +std::set<int> get_interesting_pids() { + std::set<int> interesting_pids; + get_interesting_hidl_pids(interesting_pids); + get_interesting_aidl_pids(interesting_pids); + return interesting_pids; } bool IsZygote(int pid) { diff --git a/libs/dumputils/include/dumputils/dump_utils.h b/libs/dumputils/include/dumputils/dump_utils.h index 25f712733a..7c5329d01b 100644 --- a/libs/dumputils/include/dumputils/dump_utils.h +++ b/libs/dumputils/include/dumputils/dump_utils.h @@ -21,7 +21,7 @@ bool should_dump_native_traces(const char* path); -std::set<int> get_interesting_hal_pids(); +std::set<int> get_interesting_pids(); bool IsZygote(int pid); diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 61e4a98846..6c6d7f3641 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -94,4 +94,8 @@ status_t ServiceManager::unregisterForNotifications(const String16&, return INVALID_OPERATION; } +std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() { + std::vector<IServiceManager::ServiceDebugInfo> ret; + return ret; +} } // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h index 6d6e008c4f..e0af5d4ba8 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/ServiceManager.h @@ -20,6 +20,7 @@ #include <map> #include <optional> +#include <vector> namespace android { @@ -58,6 +59,9 @@ public: status_t unregisterForNotifications(const String16& name, const sp<LocalRegistrationCallback>& callback) override; + + std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; + private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 77a883b332..bf275a5900 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -391,6 +391,71 @@ void DisplayState::merge(const DisplayState& other) { } } +void layer_state_t::sanitize(int32_t permissions) { + // TODO: b/109894387 + // + // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary + // rotation. To see the problem observe that if we have a square parent, and a child + // of the same size, then we rotate the child 45 degrees around its center, the child + // must now be cropped to a non rectangular 8 sided region. + // + // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is + // private API, and arbitrary rotation is used in limited use cases, for instance: + // - WindowManager only uses rotation in one case, which is on a top level layer in which + // cropping is not an issue. + // - Launcher, as a privileged app, uses this to transition an application to PiP + // (picture-in-picture) mode. + // + // However given that abuse of rotation matrices could lead to surfaces extending outside + // of cropped areas, we need to prevent non-root clients without permission + // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER + // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle + // preserving transformations. + if (what & eMatrixChanged) { + if (!(permissions & Permission::ROTATE_SURFACE_FLINGER)) { + ui::Transform t; + t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + if (!t.preserveRects()) { + what &= ~eMatrixChanged; + ALOGE("Stripped non rect preserving matrix in sanitize"); + } + } + } + + if (what & layer_state_t::eInputInfoChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eInputInfoChanged; + ALOGE("Stripped attempt to set eInputInfoChanged in sanitize"); + } + } + if (what & layer_state_t::eTrustedOverlayChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eTrustedOverlayChanged; + ALOGE("Stripped attempt to set eTrustedOverlay in sanitize"); + } + } + if (what & layer_state_t::eDropInputModeChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eDropInputModeChanged; + ALOGE("Stripped attempt to set eDropInputModeChanged in sanitize"); + } + } + if (what & layer_state_t::eFrameRateSelectionPriority) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateSelectionPriority; + ALOGE("Stripped attempt to set eFrameRateSelectionPriority in sanitize"); + } + } + if (what & layer_state_t::eFrameRateChanged) { + if (!ValidateFrameRate(frameRate, frameRateCompatibility, + changeFrameRateStrategy, + "layer_state_t::sanitize", + permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateChanged; // logged in ValidateFrameRate + } + } +} + void layer_state_t::merge(const layer_state_t& other) { if (other.what & ePositionChanged) { what |= ePositionChanged; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 725ea6571d..4bcc9d56c8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -551,6 +551,13 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mListenerCallbacks = other.mListenerCallbacks; } +void SurfaceComposerClient::Transaction::sanitize() { + for (auto & [handle, composerState] : mComposerStates) { + composerState.state.sanitize(0 /* permissionMask */); + } + mInputWindowCommands.clear(); +} + std::unique_ptr<SurfaceComposerClient::Transaction> SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { auto transaction = std::make_unique<Transaction>(); @@ -635,7 +642,6 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } - composerStates[surfaceControlHandle] = composerState; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 03e4aacdbe..2a8d30d2da 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -61,6 +61,12 @@ struct client_cache_t { * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { + enum Permission { + ACCESS_SURFACE_FLINGER = 0x1, + ROTATE_SURFACE_FLINGER = 0x2, + INTERNAL_SYSTEM_WINDOW = 0x4, + }; + enum { eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java eLayerOpaque = 0x02, // SURFACE_OPAQUE @@ -128,6 +134,7 @@ struct layer_state_t { status_t read(const Parcel& input); bool hasBufferChanges() const; bool hasValidBuffer() const; + void sanitize(int32_t permissions); struct matrix22_t { float dsdx{0}; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0d1d1a37bd..76b6d44fd2 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -590,6 +590,14 @@ public: void setAnimationTransaction(); void setEarlyWakeupStart(); void setEarlyWakeupEnd(); + + /** + * Strip the transaction of all permissioned requests, required when + * accepting transactions across process boundaries. + * + * TODO (b/213644870): Remove all permissioned things from Transaction + */ + void sanitize(); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; diff --git a/libs/input/Android.bp b/libs/input/Android.bp index eb0d8214a0..cdcdaabfb1 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -62,6 +62,7 @@ cc_library { "libbase", "liblog", "libcutils", + "libvintf", ], static_libs: [ diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 1aec477081..18cd474058 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -53,33 +53,39 @@ static void appendInputDeviceConfigurationFileRelativePath(std::string& path, } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - std::string versionPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type); + std::string versionPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%" + "04x_Version_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + deviceIdentifier.version, + suffix), + type); if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - std::string productPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type); + std::string productPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + suffix), + type); if (!productPath.empty()) { return productPath; } } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix, + type); } std::string getInputDeviceConfigurationFilePathByName( diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index c365ab070e..84a8ab42a1 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -23,20 +23,33 @@ #include <input/InputEventLabels.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> #include <utils/Timers.h> #include <utils/Tokenizer.h> +#include <vintf/RuntimeInfo.h> +#include <vintf/VintfObject.h> -// Enables debug output for the parser. -#define DEBUG_PARSER 0 +#include <cstdlib> +#include <string_view> +#include <unordered_map> + +/** + * Log debug output for the parser. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart) + */ +const bool DEBUG_PARSER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO); // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - +/** + * Log debug output for mapping. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart) + */ +const bool DEBUG_MAPPING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO); namespace android { @@ -62,6 +75,29 @@ static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; +bool kernelConfigsArePresent(const std::set<std::string>& configs) { + std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo = + android::vintf::VintfObject::GetInstance()->getRuntimeInfo( + vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); + LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched"); + + const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs(); + for (const std::string& requiredConfig : configs) { + const auto configIt = kernelConfigs.find(requiredConfig); + if (configIt == kernelConfigs.end()) { + ALOGI("Required kernel config %s is not found", requiredConfig.c_str()); + return false; + } + const std::string& option = configIt->second; + if (option != "y" && option != "m") { + ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(), + option.c_str()); + return false; + } + } + return true; +} + // --- KeyLayoutMap --- KeyLayoutMap::KeyLayoutMap() { @@ -72,32 +108,34 @@ KeyLayoutMap::~KeyLayoutMap() { base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key layout map.", status); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr<Tokenizer> t(tokenizer); - auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; - } - return ret; + return load(filename, contents); } -base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) { +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename, + const char* contents) { Tokenizer* tokenizer; - status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + status_t status; + if (contents == nullptr) { + status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + } else { + status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + } if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); } std::unique_ptr<Tokenizer> t(tokenizer); auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + if (!ret.ok()) { + return ret; } + const std::shared_ptr<KeyLayoutMap>& map = *ret; + LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); + if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) { + ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str()); + return Errorf("Missing kernel config"); + } + map->mLoadFileName = filename; return ret; } @@ -130,9 +168,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const { const Key* key = getKey(scanCode, usageCode); if (!key) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, + usageCode); *outKeyCode = AKEYCODE_UNKNOWN; *outFlags = 0; return NAME_NOT_FOUND; @@ -141,10 +178,9 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, *outKeyCode = key->keyCode; *outFlags = key->flags; -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); return NO_ERROR; } @@ -152,103 +188,79 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) { auto it = mSensorsByAbsCode.find(absCode); if (it == mSensorsByAbsCode.end()) { -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode); return Errorf("Can't find abs code {}.", absCode); } const Sensor& sensor = it->second; - -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, - NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + NamedEnum::string(sensor.sensorType).c_str(), sensor.sensorDataIndex); return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); + auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + return &it->second; } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); + auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + return &it->second; } } return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey( - int32_t keyCode, std::vector<int32_t>* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; i<N; i++) { - if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->push_back(mKeysByScanCode.keyAt(i)); +std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { + std::vector<int32_t> scanCodes; + for (const auto& [scanCode, key] : mKeysByScanCode) { + if (keyCode == key.keyCode) { + scanCodes.push_back(scanCode); } } - return NO_ERROR; + return scanCodes; } -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); -#endif - return NAME_NOT_FOUND; +std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const { + auto it = mAxes.find(scanCode); + if (it == mAxes.end()) { + ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); + return std::nullopt; } - *outAxisInfo = mAxes.valueAt(index); - -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); -#endif - return NO_ERROR; + const AxisInfo& axisInfo = it->second; + ALOGD_IF(DEBUG_MAPPING, + "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + return axisInfo; } -status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { - const size_t N = mLedsByScanCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { - *outScanCode = mLedsByScanCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); -#endif - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const { + for (const auto& [scanCode, led] : mLedsByScanCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode); + return scanCode; } } -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); -#endif - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } -status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { - const size_t N = mLedsByUsageCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { - *outUsageCode = mLedsByUsageCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); -#endif - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const { + for (const auto& [usageCode, led] : mLedsByUsageCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode); + return usageCode; } } -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); -#endif - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } - // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : @@ -260,10 +272,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); mTokenizer->skipDelimiters(WHITESPACE); @@ -285,6 +295,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseSensor(); if (status) return status; + } else if (keywordToken == "requires_kernel_config") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseRequiredKernelConfig(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -321,8 +335,9 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Key>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -357,14 +372,13 @@ status_t KeyLayoutMap::Parser::parseKey() { flags |= flag; } -#if DEBUG_PARSER - ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", code, keyCode, flags); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); + Key key; key.keyCode = keyCode; key.flags = flags; - map.add(code, key); + map.insert({code, key}); return NO_ERROR; } @@ -377,7 +391,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { scanCodeToken.string()); return BAD_VALUE; } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; @@ -458,14 +472,12 @@ status_t KeyLayoutMap::Parser::parseAxis() { } } -#if DEBUG_PARSER - ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - axisInfo.mode, axisInfo.axis, axisInfo.highAxis, - axisInfo.splitValue, axisInfo.flatOverride); -#endif - mMap->mAxes.add(scanCode, axisInfo); + ALOGD_IF(DEBUG_PARSER, + "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + mMap->mAxes.insert({scanCode, axisInfo}); return NO_ERROR; } @@ -485,8 +497,9 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } - KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Led>& map = + mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -501,14 +514,12 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } -#if DEBUG_PARSER - ALOGD("Parsed led %s: code=%d, ledCode=%d.", - mapUsage ? "usage" : "scan code", code, ledCode); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code", + code, ledCode); Led led; led.ledCode = ledCode; - map.add(code, led); + map.insert({code, led}); return NO_ERROR; } @@ -580,10 +591,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { } int32_t sensorDataIndex = indexOpt.value(); -#if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, - NamedEnum::string(sensorType).c_str(), sensorDataIndex); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + NamedEnum::string(sensorType).c_str(), sensorDataIndex); Sensor sensor; sensor.sensorType = sensorType; @@ -591,4 +600,23 @@ status_t KeyLayoutMap::Parser::parseSensor() { map.emplace(code, sensor); return NO_ERROR; } + +// Parse the name of a required kernel config. +// The layout won't be used if the specified kernel config is not present +// Examples: +// requires_kernel_config CONFIG_HID_PLAYSTATION +status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + std::string configName = codeToken.string(); + + const auto result = mMap->mRequiredKernelConfigs.emplace(configName); + if (!result.second) { + ALOGE("%s: Duplicate entry for required kernel config %s.", + mTokenizer->getLocation().string(), configName.c_str()); + return BAD_VALUE; + } + + ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); + return NO_ERROR; +} }; diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index f0895b32ef..c3f5151fd1 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -20,16 +20,23 @@ #include <unistd.h> #include <limits.h> -#include <input/Keyboard.h> +#include <input/InputDevice.h> #include <input/InputEventLabels.h> -#include <input/KeyLayoutMap.h> #include <input/KeyCharacterMap.h> -#include <input/InputDevice.h> +#include <input/KeyLayoutMap.h> +#include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> namespace android { +static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, + InputDeviceConfigurationFileType type) { + return name.empty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + // --- KeyMap --- KeyMap::KeyMap() { @@ -111,11 +118,25 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, } base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + if (ret.ok()) { + keyLayoutMap = *ret; + keyLayoutFile = path; + return OK; + } + + // Try to load fallback layout if the regular layout could not be loaded due to missing + // kernel modules + std::string fallbackPath( + getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, + InputDeviceConfigurationFileType:: + KEY_LAYOUT, + "_fallback")); + ret = KeyLayoutMap::load(fallbackPath); if (!ret.ok()) { return ret.error().code(); } keyLayoutMap = *ret; - keyLayoutFile = path; + keyLayoutFile = fallbackPath; return OK; } @@ -137,14 +158,6 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return OK; } -std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type) { - return name.empty() - ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) - : getInputDeviceConfigurationFilePathByName(name, type); -} - - // --- Global functions --- bool isKeyboardSpecialFunction(const PropertyMap* config) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 6ffe8518b6..d947cd99e8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -36,8 +36,9 @@ cc_test { "liblog", "libui", "libutils", + "libvintf", ], - data: ["data/*.kcm"], + data: ["data/*"], test_suites: ["device-tests"], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index 61e88df11d..e872fa442b 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -64,13 +64,11 @@ protected: mKeyMap.keyCharacterMapFile = path; } - virtual void SetUp() override { + void SetUp() override { loadKeyLayout("Generic"); loadKeyCharacterMap("Generic"); } - virtual void TearDown() override {} - KeyMap mKeyMap; }; @@ -132,4 +130,20 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); } +TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; + // We assert error message here because it's used by 'validatekeymaps' tool + ASSERT_EQ("Missing kernel config", ret.error().message()); +} + +TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath; + const std::shared_ptr<KeyLayoutMap>& map = *ret; + ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present"; +} + } // namespace android diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl new file mode 100644 index 0000000000..2d0a507fbd --- /dev/null +++ b/libs/input/tests/data/kl_with_required_fake_config.kl @@ -0,0 +1,20 @@ +# 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. + +# This KL should not be loaded unless the below config is present in the kernel +# This config will never exist, and therefore this KL should never be loaded +requires_kernel_config CONFIG_HID_FAKEMODULE + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl new file mode 100644 index 0000000000..303b23e48a --- /dev/null +++ b/libs/input/tests/data/kl_with_required_real_config.kl @@ -0,0 +1,21 @@ +# 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. + +# This KL should not be loaded unless the below config is present in the kernel +# The CONFIG_UHID option has been required for a while, and therefore it's safe +# to assume that this will always be loaded +requires_kernel_config CONFIG_UHID + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 988132cd1c..da42a96df7 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,10 +2,10 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_createFromHandle; # llndk # apex + AHardwareBuffer_createFromHandle; # llndk # systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 - AHardwareBuffer_getNativeHandle; # llndk # apex + AHardwareBuffer_getNativeHandle; # llndk # systemapi AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -23,18 +23,18 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; - ANativeWindow_getLastDequeueDuration; # apex # introduced=30 - ANativeWindow_getLastDequeueStartTime; # apex # introduced=30 - ANativeWindow_getLastQueueDuration; # apex # introduced=30 + ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 + ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30 + ANativeWindow_getLastQueueDuration; # systemapi # introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # llndk ANativeWindow_queryf; # llndk ANativeWindow_queueBuffer; # llndk - ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30 - ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30 - ANativeWindow_setPerformInterceptor; # apex # introduced=30 - ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30 + ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setPerformInterceptor; # systemapi # introduced=30 + ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30 ANativeWindow_release; ANativeWindow_setAutoPrerotation; # llndk ANativeWindow_setAutoRefresh; # llndk @@ -45,7 +45,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setDequeueTimeout; # apex # introduced=30 + ANativeWindow_setDequeueTimeout; # systemapi # introduced=30 ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index bf848af262..06ecd258ae 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -31,7 +31,7 @@ sourceFiles = [ "vr_flinger.cpp", ] -includeFiles = [ "include" ] +includeFiles = ["include"] staticLibraries = [ "libdisplay", @@ -83,8 +83,6 @@ headerLibraries = [ cc_library_static { srcs: sourceFiles, export_include_dirs: includeFiles, - - clang: true, cflags: [ "-DLOG_TAG=\"vr_flinger\"", "-DTRACE=0", diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index de36a7aea6..8363104704 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1448,7 +1448,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri setError(EGL_BAD_SURFACE, EGL_FALSE); } int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (attribute == EGL_TIMESTAMPS_ANDROID) { @@ -1458,7 +1462,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri return EGL_TRUE; } int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (s->setSmpte2086Attribute(attribute, value)) { diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index b9b6a19606..0411b31234 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -98,7 +98,7 @@ cc_binary { init_rc: ["gpuservice.rc"], required: [ "bpfloader", - "gpu_mem.o", + "gpuMem.o", ], srcs: [":gpuservice_binary_sources"], shared_libs: [ diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk index 482fc6dfc3..c51f6aa5f0 100644 --- a/services/gpuservice/CleanSpec.mk +++ b/services/gpuservice/CleanSpec.mk @@ -44,9 +44,9 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) -# Remove gpu_mem.o +# Remove gpuMem.o $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpuMem.o_gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpuMem.o) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpuMem.o-timestamp) diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp index 076affda5a..680b291fe3 100644 --- a/services/gpuservice/bpfprogs/Android.bp +++ b/services/gpuservice/bpfprogs/Android.bp @@ -22,8 +22,8 @@ package { } bpf { - name: "gpu_mem.o", - srcs: ["gpu_mem.c"], + name: "gpuMem.o", + srcs: ["gpuMem.c"], btf: true, cflags: [ "-Wall", diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpuMem.c index 16e1e8a1ef..16e1e8a1ef 100644 --- a/services/gpuservice/bpfprogs/gpu_mem.c +++ b/services/gpuservice/bpfprogs/gpuMem.c diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h index de691e2b75..7588b54818 100644 --- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h +++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h @@ -57,9 +57,9 @@ private: static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total"; // pinned gpu memory total bpf c program path in bpf sysfs static constexpr char kGpuMemTotalProgPath[] = - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"; + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"; // pinned gpu memory total bpf map path in bpf sysfs - static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"; + static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"; // 30 seconds timeout for trying to attach bpf program to tracepoint static constexpr int kGpuWaitTimeout = 30; }; diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp index e916221c2e..8dabe4fbdb 100644 --- a/services/gpuservice/tests/unittests/GpuMemTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "gpuservice_unittest" #include <android-base/stringprintf.h> +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gmock/gmock.h> #include <gpumem/GpuMem.h> @@ -65,11 +66,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); mTestableGpuMem.setInitialized(); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } @@ -89,8 +90,8 @@ TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(), - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"); - EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"); + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"); } TEST_F(GpuMemTest, bpfInitializationFailed) { diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp index d76f039a6d..5c042102b2 100644 --- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "gpuservice_unittest" +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gpumem/GpuMem.h> #include <gtest/gtest.h> @@ -64,11 +65,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp index 0baf1f93da..7ea22888f8 100644 --- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp +++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp @@ -25,6 +25,7 @@ #include <gtest/gtest.h> #include <stats_pull_atom_callback.h> #include <statslog.h> +#include <utils/Looper.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -62,8 +63,9 @@ enum InputCommand : int32_t { // clang-format on class GpuStatsTest : public testing::Test { + sp<android::Looper> looper; public: - GpuStatsTest() { + GpuStatsTest() : looper(Looper::prepare(0 /* opts */)) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); @@ -73,6 +75,16 @@ public: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + + // This is required for test due to GpuStats instance spawns binder transactions + // in its destructor. After the gtest destructor test exits immidiatelly. + // It results in binder thread not able to process above binder transactions and memory leak + // occures. Binder thread needs time to process callbacks transactions. + // It leads to GpuStats instance destructor needs to be called in advance. + mGpuStats.reset(nullptr); + // performs all pending callbacks until all data has been consumed + // gives time to process binder transactions by thread pool + looper->pollAll(1000); } std::string inputCommand(InputCommand cmd); diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS index 82c6ee12c7..c88bfe97ca 100644 --- a/services/inputflinger/OWNERS +++ b/services/inputflinger/OWNERS @@ -1,3 +1 @@ -lzye@google.com -michaelwr@google.com -svv@google.com +include platform/frameworks/base:/INPUT_OWNERS diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 166f358598..26c33d2a9d 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -449,8 +449,7 @@ bool EventHub::Device::hasKeycodeLocked(int keycode) const { return false; } - std::vector<int32_t> scanCodes; - keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode); const size_t N = scanCodes.size(); for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; @@ -545,10 +544,10 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } - int32_t scanCode; - if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { - *outScanCode = scanCode; + std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led); + if (scanCode.has_value()) { + if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) { + *outScanCode = *scanCode; return NO_ERROR; } } @@ -862,8 +861,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode); if (scanCodes.size() != 0) { if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { @@ -921,20 +919,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], - &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { - outFlags[codeIndex] = 1; - break; - } + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); + + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (device->keyBitmask.test(scanCodes[sc])) { + outFlags[codeIndex] = 1; + break; } } } @@ -988,14 +982,15 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } + if (device == nullptr || !device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; } - - return NAME_NOT_FOUND; + std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode); + if (!info.has_value()) { + return NAME_NOT_FOUND; + } + *outAxisInfo = *info; + return NO_ERROR; } base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId, diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 2ac41b1e67..231f825c1f 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -64,7 +64,11 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} -CursorInputMapper::~CursorInputMapper() {} +CursorInputMapper::~CursorInputMapper() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } +} uint32_t CursorInputMapper::getSources() { return mSource; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ac5f6b652b..17bbff8817 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -813,6 +813,10 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } } else { + if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT && + !mConfig.showTouches) { + mPointerController->clearSpots(); + } mPointerController.reset(); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 969c8ba474..0d5d06a569 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -192,7 +192,7 @@ private: mSpotsByDisplay[displayId] = newSpots; } - void clearSpots() override {} + void clearSpots() override { mSpotsByDisplay.clear(); } std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; }; @@ -7959,7 +7959,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Default device will reconfigure above, need additional reconfiguration for another device. device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); // Two fingers down at default display. int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; @@ -7986,6 +7987,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID); ASSERT_TRUE(iter != fakePointerController->getSpots().end()); ASSERT_EQ(size_t(2), iter->second.size()); + + // Disable the show touches configuration and ensure the spots are cleared. + mFakePolicy->setShowTouches(false); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); + + ASSERT_TRUE(fakePointerController->getSpots().empty()); } TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index da7ff71510..f94917cf1c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1608,8 +1608,10 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; + const float parentShadowRadius = + newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - newParent->mEffectiveShadowRadius); + parentShadowRadius); } } diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index 691676420c..1834f2a7ad 100644 --- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -94,7 +94,8 @@ TEST_F(OneShotTimerTest, resetTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, resetBackToBackTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 46a6cfb016..0afbe11a6b 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -755,7 +755,11 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // We must support R8G8B8A8 std::vector<VkSurfaceFormatKHR> all_formats = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY + {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}, + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}, + }; if (colorspace_ext) { all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -777,12 +781,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_PASS_THROUGH_EXT}); } desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT}); if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, @@ -798,6 +806,9 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, @@ -805,10 +816,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, } } - // NOTE: Any new formats that are added must be coordinated across different - // Android users. This includes the ANGLE team (a layered implementation of - // OpenGL-ES). - VkResult result = VK_SUCCESS; if (formats) { uint32_t transfer_count = all_formats.size(); diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 438e5dd10d..61e3859357 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -840,56 +840,52 @@ inline bool Iterate(Visitor* visitor, VkJsonDeviceGroup* device_group) { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { bool ret = true; - switch (device->properties.apiVersion ^ - VK_VERSION_PATCH(device->properties.apiVersion)) { - case VK_API_VERSION_1_2: - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_1: - ret &= - visitor->Visit("subgroupProperties", &device->subgroup_properties) && - visitor->Visit("pointClippingProperties", - &device->point_clipping_properties) && - visitor->Visit("multiviewProperties", - &device->multiview_properties) && - visitor->Visit("idProperties", &device->id_properties) && - visitor->Visit("maintenance3Properties", - &device->maintenance3_properties) && - visitor->Visit("16bitStorageFeatures", - &device->bit16_storage_features) && - visitor->Visit("multiviewFeatures", &device->multiview_features) && - visitor->Visit("variablePointerFeatures", - &device->variable_pointer_features) && - visitor->Visit("protectedMemoryFeatures", - &device->protected_memory_features) && - visitor->Visit("samplerYcbcrConversionFeatures", - &device->sampler_ycbcr_conversion_features) && - visitor->Visit("shaderDrawParameterFeatures", - &device->shader_draw_parameter_features) && - visitor->Visit("externalFenceProperties", - &device->external_fence_properties) && - visitor->Visit("externalSemaphoreProperties", - &device->external_semaphore_properties); - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_0: - ret &= visitor->Visit("properties", &device->properties) && - visitor->Visit("features", &device->features) && - visitor->Visit("memory", &device->memory) && - visitor->Visit("queues", &device->queues) && - visitor->Visit("extensions", &device->extensions) && - visitor->Visit("layers", &device->layers) && - visitor->Visit("formats", &device->formats); - if (device->ext_driver_properties.reported) { - ret &= visitor->Visit("VK_KHR_driver_properties", - &device->ext_driver_properties); - } - if (device->ext_variable_pointer_features.reported) { - ret &= visitor->Visit("VK_KHR_variable_pointers", - &device->ext_variable_pointer_features); - } - if (device->ext_shader_float16_int8_features.reported) { - ret &= visitor->Visit("VK_KHR_shader_float16_int8", - &device->ext_shader_float16_int8_features); - } + if (device->properties.apiVersion >= VK_API_VERSION_1_1) { + ret &= + visitor->Visit("subgroupProperties", &device->subgroup_properties) && + visitor->Visit("pointClippingProperties", + &device->point_clipping_properties) && + visitor->Visit("multiviewProperties", + &device->multiview_properties) && + visitor->Visit("idProperties", &device->id_properties) && + visitor->Visit("maintenance3Properties", + &device->maintenance3_properties) && + visitor->Visit("16bitStorageFeatures", + &device->bit16_storage_features) && + visitor->Visit("multiviewFeatures", &device->multiview_features) && + visitor->Visit("variablePointerFeatures", + &device->variable_pointer_features) && + visitor->Visit("protectedMemoryFeatures", + &device->protected_memory_features) && + visitor->Visit("samplerYcbcrConversionFeatures", + &device->sampler_ycbcr_conversion_features) && + visitor->Visit("shaderDrawParameterFeatures", + &device->shader_draw_parameter_features) && + visitor->Visit("externalFenceProperties", + &device->external_fence_properties) && + visitor->Visit("externalSemaphoreProperties", + &device->external_semaphore_properties); + } + if (device->properties.apiVersion >= VK_API_VERSION_1_0) { + ret &= visitor->Visit("properties", &device->properties) && + visitor->Visit("features", &device->features) && + visitor->Visit("memory", &device->memory) && + visitor->Visit("queues", &device->queues) && + visitor->Visit("extensions", &device->extensions) && + visitor->Visit("layers", &device->layers) && + visitor->Visit("formats", &device->formats); + if (device->ext_driver_properties.reported) { + ret &= visitor->Visit("VK_KHR_driver_properties", + &device->ext_driver_properties); + } + if (device->ext_variable_pointer_features.reported) { + ret &= visitor->Visit("VK_KHR_variable_pointers", + &device->ext_variable_pointer_features); + } + if (device->ext_shader_float16_int8_features.reported) { + ret &= visitor->Visit("VK_KHR_shader_float16_int8", + &device->ext_shader_float16_int8_features); + } } return ret; } @@ -897,16 +893,13 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) { bool ret = true; - switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) { - case VK_API_VERSION_1_2: - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_1: - ret &= visitor->Visit("deviceGroups", &instance->device_groups); - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_0: - ret &= visitor->Visit("layers", &instance->layers) && - visitor->Visit("extensions", &instance->extensions) && - visitor->Visit("devices", &instance->devices); + if (instance->api_version >= VK_API_VERSION_1_1) { + ret &= visitor->Visit("deviceGroups", &instance->device_groups); + } + if (instance->api_version >= VK_API_VERSION_1_0) { + ret &= visitor->Visit("layers", &instance->layers) && + visitor->Visit("extensions", &instance->extensions) && + visitor->Visit("devices", &instance->devices); } return ret; } |