diff options
242 files changed, 4566 insertions, 1834 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index f1d8c72d85..fc0801cce8 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -228,10 +228,6 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable -# Tracing disabled by default - write /sys/kernel/debug/tracing/tracing_on 0 - write /sys/kernel/tracing/tracing_on 0 - # Read and truncate the kernel trace. chmod 0666 /sys/kernel/debug/tracing/trace chmod 0666 /sys/kernel/tracing/trace @@ -310,18 +306,9 @@ on late-init chmod 0666 /sys/kernel/tracing/events/synthetic/suspend_resume_minimal/enable chmod 0666 /sys/kernel/debug/tracing/events/synthetic/suspend_resume_minimal/enable -on late-init && property:ro.boot.fastboot.boottrace=enabled - setprop debug.atrace.tags.enableflags 802922 - setprop persist.traced.enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction/enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 1 - write /sys/kernel/tracing/events/binder/binder_set_priority/enable 1 - write /sys/kernel/tracing/events/binder/binder_lock/enable 1 - write /sys/kernel/tracing/events/binder/binder_locked/enable 1 - write /sys/kernel/tracing/events/binder/binder_unlock/enable 1 - write /sys/kernel/debug/tracing/tracing_on 1 - write /sys/kernel/tracing/tracing_on 1 +on late-init && property:ro.boot.fastboot.boottrace= + write /sys/kernel/debug/tracing/tracing_on 0 + write /sys/kernel/tracing/tracing_on 0 # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created @@ -534,7 +521,6 @@ on late-init && property:ro.boot.hypervisor.vm.supported=1 chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/host_mem_abort/id chmod 0440 /sys/kernel/tracing/hyp/events/hyp/host_mem_abort/id - on property:persist.debug.atrace.boottrace=1 start boottrace @@ -543,17 +529,3 @@ service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categ user root disabled oneshot - -on property:sys.boot_completed=1 && property:ro.boot.fastboot.boottrace=enabled - setprop debug.atrace.tags.enableflags 0 - setprop persist.traced.enable 1 - write /sys/kernel/tracing/events/binder/binder_transaction/enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 0 - write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 0 - write /sys/kernel/tracing/events/binder/binder_set_priority/enable 0 - write /sys/kernel/tracing/events/binder/binder_lock/enable 0 - write /sys/kernel/tracing/events/binder/binder_locked/enable 0 - write /sys/kernel/tracing/events/binder/binder_unlock/enable 0 - write /sys/kernel/debug/tracing/tracing_on 0 - write /sys/kernel/tracing/tracing_on 0 - diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 484231225d..615701ccc1 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -207,9 +207,7 @@ std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; int PropertiesHelper::unroot_ = -1; int PropertiesHelper::parallel_run_ = -1; -#if !defined(__ANDROID_VNDK__) int PropertiesHelper::strict_run_ = -1; -#endif bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { @@ -240,7 +238,6 @@ bool PropertiesHelper::IsParallelRun() { return parallel_run_ == 1; } -#if !defined(__ANDROID_VNDK__) bool PropertiesHelper::IsStrictRun() { if (strict_run_ == -1) { // Defaults to using stricter timeouts. @@ -248,7 +245,6 @@ bool PropertiesHelper::IsStrictRun() { } return strict_run_ == 1; } -#endif int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 6049e3e19d..9e955e3c04 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -198,18 +198,14 @@ class PropertiesHelper { * will default to true. This results in shortened timeouts for flaky * sections. */ -#if !defined(__ANDROID_VNDK__) static bool IsStrictRun(); -#endif private: static std::string build_type_; static int dry_run_; static int unroot_; static int parallel_run_; -#if !defined(__ANDROID_VNDK__) static int strict_run_; -#endif }; /* diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index e93b46c666..e132b35996 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1420,12 +1420,12 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { auto ret = sm->list([&](const auto& interfaces) { for (const std::string& interface : interfaces) { std::string cleanName = interface; - std::replace_if(cleanName.begin(), - cleanName.end(), - [](char c) { - return !isalnum(c) && - std::string("@-_:.").find(c) == std::string::npos; - }, '_'); + std::replace_if( + cleanName.begin(), cleanName.end(), + [](char c) { + return !isalnum(c) && std::string("@-_.").find(c) == std::string::npos; + }, + '_'); const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; bool empty = false; @@ -1758,6 +1758,20 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("SYSTEM PROPERTIES", {"getprop"}); + DumpFile("SYSTEM BUILD-TIME RELEASE FLAGS", "/system/etc/build_flags.json"); + DumpFile("SYSTEM_EXT BUILD-TIME RELEASE FLAGS", "/system_ext/etc/build_flags.json"); + DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json"); + DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json"); + + DumpFile("SYSTEM BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)", + "/system/etc/aconfig_flags.textproto"); + DumpFile("SYSTEM_EXT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime" + " values)", "/system_ext/etc/aconfig_flags.textproto"); + DumpFile("PRODUCT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)", + "/product/etc/aconfig_flags.textproto"); + DumpFile("VENDOR BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)", + "/vendor/etc/aconfig_flags.textproto"); + RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp index b17cba15d5..fd1df35aee 100644 --- a/cmds/installd/CrateManager.cpp +++ b/cmds/installd/CrateManager.cpp @@ -29,9 +29,10 @@ #include <sys/xattr.h> #include <unistd.h> +#include <utils.h> #include <fstream> +#include <functional> #include <string> -#include <utils.h> #include "utils.h" diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h index 1f30b5dc79..d9b590f784 100644 --- a/cmds/installd/CrateManager.h +++ b/cmds/installd/CrateManager.h @@ -25,6 +25,7 @@ #include <sys/stat.h> #include <sys/types.h> +#include <functional> #include <optional> #include <string> #include <vector> diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index bb6639e1a8..e2a2927f2b 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -19,6 +19,7 @@ #include <errno.h> #include <fts.h> #include <inttypes.h> +#include <linux/fsverity.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -51,6 +52,7 @@ #include <android-base/unique_fd.h> #include <cutils/ashmem.h> #include <cutils/fs.h> +#include <cutils/misc.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <linux/quota.h> @@ -84,6 +86,8 @@ using android::base::ParseUint; using android::base::Split; using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::endl; namespace android { @@ -229,6 +233,14 @@ binder::Status checkArgumentFileName(const std::string& path) { return ok(); } +binder::Status checkUidInAppRange(int32_t appUid) { + if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) { + return ok(); + } + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("UID %d is outside of the range", appUid)); +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -236,6 +248,16 @@ binder::Status checkArgumentFileName(const std::string& path) { } \ } +// we could have tighter checks, but this is only to avoid hard errors. Negative values are defined +// in UserHandle.java and carry specific meanings that may not be handled by certain APIs here. +#define ENFORCE_VALID_USER(userId) \ + { \ + if (static_cast<uid_t>(std::abs(userId)) >= \ + std::numeric_limits<uid_t>::max() / AID_USER_OFFSET) { \ + return error("userId invalid: " + std::to_string(userId)); \ + } \ + } + #define CHECK_ARGUMENT_UUID(uuid) { \ binder::Status status = checkArgumentUuid((uuid)); \ if (!status.isOk()) { \ @@ -273,6 +295,14 @@ binder::Status checkArgumentFileName(const std::string& path) { } \ } +#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \ + { \ + binder::Status status = checkUidInAppRange((uid)); \ + if (!status.isOk()) { \ + return status; \ + } \ + } + #ifdef GRANULAR_LOCKS /** @@ -373,6 +403,33 @@ using PackageLockGuard = std::lock_guard<PackageLock>; } // namespace +binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) { + int open_flags = fcntl(authFd.get(), F_GETFL); + if (open_flags < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed"); + } + if ((open_flags & O_ACCMODE) != O_WRONLY && (open_flags & O_ACCMODE) != O_RDWR) { + return exception(binder::Status::EX_SECURITY, "Received FD with unexpected open flag"); + } + if (fstat(authFd.get(), &this->mStatFromAuthFd) < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fstat failed"); + } + if (!S_ISREG(this->mStatFromAuthFd.st_mode)) { + return exception(binder::Status::EX_SECURITY, "Not a regular file"); + } + // Don't accept a file owned by a different app. + uid_t uid = multiuser_get_uid(userId, appUid); + if (this->mStatFromAuthFd.st_uid != uid) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid"); + } + return ok(); +} + +bool InstalldNativeService::FsveritySetupAuthToken::isSameStat(const struct stat& st) const { + return memcmp(&st, &mStatFromAuthFd, sizeof(st)) == 0; +} + status_t InstalldNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService<InstalldNativeService>::publish(); @@ -416,10 +473,12 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) */ static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid, bool existing) { + ScopedTrace tracer("restorecon-lazy"); int res = 0; char* before = nullptr; char* after = nullptr; if (!existing) { + ScopedTrace tracer("new-path"); if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, SELINUX_ANDROID_RESTORECON_RECURSE) < 0) { PLOG(ERROR) << "Failed recursive restorecon for " << path; @@ -446,6 +505,7 @@ static int restorecon_app_data_lazy(const std::string& path, const std::string& // If the initial top-level restorecon above changed the label, then go // back and restorecon everything recursively if (strcmp(before, after)) { + ScopedTrace tracer("label-change"); if (existing) { LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path << "; running recursive restorecon"; @@ -480,11 +540,15 @@ static bool internal_storage_has_project_id() { static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, long project_id) { - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { - PLOG(ERROR) << "Failed to prepare " << path; - return -1; + { + ScopedTrace tracer("prepare-dir"); + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << path; + return -1; + } } if (internal_storage_has_project_id()) { + ScopedTrace tracer("set-quota"); return set_quota_project_id(path, project_id, true); } return 0; @@ -493,14 +557,20 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, uid_t uid, gid_t gid, long project_id) { auto path = StringPrintf("%s/%s", parent.c_str(), name); - int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + int ret; + { + ScopedTrace tracer("prepare-cache-dir"); + ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + } if (ret == 0 && internal_storage_has_project_id()) { + ScopedTrace tracer("set-quota-cache-dir"); return set_quota_project_id(path, project_id, true); } return ret; } static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { + ScopedTrace tracer("prepare-app-profile"); int32_t uid = multiuser_get_uid(userId, appId); int shared_app_gid = multiuser_get_shared_gid(userId, appId); if (shared_app_gid == -1) { @@ -633,6 +703,7 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in int32_t previousUid, int32_t cacheGid, const std::string& seInfo, mode_t targetMode, long projectIdApp, long projectIdCache) { + ScopedTrace tracer("create-dirs"); struct stat st{}; bool parent_dir_exists = (stat(path.c_str(), &st) == 0); @@ -682,6 +753,7 @@ binder::Status InstalldNativeService::createAppDataLocked( int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -709,6 +781,7 @@ binder::Status InstalldNativeService::createAppDataLocked( long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START); if (flags & FLAG_STORAGE_CE) { + ScopedTrace tracer("ce"); auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, @@ -735,6 +808,7 @@ binder::Status InstalldNativeService::createAppDataLocked( } } if (flags & FLAG_STORAGE_DE) { + ScopedTrace tracer("de"); auto path = create_data_user_de_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, @@ -752,13 +826,14 @@ binder::Status InstalldNativeService::createAppDataLocked( } if (flags & FLAG_STORAGE_SDK) { + ScopedTrace tracer("sdk"); // Safe to ignore status since we can retry creating this by calling reconcileSdkData auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); if (!ignore.isOk()) { PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName; } - } else { + ScopedTrace tracer("destroy-sdk"); // Package does not need sdk storage. Remove it. destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); } @@ -773,6 +848,8 @@ binder::Status InstalldNativeService::createAppDataLocked( binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t appId, int32_t flags) { + ENFORCE_VALID_USER(userId); + int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); if (sdkSandboxUid == -1) { // There no valid sdk sandbox process for this app. Skip creation of data directory @@ -811,6 +888,7 @@ binder::Status InstalldNativeService::createAppData( int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -822,6 +900,7 @@ binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(args.userId); // Locking is performed depeer in the callstack. int64_t ceDataInode = -1; @@ -837,6 +916,10 @@ binder::Status InstalldNativeService::createAppDataBatched( const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + for (const auto& arg : args) { + ENFORCE_VALID_USER(arg.userId); + } + // Locking is performed depeer in the callstack. std::vector<android::os::CreateAppDataResult> results; @@ -851,6 +934,7 @@ binder::Status InstalldNativeService::createAppDataBatched( binder::Status InstalldNativeService::reconcileSdkData( const android::os::ReconcileSdkDataArgs& args) { + ENFORCE_VALID_USER(args.userId); // Locking is performed depeer in the callstack. return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId, @@ -874,6 +958,7 @@ binder::Status InstalldNativeService::reconcileSdkData(const std::optional<std:: int userId, int appId, int previousAppId, const std::string& seInfo, int flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -957,6 +1042,7 @@ binder::Status InstalldNativeService::reconcileSdkData(const std::optional<std:: binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1024,6 +1110,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1115,6 +1202,7 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -1201,6 +1289,7 @@ binder::Status InstalldNativeService::deleteReferenceProfile(const std::string& binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1271,6 +1360,8 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); + const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -1418,6 +1509,7 @@ binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::s int32_t userId, int32_t snapshotId, int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1552,6 +1644,7 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( const int32_t appId, const std::string& seInfo, const int32_t userId, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1624,6 +1717,7 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -1657,6 +1751,7 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( const std::optional<std::string>& volumeUuid, const int32_t userId, const std::vector<int32_t>& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); LOCK_USER(); @@ -1847,9 +1942,12 @@ fail: binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); LOCK_USER(); + ScopedTrace tracer("create-user-data"); + const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { if (uuid_ == nullptr) { @@ -1865,6 +1963,7 @@ binder::Status InstalldNativeService::createUserData(const std::optional<std::st binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); LOCK_USER(); @@ -2355,11 +2454,15 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st p->fts_number = p->fts_parent->fts_number; switch (p->fts_info) { case FTS_D: - if (p->fts_level == 4 + if (p->fts_level == 3 + && !strcmp(p->fts_parent->fts_name, "obb") + && !strcmp(p->fts_parent->fts_parent->fts_name, "Android")) { + p->fts_number = 1; + } else if (p->fts_level == 4 && !strcmp(p->fts_name, "cache") && !strcmp(p->fts_parent->fts_parent->fts_name, "data") && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) { - p->fts_number = 1; + p->fts_number = 2; } [[fallthrough]]; // to count the directory case FTS_DEFAULT: @@ -2368,9 +2471,13 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st case FTS_SLNONE: int64_t size = (p->fts_statp->st_blocks * 512); if (p->fts_number == 1) { - stats->cacheSize += size; + stats->codeSize += size; + } else { + if (p->fts_number == 2) { + stats->cacheSize += size; + } + stats->dataSize += size; } - stats->dataSize += size; break; } } @@ -2644,6 +2751,7 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. @@ -2716,11 +2824,6 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin extStats.dataSize = dataSize; atrace_pm_end(); } else { - atrace_pm_begin("obb"); - auto obbPath = create_data_path(uuid_) + "/media/obb"; - calculate_tree_size(obbPath, &extStats.codeSize); - atrace_pm_end(); - atrace_pm_begin("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize); atrace_pm_end(); @@ -2751,9 +2854,10 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin atrace_pm_begin("external"); auto dataMediaPath = create_data_media_path(uuid_, userId); collectManualExternalStatsForUser(dataMediaPath, &extStats); + #if MEASURE_DEBUG LOG(DEBUG) << "Measured external data " << extStats.dataSize << " cache " - << extStats.cacheSize; + << extStats.cacheSize << " code " << extStats.codeSize; #endif atrace_pm_end(); @@ -2783,6 +2887,7 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. @@ -2903,6 +3008,7 @@ binder::Status InstalldNativeService::getAppCrates( const std::vector<std::string>& packageNames, int32_t userId, std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); for (const auto& packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -2952,6 +3058,7 @@ binder::Status InstalldNativeService::getUserCrates( const std::optional<std::string>& uuid, int32_t userId, std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES LOCK_USER(); @@ -2995,6 +3102,7 @@ binder::Status InstalldNativeService::getUserCrates( binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid, int32_t userId, int32_t appId, int64_t cacheQuota) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); std::lock_guard<std::recursive_mutex> lock(mQuotasLock); @@ -3238,6 +3346,7 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); LOCK_PACKAGE_USER(); @@ -3248,6 +3357,7 @@ binder::Status InstalldNativeService::restoreconAppDataLocked( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -3279,6 +3389,7 @@ binder::Status InstalldNativeService::restoreconSdkDataLocked( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -3555,22 +3666,22 @@ binder::Status InstalldNativeService::tryMountDataMirror( std::lock_guard<std::recursive_mutex> lock(mMountsLock); std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); - if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE data mirror"); } std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_)); - if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create DE data mirror"); } std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_)); - if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE misc mirror"); } std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_)); - if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { + if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0511, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create DE misc mirror"); } @@ -3730,6 +3841,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, const std::optional<std::string>& dexMetadata, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + ENFORCE_VALID_USER(userId); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); LOCK_PACKAGE_USER(); @@ -3752,6 +3864,7 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { binder::Status InstalldNativeService::cleanupInvalidPackageDirs( const std::optional<std::string>& uuid, int32_t userId, int32_t flags) { + ENFORCE_VALID_USER(userId); const char* uuid_cstr = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_CE) { @@ -3791,5 +3904,84 @@ binder::Status InstalldNativeService::getOdexVisibility( return *_aidl_return == -1 ? error() : ok(); } +// Creates an auth token to be used in enableFsverity. This token is really to store a proof that +// the caller can write to a file, represented by the authFd. Effectively, system_server as the +// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable, +// return null. +// +// appUid and userId are passed for additional ownership check, such that one app can not be +// authenticated for another app's file. These parameters are assumed trusted for this purpose of +// consistency check. +// +// Notably, creating the token allows us to manage the writable FD easily during enableFsverity. +// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the +// server allows the server to hold the only reference (as long as the client app doesn't). +binder::Status InstalldNativeService::createFsveritySetupAuthToken( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId, + sp<IFsveritySetupAuthToken>* _aidl_return) { + CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid); + ENFORCE_VALID_USER(userId); + + auto token = sp<FsveritySetupAuthToken>::make(); + binder::Status status = token->authenticate(authFd, appUid, userId); + if (!status.isOk()) { + return status; + } + *_aidl_return = token; + return ok(); +} + +// Enables fs-verity for filePath, which must be an absolute path and the same inode as in the auth +// token previously returned from createFsveritySetupAuthToken, and owned by the app uid. As +// installd is more privileged than its client / system server, we attempt to limit what a +// (compromised) client can do. +// +// The reason for this app request to go through installd is to avoid exposing a risky area (PKCS#7 +// signature verification) in the kernel to the app as an attack surface (it can't be system server +// because it can't override DAC and manipulate app files). Note that we should be able to drop +// these hops and simply the app calls the ioctl, once all upgrading devices run with a kernel +// without fs-verity built-in signature (https://r.android.com/2650402). +binder::Status InstalldNativeService::enableFsverity(const sp<IFsveritySetupAuthToken>& authToken, + const std::string& filePath, + const std::string& packageName, + int32_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(filePath); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE(); + if (authToken == nullptr) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token"); + } + + // Authenticate to check the targeting file is the same inode as the authFd. + sp<IBinder> authTokenBinder = IInterface::asBinder(authToken)->localBinder(); + if (authTokenBinder == nullptr) { + return exception(binder::Status::EX_SECURITY, "Received a non-local auth token"); + } + auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder); + unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + struct stat stFromPath; + if (fstat(rfd.get(), &stFromPath) < 0) { + *_aidl_return = errno; + return ok(); + } + if (!authTokenInstance->isSameStat(stFromPath)) { + LOG(DEBUG) << "FD authentication failed"; + *_aidl_return = EPERM; + return ok(); + } + + fsverity_enable_arg arg = {}; + arg.version = 1; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.block_size = 4096; + if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) { + *_aidl_return = errno; + } else { + *_aidl_return = 0; + } + return ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 521afc3f97..0f28234422 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -19,6 +19,7 @@ #define COMMANDS_H_ #include <inttypes.h> +#include <sys/stat.h> #include <unistd.h> #include <shared_mutex> @@ -35,8 +36,26 @@ namespace android { namespace installd { +using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken; + class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld { public: + class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken { + public: + FsveritySetupAuthToken() : mStatFromAuthFd() {} + + binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid, + int32_t userId); + bool isSameStat(const struct stat& st) const; + + private: + // Not copyable or movable + FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete; + FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete; + + struct stat mStatFromAuthFd; + }; + static status_t start(); static char const* getServiceName() { return "installd"; } virtual status_t dump(int fd, const Vector<String16> &args) override; @@ -192,6 +211,13 @@ public: const std::optional<std::string>& outputPath, int32_t* _aidl_return); + binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd, + int32_t appUid, int32_t userId, + android::sp<IFsveritySetupAuthToken>* _aidl_return); + binder::Status enableFsverity(const android::sp<IFsveritySetupAuthToken>& authToken, + const std::string& filePath, const std::string& packageName, + int32_t* _aidl_return); + private: std::recursive_mutex mLock; std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h index 18506a9258..0deaeb4341 100644 --- a/cmds/installd/SysTrace.h +++ b/cmds/installd/SysTrace.h @@ -19,4 +19,16 @@ namespace android::installd { void atrace_pm_begin(const char*); void atrace_pm_end(); + +class ScopedTrace { +public: + explicit ScopedTrace(const char* label) { atrace_pm_begin(label); } + ~ScopedTrace() { atrace_pm_end(); } + +private: + ScopedTrace(const ScopedTrace&) = delete; + ScopedTrace& operator=(const ScopedTrace&) = delete; + ScopedTrace(ScopedTrace&&) = delete; + ScopedTrace& operator=(ScopedTrace&&) = delete; +}; } /* namespace android::installd */ diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 9ad853b1df..8893e38c03 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -134,6 +134,22 @@ interface IInstalld { int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath, @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + interface IFsveritySetupAuthToken { + // Using an interface here is an easy way to create and maintain an IBinder object across + // the processes. When installd creates this binder object, it stores the file stat + // privately for later authentication, and only returns the reference to the caller process. + // Once the binder object has no reference count, it gets destructed automatically + // (alternatively, installd can maintain an internal mapping, but it is more error prone + // because the app may crash and not finish the fs-verity setup, keeping the memory unused + // forever). + // + // We don't necessarily need a method here, so it's left blank intentionally. + } + IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid, + int userId); + int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath, + @utf8InCpp String packageName); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 5cf402c54c..df02588a22 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -18,6 +18,7 @@ #define DEXOPT_H_ #include "installd_constants.h" +#include "unique_file.h" #include <sys/types.h> @@ -156,6 +157,10 @@ const char* select_execution_binary( // artifacts. int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir); +UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path, + const char* profile_name, bool profile_guided, + bool is_public, int uid, bool is_secondary_dex); + } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 7cabdb09e1..27ae8f6e6f 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -14,20 +14,21 @@ ** limitations under the License. */ -#include <algorithm> #include <inttypes.h> -#include <limits> -#include <random> -#include <regex> #include <selinux/android.h> #include <selinux/avc.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> +#include <sys/mman.h> #include <sys/prctl.h> #include <sys/stat.h> -#include <sys/mman.h> #include <sys/wait.h> +#include <algorithm> +#include <iterator> +#include <limits> +#include <random> +#include <regex> #include <android-base/logging.h> #include <android-base/macros.h> @@ -47,6 +48,7 @@ #include "otapreopt_parameters.h" #include "otapreopt_utils.h" #include "system_properties.h" +#include "unique_file.h" #include "utils.h" #ifndef LOG_TAG @@ -87,6 +89,9 @@ static_assert(DEXOPT_GENERATE_APP_IMAGE == 1 << 12, "DEXOPT_GENERATE_APP_IMAGE u static_assert(DEXOPT_MASK == (0x3dfe | DEXOPT_IDLE_BACKGROUND_JOB), "DEXOPT_MASK unexpected."); +constexpr const char* kAotCompilerFilters[]{ + "space-profile", "space", "speed-profile", "speed", "everything-profile", "everything", +}; template<typename T> static constexpr bool IsPowerOfTwo(T x) { @@ -415,6 +420,32 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } + bool IsAotCompilation() const { + if (std::find(std::begin(kAotCompilerFilters), std::end(kAotCompilerFilters), + parameters_.compiler_filter) == std::end(kAotCompilerFilters)) { + return false; + } + + int dexopt_flags = parameters_.dexopt_flags; + bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0; + bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0; + bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0; + + if (profile_guided) { + UniqueFile reference_profile = + maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path, + parameters_.profile_name, profile_guided, + is_public, parameters_.uid, is_secondary_dex); + struct stat sbuf; + if (reference_profile.fd() == -1 || + (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) { + return false; + } + } + + return true; + } + bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, @@ -439,9 +470,12 @@ private: // (This is ugly as it's the only thing where we need to understand the contents // of parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) + + // In addition, no need to preopt for "verify". The existing vdex files in the OTA package + // and the /data partition will still be usable after the OTA update is applied. const char* apk_path = parameters_.apk_path; CHECK(apk_path != nullptr); - if (StartsWith(apk_path, android_root_)) { + if (StartsWith(apk_path, android_root_) || !IsAotCompilation()) { const char* last_slash = strrchr(apk_path, '/'); if (last_slash != nullptr) { std::string path(apk_path, last_slash - apk_path + 1); @@ -471,13 +505,18 @@ private: // TODO(calin): embed the profile name in the parameters. int Dexopt() { std::string error; + + int dexopt_flags = parameters_.dexopt_flags; + // Make sure dex2oat is run with background priority. + dexopt_flags |= DEXOPT_BOOTCOMPLETE | DEXOPT_IDLE_BACKGROUND_JOB; + int res = dexopt(parameters_.apk_path, parameters_.uid, parameters_.pkgName, parameters_.instruction_set, parameters_.dexopt_needed, parameters_.oat_dir, - parameters_.dexopt_flags, + dexopt_flags, parameters_.compiler_filter, parameters_.volume_uuid, parameters_.shared_libraries, @@ -521,61 +560,6 @@ private: return Dexopt(); } - //////////////////////////////////// - // Helpers, mostly taken from ART // - //////////////////////////////////// - - // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. - static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { - constexpr size_t kPageSize = PAGE_SIZE; - static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two"); - CHECK_EQ(min_delta % kPageSize, 0u); - CHECK_EQ(max_delta % kPageSize, 0u); - CHECK_LT(min_delta, max_delta); - - std::default_random_engine generator; - generator.seed(GetSeed()); - std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta); - int32_t r = distribution(generator); - if (r % 2 == 0) { - r = RoundUp(r, kPageSize); - } else { - r = RoundDown(r, kPageSize); - } - CHECK_LE(min_delta, r); - CHECK_GE(max_delta, r); - CHECK_EQ(r % kPageSize, 0u); - return r; - } - - static uint64_t GetSeed() { -#ifdef __BIONIC__ - // Bionic exposes arc4random, use it. - uint64_t random_data; - arc4random_buf(&random_data, sizeof(random_data)); - return random_data; -#else -#error "This is only supposed to run with bionic. Otherwise, implement..." -#endif - } - - void AddCompilerOptionFromSystemProperty(const char* system_property, - const char* prefix, - bool runtime, - std::vector<std::string>& out) const { - const std::string* value = system_properties_.GetProperty(system_property); - if (value != nullptr) { - if (runtime) { - out.push_back("--runtime-arg"); - } - if (prefix != nullptr) { - out.push_back(StringPrintf("%s%s", prefix, value->c_str())); - } else { - out.push_back(*value); - } - } - } - static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 4221a3a593..7648265f0f 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -208,36 +208,13 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, } // Compute compiler filter. - { - std::string dex2oat_compiler_filter_arg; - { - // If we are booting without the real /data, don't spend time compiling. - std::string vold_decrypt = GetProperty("vold.decrypt", ""); - bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || - vold_decrypt == "1"; - - bool have_dex2oat_relocation_skip_flag = false; - if (skip_compilation) { - dex2oat_compiler_filter_arg = "--compiler-filter=extract"; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", - compiler_filter); - } - if (have_dex2oat_relocation_skip_flag) { - AddRuntimeArg("-Xnorelocate"); - } - } - - if (dex2oat_compiler_filter_arg.empty()) { - dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", - "--compiler-filter=%s"); - } - AddArg(dex2oat_compiler_filter_arg); - - if (compilation_reason != nullptr) { - AddArg(std::string("--compilation-reason=") + compilation_reason); - } + if (compiler_filter != nullptr) { + AddArg(StringPrintf("--compiler-filter=%s", compiler_filter)); + } else { + AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s")); + } + if (compilation_reason != nullptr) { + AddArg(std::string("--compilation-reason=") + compilation_reason); } AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 304ba7b04f..56f84a5e12 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -441,24 +441,6 @@ TEST_F(RunDex2OatTest, Runtime) { VerifyExpectedFlags(); } -TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) { - setSystemProperty("vold.decrypt", "trigger_restart_min_framework"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - -TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) { - setSystemProperty("vold.decrypt", "1"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) { setSystemProperty("dalvik.vm.dex2oat-filter", "speed"); auto args = RunDex2OatArgs::MakeDefaultTestArgs(); diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 07f73b9029..61fe316225 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -77,10 +77,8 @@ cc_test { }, } -cc_test { - name: "installd_service_test", - test_suites: ["device-tests"], - srcs: ["installd_service_test.cpp"], +cc_defaults { + name: "installd_service_test_defaults", cflags: [ "-Wall", "-Werror", @@ -106,8 +104,6 @@ cc_test { "liblogwrap", "libc++fs", ], - test_config: "installd_service_test.xml", - product_variables: { arc: { exclude_srcs: [ @@ -125,6 +121,14 @@ cc_test { } cc_test { + name: "installd_service_test", + test_suites: ["device-tests"], + srcs: ["installd_service_test.cpp"], + defaults: ["installd_service_test_defaults"], + test_config: "installd_service_test.xml", +} + +cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], srcs: ["installd_dexopt_test.cpp"], @@ -209,3 +213,19 @@ cc_test { "liblog", ], } + +cc_fuzz { + name: "installd_service_fuzzer", + defaults: [ + "service_fuzzer_defaults", + "fuzzer_disable_leaks", + "installd_service_test_defaults", + ], + srcs: ["fuzzers/InstalldServiceFuzzer.cpp"], + fuzz_config: { + cc: [ + "android-package-manager-team@google.com", + ], + triage_assignee: "waghpawan@google.com", + }, +} diff --git a/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp new file mode 100644 index 0000000000..b1c6940207 --- /dev/null +++ b/cmds/installd/tests/fuzzers/InstalldServiceFuzzer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h> + +#include "InstalldNativeService.h" +#include "dexopt.h" + +using ::android::fuzzService; +using ::android::sp; +using ::android::installd::InstalldNativeService; + +namespace android { +namespace installd { + +bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char* oat_dir, const char* apk_path, + const char* instruction_set) { + return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); +} + +bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char* apk_path, + const char* instruction_set) { + return calculate_odex_file_path_default(path, apk_path, instruction_set); +} + +bool create_cache_path(char path[PKG_PATH_MAX], const char* src, const char* instruction_set) { + return create_cache_path_default(path, src, instruction_set); +} + +bool force_compile_without_image() { + return false; +} + +} // namespace installd +} // namespace android + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto service = sp<InstalldNativeService>::make(); + fuzzService(service, FuzzedDataProvider(data, size)); + return 0; +}
\ No newline at end of file diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 858a92cc65..4bc92afa18 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -42,9 +42,12 @@ #include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" +#include "unique_file.h" #include "utils.h" using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::filesystem::is_empty; namespace android { @@ -136,6 +139,16 @@ static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode return fd; } +static void create_with_content(const std::string& path, uid_t owner, gid_t group, mode_t mode, + const std::string& content) { + int fd = ::open(path.c_str(), O_RDWR | O_CREAT, mode); + EXPECT_NE(fd, -1); + EXPECT_TRUE(android::base::WriteStringToFd(content, fd)); + EXPECT_EQ(::fchown(fd, owner, group), 0); + EXPECT_EQ(::fchmod(fd, mode), 0); + close(fd); +} + static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) { EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0); } @@ -527,6 +540,94 @@ TEST_F(ServiceTest, GetAppSizeWrongSizes) { externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize)); } + +class FsverityTest : public ServiceTest { +protected: + binder::Status createFsveritySetupAuthToken(const std::string& path, int open_mode, + sp<IFsveritySetupAuthToken>* _aidl_return) { + unique_fd ufd(open(path.c_str(), open_mode)); + EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno); + ParcelFileDescriptor rfd(std::move(ufd)); + return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId, + _aidl_return); + } +}; + +TEST_F(FsverityTest, enableFsverity) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token works to enable fs-verity + int32_t errno_local; + status = service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(errno_local, 0); +} + +TEST_F(FsverityTest, enableFsverity_nullAuthToken) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Verity null auth token fails + sp<IFsveritySetupAuthToken> authToken; + int32_t errno_local; + binder::Status status = + service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, enableFsverity_differentFile) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token does not work for a different file + const std::string anotherPath = kTestPath + "/bar"; + ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath)); + UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); }); + int32_t errno_local; + status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_NE(errno_local, 0); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDONLY, &authToken); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) { + const std::string path = kTestPath + "/foo"; + // Simulate world-writable file owned by another app + create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_FALSE(status.isOk()); +} + static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index ecea1d2b1c..c43fdbd547 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -18,6 +18,7 @@ #ifndef UTILS_H_ #define UTILS_H_ +#include <functional> #include <string> #include <vector> diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index af85666276..d0e4b74543 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -37,4 +37,8 @@ cc_library_static { "include", ], min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.neuralnetworks", + ], } diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index fb69513d24..d73a30bf9b 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -93,22 +93,9 @@ cc_fuzz { libfuzzer_options: [ "max_len=50000", ], - }, -} - -// Adding this new fuzzer to test the corpus generated by record_binder -cc_fuzz { - name: "servicemanager_test_fuzzer", - defaults: [ - "servicemanager_defaults", - "service_fuzzer_defaults", - ], - host_supported: true, - srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"], - fuzz_config: { - libfuzzer_options: [ - "max_len=50000", + cc: [ + "smoreland@google.com", + "waghpawan@google.com", ], }, - corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"], } diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 63f382170c..f6e4ec3c0d 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -301,7 +301,7 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN } if (!out && startIfNotFound) { - tryStartService(name); + tryStartService(ctx, name); } if (out) { @@ -372,8 +372,10 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi } auto it = mNameToService.find(name); + bool prevClients = false; if (it != mNameToService.end()) { const Service& existing = it->second; + prevClients = existing.hasClients; // We could do better than this because if the other service dies, it // may not have an entry here. However, this case is unlikely. We are @@ -401,12 +403,14 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi .binder = binder, .allowIsolated = allowIsolated, .dumpPriority = dumpPriority, + .hasClients = prevClients, // see b/279898063, matters if existing callbacks + .guaranteeClient = false, .ctx = ctx, }; if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) { - // See also getService - handles case where client never gets the service, - // we want the service to quit. + // If someone is currently waiting on the service, notify the service that + // we're waiting and flush it to the service. mNameToService[name].guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); mNameToService[name].guaranteeClient = true; @@ -633,6 +637,14 @@ void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who, void ServiceManager::binderDied(const wp<IBinder>& who) { for (auto it = mNameToService.begin(); it != mNameToService.end();) { if (who == it->second.binder) { + // TODO: currently, this entry contains the state also + // associated with mNameToClientCallback. If we allowed + // other processes to register client callbacks, we + // would have to preserve hasClients (perhaps moving + // that state into mNameToClientCallback, which is complicated + // because those callbacks are associated w/ particular binder + // objects, though they are indexed by name now, they may + // need to be indexed by binder at that point). it = mNameToService.erase(it); } else { ++it; @@ -648,10 +660,11 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { } } -void ServiceManager::tryStartService(const std::string& name) { - ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not " - "configured to be a lazy service, it may be stuck starting or still starting).", - name.c_str()); +void ServiceManager::tryStartService(const Access::CallingContext& ctx, const std::string& name) { + ALOGI("Since '%s' could not be found (requested by debug pid %d), trying to start it as a lazy " + "AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or " + "still starting).", + name.c_str(), ctx.debugPid); std::thread([=] { if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) { @@ -700,13 +713,21 @@ Status ServiceManager::registerClientCallback(const std::string& name, const sp< return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath."); } - // make sure all callbacks have been told about a consistent state - b/278038751 + // WARNING: binderDied makes an assumption about this. If we open up client + // callbacks to other services, certain race conditions may lead to services + // getting extra client callback notifications. + // Make sure all callbacks have been told about a consistent state - b/278038751 if (serviceIt->second.hasClients) { cb->onClients(service, true); } mNameToClientCallback[name].push_back(cb); + // Flush updated info to client callbacks (especially if guaranteeClient + // and !hasClient, see b/285202885). We may or may not have clients at + // this point, so ignore the return value. + (void)handleServiceClientCallback(2 /* sm + transaction */, name, false); + return Status::ok(); } diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 3aa6731eb3..3b925a48cb 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -67,7 +67,7 @@ public: void clear(); protected: - virtual void tryStartService(const std::string& name); + virtual void tryStartService(const Access::CallingContext& ctx, const std::string& name); private: struct Service { diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp deleted file mode 100644 index e19b6eb279..0000000000 --- a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h> -#include <utils/StrongPointer.h> - -#include "Access.h" -#include "ServiceManager.h" - -using ::android::Access; -using ::android::Parcel; -using ::android::ServiceManager; -using ::android::sp; - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - FuzzedDataProvider provider(data, size); - auto accessPtr = std::make_unique<Access>(); - auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr)); - - // Reserved bytes - provider.ConsumeBytes<uint8_t>(8); - uint32_t code = provider.ConsumeIntegral<uint32_t>(); - uint32_t flag = provider.ConsumeIntegral<uint32_t>(); - std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>(); - - Parcel inputParcel; - inputParcel.setData(parcelData.data(), parcelData.size()); - - Parcel reply; - serviceManager->transact(code, inputParcel, &reply, flag); - - serviceManager->clear(); - - return 0; -} diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 Binary files differdeleted file mode 100644 index e69ab49d5d..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 Binary files differdeleted file mode 100644 index 88ad474f09..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 Binary files differdeleted file mode 100644 index fae15a2fea..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 Binary files differdeleted file mode 100644 index b326907a58..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 Binary files differdeleted file mode 100644 index 05b27bf413..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 Binary files differdeleted file mode 100644 index b326907a58..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 Binary files differdeleted file mode 100644 index cdaa1f01b1..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 Binary files differdeleted file mode 100644 index ff0941b7a6..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 Binary files differdeleted file mode 100644 index cdaa1f01b1..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 Binary files differdeleted file mode 100644 index 7e5f948682..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 Binary files differdeleted file mode 100644 index 07319f864e..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 +++ /dev/null diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 Binary files differdeleted file mode 100644 index 39e5104927..0000000000 --- a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 +++ /dev/null diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index c1a04dde89..86a45e61ea 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -131,7 +131,9 @@ int main(int argc, char** argv) { } IPCThreadState::self()->setTheContextObject(manager); - ps->becomeContextManager(); + if (!ps->becomeContextManager()) { + LOG(FATAL) << "Could not become context manager"; + } sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc index b927c018df..6354fd7d53 100644 --- a/cmds/servicemanager/servicemanager.recovery.rc +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -1,5 +1,6 @@ service servicemanager /system/bin/servicemanager disabled group system readproc + user root onrestart setprop servicemanager.ready false seclabel u:r:servicemanager:s0 diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index cae32e3bc3..97e500d0a7 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -27,11 +27,14 @@ #include "Access.h" #include "ServiceManager.h" -using android::sp; using android::Access; using android::BBinder; using android::IBinder; using android::ServiceManager; +using android::sp; +using android::base::EndsWith; +using android::base::GetProperty; +using android::base::StartsWith; using android::binder::Status; using android::os::BnServiceCallback; using android::os::IServiceManager; @@ -62,7 +65,7 @@ public: class MockServiceManager : public ServiceManager { public: MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {} - MOCK_METHOD1(tryStartService, void(const std::string& name)); + MOCK_METHOD2(tryStartService, void(const Access::CallingContext&, const std::string& name)); }; static sp<ServiceManager> getPermissiveServiceManager() { @@ -77,9 +80,11 @@ static sp<ServiceManager> getPermissiveServiceManager() { return sm; } -static bool isCuttlefish() { - return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""), - "vsoc_"); +// Determines if test device is a cuttlefish phone device +static bool isCuttlefishPhone() { + auto device = GetProperty("ro.product.vendor.device", ""); + auto product = GetProperty("ro.product.vendor.name", ""); + return StartsWith(device, "vsoc_") && EndsWith(product, "_phone"); } TEST(AddService, HappyHappy) { @@ -314,7 +319,7 @@ TEST(ListServices, CriticalServices) { } TEST(Vintf, UpdatableViaApex) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::optional<std::string> updatableViaApex; @@ -326,7 +331,7 @@ TEST(Vintf, UpdatableViaApex) { } TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::optional<std::string> updatableViaApex; @@ -337,7 +342,7 @@ TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) { } TEST(Vintf, GetUpdatableNames) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::vector<std::string> names; @@ -348,7 +353,7 @@ TEST(Vintf, GetUpdatableNames) { } TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) { - if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices"; + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; auto sm = getPermissiveServiceManager(); std::vector<std::string> names; diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 226cae12aa..5f0bc7541b 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -107,6 +107,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.nfc.prebuilt.xml", + src: "android.hardware.nfc.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.reboot_escrow.prebuilt.xml", src: "android.hardware.reboot_escrow.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -263,6 +269,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.thread_network.prebuilt.xml", + src: "android.hardware.thread_network.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.usb.accessory.prebuilt.xml", src: "android.hardware.usb.accessory.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.thread_network.xml b/data/etc/android.hardware.thread_network.xml new file mode 100644 index 0000000000..b116ed6bf8 --- /dev/null +++ b/data/etc/android.hardware.thread_network.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> +<!-- Adds the feature indicating support for the Thread networking protocol --> +<permissions> + <feature name="android.hardware.thread_network" /> +</permissions> diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 5e539f24e1..1a9766d72e 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -72,6 +72,7 @@ cc_library_static { "//apex_available:platform", "com.android.media", "com.android.media.swcodec", + "com.android.neuralnetworks", ], } diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 49dd9c7366..6c2b313f8a 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -144,10 +144,6 @@ cc_defaults { "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], - }, - debuggable: { cflags: [ "-DBINDER_RPC_DEV_SERVERS", @@ -194,6 +190,9 @@ cc_defaults { "-performance-move-const-arg", // b/273486801 "portability*", ], + lto: { + thin: true, + }, } cc_library_headers { @@ -285,14 +284,6 @@ cc_defaults { cflags: [ "-DBINDER_WITH_KERNEL_IPC", ], - arch: { - // TODO(b/254713216): undefined symbol in BufferedTextOutput::getBuffer - riscv64: { - lto: { - thin: false, - }, - }, - }, } cc_library { @@ -531,7 +522,6 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", - "libcutils_sockets", "liblog", "libutils", ], @@ -548,6 +538,7 @@ cc_library { ":__subpackages__", "//packages/modules/Virtualization/javalib/jni", "//packages/modules/Virtualization/vm_payload", + "//packages/modules/Virtualization/demo_native", "//device/google/cuttlefish/shared/minidroid:__subpackages__", "//system/software_defined_vehicle:__subpackages__", ], diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 3e49656575..0f4a6cabde 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -58,15 +58,15 @@ static_assert(sizeof(BBinder) == 20); // global b/c b/230079120 - consistent symbol table #ifdef BINDER_RPC_DEV_SERVERS -bool kEnableRpcDevServers = true; +constexpr bool kEnableRpcDevServers = true; #else -bool kEnableRpcDevServers = false; +constexpr bool kEnableRpcDevServers = false; #endif #ifdef BINDER_ENABLE_RECORDING -bool kEnableRecording = true; +constexpr bool kEnableRecording = true; #else -bool kEnableRecording = false; +constexpr bool kEnableRecording = false; #endif // Log any reply transactions for which the data exceeds this size diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index f66993f926..7644806e2b 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -324,6 +324,10 @@ LazyServiceRegistrar& LazyServiceRegistrar::getInstance() { return *registrarInstance; } +LazyServiceRegistrar LazyServiceRegistrar::createExtraTestInstance() { + return LazyServiceRegistrar(); +} + status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name, bool allowIsolated, int dumpFlags) { if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) { diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 8fe1d2bb3d..3da06ba4db 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -78,7 +78,7 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) { ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name, SEAL_FLAGS, strerror(errno)); - munmap(mBase, mSize); + if (mNeedUnmap) munmap(mBase, mSize); mBase = nullptr; mSize = 0; close(fd); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 0aca163eab..9b685f9145 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -947,7 +947,10 @@ bool Parcel::enforceInterface(const char16_t* interface, threadState->setCallingWorkSourceUidWithoutPropagation(workSource); // vendor header int32_t header = readInt32(); - if (header != kHeader) { + + // fuzzers skip this check, because it is for protecting the underlying ABI, but + // we don't want it to reduce our coverage + if (header != kHeader && !mServiceFuzzing) { ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header); return false; @@ -966,10 +969,18 @@ bool Parcel::enforceInterface(const char16_t* interface, (!len || !memcmp(parcel_interface, interface, len * sizeof (char16_t)))) { return true; } else { - ALOGW("**** enforceInterface() expected '%s' but read '%s'", - String8(interface, len).string(), - String8(parcel_interface, parcel_interface_len).string()); - return false; + if (mServiceFuzzing) { + // ignore. Theoretically, this could cause a few false positives, because + // people could assume things about getInterfaceDescriptor if they pass + // this point, but it would be extremely fragile. It's more important that + // we fuzz with the above things read from the Parcel. + return true; + } else { + ALOGW("**** enforceInterface() expected '%s' but read '%s'", + String8(interface, len).string(), + String8(parcel_interface, parcel_interface_len).string()); + return false; + } } } @@ -977,6 +988,14 @@ void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) { mEnforceNoDataAvail = enforceNoDataAvail; } +void Parcel::setServiceFuzzing() { + mServiceFuzzing = true; +} + +bool Parcel::isServiceFuzzing() const { + return mServiceFuzzing; +} + binder::Status Parcel::enforceNoDataAvail() const { if (!mEnforceNoDataAvail) { return binder::Status::ok(); @@ -1722,7 +1741,9 @@ status_t Parcel::validateReadData(size_t upperBound) const do { if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) { // Requested info overlaps with an object - ALOGE("Attempt to read from protected data in Parcel %p", this); + if (!mServiceFuzzing) { + ALOGE("Attempt to read from protected data in Parcel %p", this); + } return PERMISSION_DENIED; } nextObject++; @@ -2092,7 +2113,11 @@ String8 Parcel::readString8() const size_t len; const char* str = readString8Inplace(&len); if (str) return String8(str, len); - ALOGE("Reading a NULL string not supported here."); + + if (!mServiceFuzzing) { + ALOGE("Reading a NULL string not supported here."); + } + return String8(); } @@ -2132,7 +2157,11 @@ String16 Parcel::readString16() const size_t len; const char16_t* str = readString16Inplace(&len); if (str) return String16(str, len); - ALOGE("Reading a NULL string not supported here."); + + if (!mServiceFuzzing) { + ALOGE("Reading a NULL string not supported here."); + } + return String16(); } @@ -2172,7 +2201,9 @@ status_t Parcel::readStrongBinder(sp<IBinder>* val) const { status_t status = readNullableStrongBinder(val); if (status == OK && !val->get()) { - ALOGW("Expecting binder but got null!"); + if (!mServiceFuzzing) { + ALOGW("Expecting binder but got null!"); + } status = UNEXPECTED_NULL; } return status; @@ -2237,9 +2268,11 @@ 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); + if (!mServiceFuzzing) { + ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in " + "the object list", + this, mDataPos); + } return BAD_TYPE; } @@ -2497,8 +2530,11 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const return obj; } } - ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list", - this, DPOS); + if (!mServiceFuzzing) { + ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object " + "list", + this, DPOS); + } } return nullptr; } @@ -3093,6 +3129,7 @@ void Parcel::initState() mDeallocZero = false; mOwner = nullptr; mEnforceNoDataAvail = true; + mServiceFuzzing = false; } void Parcel::scanForFds() const { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 5f1f50672a..02b0447304 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -104,14 +104,7 @@ bool ProcessState::isVndservicemanagerEnabled() { return access("/vendor/bin/vndservicemanager", R_OK) == 0; } -sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) -{ -#ifdef BINDER_IPC_32BIT - LOG_ALWAYS_FATAL("32-bit binder IPC is not supported for new devices starting in Android P. If " - "you do need to use this mode, please see b/232423610 or file an issue with " - "AOSP upstream as otherwise this will be removed soon."); -#endif - +sp<ProcessState> ProcessState::init(const char* driver, bool requireDefault) { if (driver == nullptr) { std::lock_guard<std::mutex> l(gProcessMutex); if (gProcess) { @@ -199,6 +192,7 @@ void ProcessState::startThreadPool() AutoMutex _l(mLock); if (!mThreadPoolStarted) { if (mMaxThreads == 0) { + // see also getThreadPoolMaxTotalThreadCount ALOGW("Extra binder thread started, but 0 threads requested. Do not use " "*startThreadPool when zero threads are requested."); } @@ -414,6 +408,11 @@ void ProcessState::spawnPooledThread(bool isMain) mKernelStartedThreads++; pthread_mutex_unlock(&mThreadCountLock); } + // TODO: if startThreadPool is called on another thread after the process + // starts up, the kernel might think that it already requested those + // binder threads, and additional won't be started. This is likely to + // cause deadlocks, and it will also cause getThreadPoolMaxTotalThreadCount + // to return too high of a value. } status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { @@ -433,12 +432,32 @@ 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 - ? mMaxThreads - : mMaxThreads + mCurrentThreads - mKernelStartedThreads; + LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1, + "too many kernel-started threads: %zu > %zu + 1", mKernelStartedThreads, + mMaxThreads); + + // calling startThreadPool starts a thread + size_t threads = 1; + + // the kernel is configured to start up to mMaxThreads more threads + threads += mMaxThreads; + + // Users may call IPCThreadState::joinThreadPool directly. We don't + // currently have a way to count this directly (it could be added by + // adding a separate private joinKernelThread method in IPCThreadState). + // So, if we are in a race between the kernel thread variable being + // incremented in this file and mCurrentThreads being incremented + // in IPCThreadState, temporarily forget about the extra join threads. + // This is okay, because most callers of this method only care about + // having 0, 1, or more threads. + if (mCurrentThreads > mKernelStartedThreads) { + threads += mCurrentThreads - mKernelStartedThreads; + } + + return threads; } + // must not be initialized or maybe has poll thread setup, we // currently don't track this in libbinder LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0, diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 1c7613584b..44a9e3befa 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -161,17 +161,6 @@ static_assert(sizeof(ChunkDescriptor) % 8 == 0); constexpr uint32_t kMaxChunkDataSize = 0xfffffff0; typedef uint64_t transaction_checksum_t; -static android::status_t readChunkDescriptor(borrowed_fd fd, ChunkDescriptor* chunkOut, - transaction_checksum_t* sum) { - if (!android::base::ReadFully(fd, chunkOut, sizeof(ChunkDescriptor))) { - LOG(ERROR) << "Failed to read Chunk Descriptor from fd " << fd.get(); - return android::UNKNOWN_ERROR; - } - - *sum ^= *reinterpret_cast<transaction_checksum_t*>(chunkOut); - return android::NO_ERROR; -} - std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { RecordedTransaction t; ChunkDescriptor chunk; @@ -192,11 +181,13 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor"; return std::nullopt; } - transaction_checksum_t checksum = 0; - if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) { - LOG(ERROR) << "Failed to read chunk descriptor."; + + if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) { + LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". " + << strerror(errno); return std::nullopt; } + transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk); fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); if (fdCurrentPosition == -1) { diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 9282856f5c..55fc16de45 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -81,6 +81,7 @@ status_t RpcServer::setupInetServer(const char* address, unsigned int port, auto aiStart = InetSocketAddress::getAddrInfo(address, port); if (aiStart == nullptr) return UNKNOWN_ERROR; for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { + if (ai->ai_addr == nullptr) continue; InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, address, port); if (status_t status = setupSocketServer(socketAddress); status != OK) { continue; @@ -123,8 +124,13 @@ size_t RpcServer::getMaxThreads() { return mMaxThreads; } -void RpcServer::setProtocolVersion(uint32_t version) { +bool RpcServer::setProtocolVersion(uint32_t version) { + if (!RpcState::validateProtocolVersion(version)) { + return false; + } + mProtocolVersion = version; + return true; } void RpcServer::setSupportedFileDescriptorTransportModes( @@ -148,7 +154,7 @@ void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function<sp<IBinder>(const void*, size_t)>&& makeObject) { + std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& makeObject) { RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); @@ -161,6 +167,12 @@ void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& f mConnectionFilter = std::move(filter); } +void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) { + RpcMutexLockGuard _l(mLock); + LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started"); + mServerSocketModifier = std::move(modifier); +} + sp<IBinder> RpcServer::getRootObject() { RpcMutexLockGuard _l(mLock); bool hasWeak = mRootObjectWeak.unsafe_get(); @@ -335,6 +347,8 @@ bool RpcServer::shutdown() { mJoinThread.reset(); } + mServer = RpcTransportFd(); + LOG_RPC_DETAIL("Finished waiting on shutdown."); mShutdownTrigger = nullptr; @@ -501,7 +515,8 @@ void RpcServer::establishConnection( // if null, falls back to server root sp<IBinder> sessionSpecificRoot; if (server->mRootObjectFactory != nullptr) { - sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen); + sessionSpecificRoot = + server->mRootObjectFactory(wp<RpcSession>(session), addr.data(), addrLen); if (sessionSpecificRoot == nullptr) { ALOGE("Warning: server returned null from root object factory"); } @@ -556,6 +571,14 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); return -savedErrno; } + + { + RpcMutexLockGuard _l(mLock); + if (mServerSocketModifier != nullptr) { + mServerSocketModifier(socket_fd); + } + } + if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) { int savedErrno = errno; ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index fbad0f7756..c3dee1650e 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -104,11 +104,7 @@ size_t RpcSession::getMaxOutgoingThreads() { } bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) { - if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT && - version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { - ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version " - "is %u).", - version, RPC_WIRE_PROTOCOL_VERSION); + if (!RpcState::validateProtocolVersion(version)) { return false; } diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 03fa69973d..bac2808b26 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -34,6 +34,10 @@ #include <inttypes.h> +#ifdef __ANDROID__ +#include <cutils/properties.h> +#endif + namespace android { using base::StringPrintf; @@ -59,6 +63,7 @@ static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) { case RpcSession::FileDescriptorTransportMode::TRUSTY: return true; } + LOG_ALWAYS_FATAL("Invalid FileDescriptorTransportMode: %d", static_cast<int>(mode)); } RpcState::RpcState() {} @@ -398,6 +403,31 @@ status_t RpcState::rpcRec( return OK; } +bool RpcState::validateProtocolVersion(uint32_t version) { + if (version == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { +#if defined(__ANDROID__) + char codename[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.codename", codename, ""); + if (!strcmp(codename, "REL")) { + ALOGE("Cannot use experimental RPC binder protocol on a release branch."); + return false; + } +#else + // don't restrict on other platforms, though experimental should + // only really be used for testing, we don't have a good way to see + // what is shipping outside of Android +#endif + } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) { + ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol " + "version " + "is %u).", + version, RPC_WIRE_PROTOCOL_VERSION); + return false; + } + + return true; +} + status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, uint32_t* version) { RpcNewSessionResponse response; diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 0e23ea7515..1fe71a5a78 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -63,6 +63,8 @@ public: RpcState(); ~RpcState(); + [[nodiscard]] static bool validateProtocolVersion(uint32_t version); + [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, uint32_t* version); [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection, diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index cd067bfee7..f3575cc7d8 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -29,8 +29,6 @@ namespace android { -namespace { - // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: @@ -96,8 +94,6 @@ public: std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryRaw::newServerCtx() const { return std::make_unique<RpcTransportCtxRaw>(); } diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index d5a6da2e3d..0c81d83032 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -31,8 +31,6 @@ using android::base::Result; namespace android { -namespace { - // RpcTransport for writing Trusty IPC clients in Android. class RpcTransportTipcAndroid : public RpcTransport { public: @@ -217,8 +215,6 @@ public: std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const { return std::make_unique<RpcTransportCtxTipcAndroid>(); } diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index 3e98ecca9b..785f6ce2ce 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -275,6 +275,8 @@ private: bssl::UniquePtr<SSL> mSsl; }; +} // namespace + class RpcTransportTls : public RpcTransport { public: RpcTransportTls(RpcTransportFd socket, Ssl ssl) @@ -411,7 +413,8 @@ status_t RpcTransportTls::interruptableReadFully( } // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|. -bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, FdTrigger* fdTrigger) { +static bool setFdAndDoHandshake(Ssl* ssl, const android::RpcTransportFd& socket, + FdTrigger* fdTrigger) { bssl::UniquePtr<BIO> bio = newSocketBio(socket.fd); TEST_AND_RETURN(false, bio != nullptr); auto [_, errorQueue] = ssl->call(SSL_set_bio, bio.get(), bio.get()); @@ -540,8 +543,6 @@ protected: } }; -} // namespace - std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const { return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier, mAuth.get()); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 0e8e18747b..2b3ff4407a 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -16,9 +16,15 @@ "name": "binderDriverInterfaceTest" }, { + "name": "binderRecordReplayTest" + }, + { "name": "binderHostDeviceTest" }, { + "name": "binderParcelBenchmark" + }, + { "name": "binderTextOutputTest" }, { @@ -58,6 +64,9 @@ "name": "libbinderthreadstateutils_test" }, { + "name": "fuzz_service_test" + }, + { "name": "CtsOsTestCases", "options": [ { diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index d960a0b894..744da0f825 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -105,12 +105,6 @@ public: [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd, const sp<IBinder>& keepAliveBinder); - // Start recording transactions to the unique_fd in data. - // See RecordedTransaction.h for more details. - [[nodiscard]] status_t startRecordingTransactions(const Parcel& data); - // Stop the current recording. - [[nodiscard]] status_t stopRecordingTransactions(); - protected: virtual ~BBinder(); @@ -131,6 +125,8 @@ private: [[nodiscard]] status_t setRpcClientDebug(const Parcel& data); void removeRpcServerLink(const sp<RpcServerLink>& link); + [[nodiscard]] status_t startRecordingTransactions(const Parcel& data); + [[nodiscard]] status_t stopRecordingTransactions(); std::atomic<Extras*> mExtras; diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index d261c2143c..9347ce47a5 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -147,7 +147,12 @@ public: void flushCommands(); bool flushIfNeeded(); - // For main functions - dangerous for libraries to use + // Adds the current thread into the binder threadpool. + // + // This is in addition to any threads which are started + // with startThreadPool. Libraries should not call this + // function, as they may be loaded into processes which + // try to configure the threadpool differently. void joinThreadPool(bool isMain = true); // Stop the local process. diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h index 2e22b84ff0..bda3d19ee1 100644 --- a/libs/binder/include/binder/LazyServiceRegistrar.h +++ b/libs/binder/include/binder/LazyServiceRegistrar.h @@ -93,7 +93,17 @@ class LazyServiceRegistrar { */ void reRegister(); - private: + /** + * Create a second instance of lazy service registrar. + * + * WARNING: dangerous! DO NOT USE THIS - LazyServiceRegistrar + * should be single-instanced, so that the service will only + * shut down when all services are unused. A separate instance + * is only used to test race conditions. + */ + static LazyServiceRegistrar createExtraTestInstance(); + + private: std::shared_ptr<internal::ClientCounterCallback> mClientCC; LazyServiceRegistrar(); }; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 162cd406dc..4e231edac5 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -34,13 +34,8 @@ #include <binder/IInterface.h> #include <binder/Parcelable.h> -#ifdef BINDER_IPC_32BIT -//NOLINTNEXTLINE(google-runtime-int) b/173188702 -typedef unsigned int binder_size_t; -#else //NOLINTNEXTLINE(google-runtime-int) b/173188702 typedef unsigned long long binder_size_t; -#endif struct flat_binder_object; @@ -154,6 +149,11 @@ public: // This Api is used by fuzzers to skip dataAvail checks. void setEnforceNoDataAvail(bool enforceNoDataAvail); + // When fuzzing, we want to remove certain ABI checks that cause significant + // lost coverage, and we also want to avoid logs that cost too much to write. + void setServiceFuzzing(); + bool isServiceFuzzing() const; + void freeData(); size_t objectsCount() const; @@ -266,7 +266,8 @@ public: status_t writeEnumVector(const std::optional<std::vector<T>>& val) { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { return writeData(val); } // Write an Enum vector with underlying type != int8_t. template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> @@ -276,17 +277,20 @@ public: status_t writeEnumVector(const std::optional<std::vector<T>>& val) { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { return writeData(val); } template<typename T> status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) { return writeData(val); } template<typename T> status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) @@ -422,7 +426,8 @@ public: status_t readEnumVector(std::vector<T>* val) const { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::optional<std::vector<T>>* val) const @@ -432,7 +437,8 @@ public: status_t readEnumVector(std::vector<T>* val) const { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))) + [[deprecated("use std::optional version instead")]] // + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::optional<std::vector<T>>* val) const @@ -443,8 +449,9 @@ public: std::optional<std::vector<std::optional<T>>>* val) const { return readData(val); } template<typename T> + [[deprecated("use std::optional version instead")]] // status_t readParcelableVector( - std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead"))) + std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const { return readData(val); } template<typename T> status_t readParcelableVector(std::vector<T>* val) const @@ -1335,6 +1342,7 @@ private: // Set this to false to skip dataAvail checks. bool mEnforceNoDataAvail; + bool mServiceFuzzing; release_func mOwner; diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index ce578e3f5c..9dc370b412 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -52,10 +52,29 @@ public: sp<IBinder> getContextObject(const sp<IBinder>& caller); - // For main functions - dangerous for libraries to use + // This should be called before startThreadPool at the beginning + // of a program, and libraries should never call it because programs + // should configure their own threadpools. The threadpool size can + // never be decreased. + // + // The 'maxThreads' value refers to the total number of threads + // that will be started by the kernel. This is in addition to any + // threads started by 'startThreadPool' or 'joinRpcThreadpool'. + status_t setThreadPoolMaxThreadCount(size_t maxThreads); + + // Libraries should not call this, as processes should configure + // threadpools themselves. Should be called in the main function + // directly before any code executes or joins the threadpool. + // + // Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount, + // PLUS those manually requested in joinThreadPool. + // + // For instance, if setThreadPoolMaxCount(3) is called and + // startThreadpPool (+1 thread) and joinThreadPool (+1 thread) + // are all called, then up to 5 threads can be started. void startThreadPool(); - bool becomeContextManager(); + [[nodiscard]] bool becomeContextManager(); sp<IBinder> getStrongProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); @@ -63,8 +82,6 @@ public: // TODO: deprecate. void spawnPooledThread(bool isMain); - // For main functions - dangerous for libraries to use - status_t setThreadPoolMaxThreadCount(size_t maxThreads); status_t enableOnewaySpamDetection(bool enable); // Set the name of the current thread to look like a threadpool diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 1001b64ede..b804f7b92a 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -137,7 +137,7 @@ public: * used. However, this can be used in order to prevent newer protocol * versions from ever being used. This is expected to be useful for testing. */ - void setProtocolVersion(uint32_t version); + [[nodiscard]] bool setProtocolVersion(uint32_t version); /** * Set the supported transports for sending and receiving file descriptors. @@ -163,14 +163,18 @@ public: * 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*. + * The callable takes three arguments: + * - a weak pointer to the session. If you want to hold onto this in the root object, then + * you should keep a weak pointer, and promote it when needed. For instance, if you refer + * to this from the root object, then you could get ahold of transport-specific information. + * - a type-erased pointer to an OS- and transport-specific address structure, e.g., + * sockaddr_vm for vsock + * - 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); + void setPerSessionRootObject( + std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object); sp<IBinder> getRootObject(); /** @@ -184,6 +188,13 @@ public: void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter); /** + * Set optional modifier of each newly created server socket. + * + * The only argument is a successfully created file descriptor, not bound to an address yet. + */ + void setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier); + + /** * See RpcTransportCtx::getCertificate */ std::vector<uint8_t> getCertificate(RpcCertificateFormat); @@ -265,8 +276,9 @@ private: sp<IBinder> mRootObject; wp<IBinder> mRootObjectWeak; - std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory; + std::function<sp<IBinder>(wp<RpcSession>, const void*, size_t)> mRootObjectFactory; std::function<bool(const void*, size_t)> mConnectionFilter; + std::function<void(base::borrowed_fd)> mServerSocketModifier; std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions; std::unique_ptr<FdTrigger> mShutdownTrigger; RpcConditionVariable mShutdownCv; diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index fd52a3a1a9..6db9ad983c 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -39,6 +39,16 @@ namespace android { class FdTrigger; struct RpcTransportFd; +// for 'friend' +class RpcTransportRaw; +class RpcTransportTls; +class RpcTransportTipcAndroid; +class RpcTransportTipcTrusty; +class RpcTransportCtxRaw; +class RpcTransportCtxTls; +class RpcTransportCtxTipcAndroid; +class RpcTransportCtxTipcTrusty; + // Represents a socket connection. // No thread-safety is guaranteed for these APIs. class RpcTransport { @@ -92,7 +102,21 @@ public: */ [[nodiscard]] virtual bool isWaiting() = 0; -protected: +private: + // limit the classes which can implement RpcTransport. Being able to change this + // interface is important to allow development of RPC binder. In the past, we + // changed this interface to use iovec for efficiency, and we added FDs to the + // interface. If another transport is needed, it should be added directly here. + // non-socket FDs likely also need changes in RpcSession in order to get + // connected, and similarly to how addrinfo was type-erased from RPC binder + // interfaces when RpcTransportTipc* was added, other changes may be needed + // to add more transports. + + friend class ::android::RpcTransportRaw; + friend class ::android::RpcTransportTls; + friend class ::android::RpcTransportTipcAndroid; + friend class ::android::RpcTransportTipcTrusty; + RpcTransport() = default; }; @@ -117,7 +141,13 @@ public: [[nodiscard]] virtual std::vector<uint8_t> getCertificate( RpcCertificateFormat format) const = 0; -protected: +private: + // see comment on RpcTransport + friend class ::android::RpcTransportCtxRaw; + friend class ::android::RpcTransportCtxTls; + friend class ::android::RpcTransportCtxTipcAndroid; + friend class ::android::RpcTransportCtxTipcTrusty; + RpcTransportCtx() = default; }; @@ -140,7 +170,7 @@ protected: RpcTransportCtxFactory() = default; }; -struct RpcTransportFd { +struct RpcTransportFd final { private: mutable bool isPolling{false}; diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index a157792156..7d0acd1843 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -40,12 +40,13 @@ enum class ARpcSession_FileDescriptorTransportMode { [[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port); -// Starts a Unix domain RPC server with a given init-managed Unix domain `name` +// Starts a Unix domain RPC server with an open raw socket file descriptor // and a given root IBinder object. -// The socket should be created in init.rc with the same `name`. +// The socket should be created and bound to an address. // Returns an opaque handle to the running server instance, or null if the server // could not be started. -[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name); +// The socket will be closed by the server once the server goes out of scope. +[[nodiscard]] ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd); // Starts an RPC server that bootstraps sessions using an existing Unix domain // socket pair, with a given root IBinder object. diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index a167f235d5..f51cd9bc99 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -105,22 +105,15 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in return createObjectHandle<ARpcServer>(server); } -ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) { +ARpcServer* ARpcServer_newBoundSocket(AIBinder* service, int socketFd) { auto server = RpcServer::make(); - auto fd = unique_fd(android_get_control_socket(name)); + auto fd = unique_fd(socketFd); if (!fd.ok()) { - LOG(ERROR) << "Failed to get fd for the socket:" << name; + LOG(ERROR) << "Invalid socket fd " << socketFd; return nullptr; } - // Control socket fds are inherited from init, so they don't have O_CLOEXEC set. - // But we don't want any child processes to inherit the socket we are running - // the server on, so attempt to set the flag now. - if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { - LOG(WARNING) << "Failed to set CLOEXEC on control socket with name " << name - << " error: " << errno; - } if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) { - LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name + LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd << " error: " << statusToString(status).c_str(); return nullptr; } diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt index 63679c28d0..50f7deb7d9 100644 --- a/libs/binder/libbinder_rpc_unstable.map.txt +++ b/libs/binder/libbinder_rpc_unstable.map.txt @@ -3,7 +3,7 @@ LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only ARpcServer_free; ARpcServer_join; ARpcServer_newInet; - ARpcServer_newInitUnixDomain; + ARpcServer_newBoundSocket; ARpcServer_newVsock; ARpcServer_shutdown; ARpcServer_start; diff --git a/libs/binder/ndk/.clang-format b/libs/binder/ndk/.clang-format index 9a9d936f15..60774143ee 100644 --- a/libs/binder/ndk/.clang-format +++ b/libs/binder/ndk/.clang-format @@ -2,9 +2,7 @@ BasedOnStyle: Google ColumnLimit: 100 IndentWidth: 4 ContinuationIndentWidth: 8 -PointerAlignment: Left TabWidth: 4 AllowShortFunctionsOnASingleLine: Inline PointerAlignment: Left -TabWidth: 4 UseTab: Never diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index d0de7b96b5..f7dd9c9715 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -137,7 +137,7 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { // since it's an error condition. Do the comparison after we take the lock and // check the pointer equality fast path. By always taking the lock, it's also // more flake-proof. However, the check is not dependent on the lock. - if (descriptor != newDescriptor) { + if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) { if (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor << "' but descriptor is actually '" << SanitizeString(descriptor) << "'."; diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 67bb092f0f..9d5368f674 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -104,10 +104,14 @@ struct ABpBinder : public AIBinder { ::android::sp<::android::IBinder> getBinder() override { return mRemote; } ABpBinder* asABpBinder() override { return this; } + bool isServiceFuzzing() const { return mServiceFuzzing; } + void setServiceFuzzing() { mServiceFuzzing = true; } + private: friend android::sp<ABpBinder>; explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); ::android::sp<::android::IBinder> mRemote; + bool mServiceFuzzing = false; }; struct AIBinder_Class { diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index d6937c2c52..ed53891e3d 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -115,17 +115,29 @@ class SpAIBinder { */ AIBinder** getR() { return &mBinder; } - bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); } - bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); } - bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); } - bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); } - bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); } - bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); } - private: AIBinder* mBinder = nullptr; }; +#define SP_AIBINDER_COMPARE(_op_) \ + static inline bool operator _op_(const SpAIBinder& lhs, const SpAIBinder& rhs) { \ + return lhs.get() _op_ rhs.get(); \ + } \ + static inline bool operator _op_(const SpAIBinder& lhs, const AIBinder* rhs) { \ + return lhs.get() _op_ rhs; \ + } \ + static inline bool operator _op_(const AIBinder* lhs, const SpAIBinder& rhs) { \ + return lhs _op_ rhs.get(); \ + } + +SP_AIBINDER_COMPARE(!=) +SP_AIBINDER_COMPARE(<) +SP_AIBINDER_COMPARE(<=) +SP_AIBINDER_COMPARE(==) +SP_AIBINDER_COMPARE(>) +SP_AIBINDER_COMPARE(>=) +#undef SP_AIBINDER_COMPARE + namespace impl { /** diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 9949de2aac..62738041ba 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -138,6 +138,8 @@ class ICInterface : public SharedRefBase { /** * Dumps information about the interface. By default, dumps nothing. + * + * This method is not given ownership of the FD. */ virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs); diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 3fbe90d70a..68528e1004 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -24,7 +24,14 @@ __BEGIN_DECLS /** - * This creates a threadpool for incoming binder transactions if it has not already been created. + * This creates a threadpool for incoming binder transactions if it has not already been created, + * spawning one thread, and allowing the kernel to lazily start threads according to the count + * that is specified in ABinderProcess_setThreadPoolMaxThreadCount. + * + * For instance, if ABinderProcess_setThreadPoolMaxThreadCount(3) is called, + * ABinderProcess_startThreadPool() is called (+1 thread) then the main thread calls + * ABinderProcess_joinThreadPool() (+1 thread), up to *5* total threads will be started + * (2 directly, and 3 more if the kernel starts them lazily). * * When using this, it is expected that ABinderProcess_setupPolling and * ABinderProcess_handlePolledCommands are not used. @@ -36,7 +43,12 @@ void ABinderProcess_startThreadPool(void); /** * This sets the maximum number of threads that can be started in the threadpool. By default, after * startThreadPool is called, this is 15. If it is called additional times, it will only prevent - * the kernel from starting new threads and will not delete already existing threads. + * the kernel from starting new threads and will not delete already existing threads. This should + * be called once before startThreadPool. The number of threads can never decrease. + * + * This count refers to the number of threads that will be created lazily by the kernel, in + * addition to the threads created by ABinderProcess_startThreadPool or + * ABinderProcess_joinThreadPool. * * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main * function should be responsible for configuring the threadpool for the entire application. @@ -50,8 +62,9 @@ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); */ bool ABinderProcess_isThreadPoolStarted(void); /** - * This adds the current thread to the threadpool. This may cause the threadpool to exceed the - * maximum size. + * This adds the current thread to the threadpool. This thread will be in addition to the thread + * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by + * ABinderProcess_setThreadPoolMaxThreadCount. * * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main * function should be responsible for configuring the threadpool for the entire application. diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index b5a2e2ff0b..037aa2e120 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -270,6 +270,13 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde } sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(readBinder); AIBinder_incStrong(ret.get()); + + if (ret.get() != nullptr && parcel->get()->isServiceFuzzing()) { + if (auto bp = ret->asABpBinder(); bp != nullptr) { + bp->setServiceFuzzing(); + } + } + *binder = ret.get(); return PruneStatusT(status); } diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index cefc42f25e..25b8e975b3 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -107,11 +107,13 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { } static bool activeServicesCallback(bool hasClients, void* context) { if (hasClients) { + LOG(INFO) << "hasClients, so not unregistering."; return false; } // Unregister all services if (!AServiceManager_tryUnregister()) { + LOG(INFO) << "Could not unregister service the first time."; // Prevent shutdown (test will fail) return false; } @@ -121,6 +123,7 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { // Unregister again before shutdown if (!AServiceManager_tryUnregister()) { + LOG(INFO) << "Could not unregister service the second time."; // Prevent shutdown (test will fail) return false; } @@ -128,6 +131,7 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { // Check if the context was passed correctly MyBinderNdkUnitTest* service = static_cast<MyBinderNdkUnitTest*>(context); if (service->contextTestValue != kContextTestValue) { + LOG(INFO) << "Incorrect context value."; // Prevent shutdown (test will fail) return false; } @@ -279,8 +283,8 @@ TEST(NdkBinder, CheckServiceThatDoesntExist) { TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); - EXPECT_NE(nullptr, binder); - EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); + ASSERT_NE(nullptr, binder) << "Could not get " << kExistingNonNdkService; + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)) << "Could not ping " << kExistingNonNdkService; AIBinder_decStrong(binder); } @@ -373,18 +377,24 @@ TEST(NdkBinder, CantHaveTwoLocalBinderClassesWithSameDescriptor) { } TEST(NdkBinder, GetTestServiceStressTest) { - // libbinder has some complicated logic to make sure only one instance of - // ABpBinder is associated with each binder. - constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = 1000; std::vector<std::thread> threads; + // this is not a lazy service, but we must make sure that it's started before calling + // checkService on it, since the other process serving it might not be started yet. + { + // getService, not waitForService, to take advantage of timeout + auto binder = ndk::SpAIBinder(AServiceManager_getService(IFoo::kSomeInstanceName)); + ASSERT_NE(nullptr, binder.get()); + } + for (size_t i = 0; i < kNumThreads; i++) { threads.push_back(std::thread([&]() { for (size_t j = 0; j < kNumCalls; j++) { auto binder = ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName)); + ASSERT_NE(nullptr, binder.get()); EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } })); @@ -479,6 +489,8 @@ TEST(NdkBinder, ForcedPersistenceTest) { } TEST(NdkBinder, ActiveServicesCallbackTest) { + LOG(INFO) << "ActiveServicesCallbackTest starting"; + ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService)); std::shared_ptr<aidl::IBinderNdkUnitTest> service = aidl::IBinderNdkUnitTest::fromBinder(binder); @@ -489,6 +501,7 @@ TEST(NdkBinder, ActiveServicesCallbackTest) { service = nullptr; IPCThreadState::self()->flushCommands(); + LOG(INFO) << "ActiveServicesCallbackTest about to sleep"; sleep(kShutdownWaitTime); ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService)) @@ -497,14 +510,28 @@ TEST(NdkBinder, ActiveServicesCallbackTest) { struct DeathRecipientCookie { std::function<void(void)>*onDeath, *onUnlink; + + // may contain additional data + // - if it contains AIBinder, then you must call AIBinder_unlinkToDeath manually, + // because it would form a strong reference cycle + // - if it points to a data member of another structure, this should have a weak + // promotable reference or a strong reference, in case that object is deleted + // while the death recipient is firing }; void LambdaOnDeath(void* cookie) { auto funcs = static_cast<DeathRecipientCookie*>(cookie); + + // may reference other cookie members + (*funcs->onDeath)(); }; void LambdaOnUnlink(void* cookie) { auto funcs = static_cast<DeathRecipientCookie*>(cookie); (*funcs->onUnlink)(); + + // may reference other cookie members + + delete funcs; }; TEST(NdkBinder, DeathRecipient) { using namespace std::chrono_literals; @@ -536,12 +563,12 @@ TEST(NdkBinder, DeathRecipient) { unlinkCv.notify_one(); }; - DeathRecipientCookie cookie = {&onDeath, &onUnlink}; + DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink}; AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath); AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink); - EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie))); + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie))); // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); @@ -734,9 +761,9 @@ TEST(NdkBinder, ConvertToPlatformBinder) { // local ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) { // convert to platform binder - EXPECT_NE(binder.get(), nullptr); + EXPECT_NE(binder, nullptr); sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get()); - EXPECT_NE(platformBinder.get(), nullptr); + EXPECT_NE(platformBinder, nullptr); auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder); EXPECT_NE(proxy, nullptr); @@ -747,7 +774,7 @@ TEST(NdkBinder, ConvertToPlatformBinder) { // convert back ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder)); - EXPECT_EQ(backBinder.get(), binder.get()); + EXPECT_EQ(backBinder, binder); } } diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d36ebac109..57a38dc480 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -11,9 +11,6 @@ rust_library { name: "libbinder_rs", crate_name: "binder", srcs: ["src/lib.rs"], - shared_libs: [ - "libutils", - ], rustlibs: [ "libbinder_ndk_sys", "libdowncast_rs", @@ -97,34 +94,12 @@ rust_bindgen { crate_name: "binder_ndk_bindgen", wrapper_src: "sys/BinderBindings.hpp", source_stem: "bindings", - bindgen_flags: [ + bindgen_flag_files: [ // Unfortunately the only way to specify the rust_non_exhaustive enum // style for a type is to make it the default - "--default-enum-style", - "rust_non_exhaustive", // and then specify constified enums for the enums we don't want // rustified - "--constified-enum", - "android::c_interface::consts::.*", - - "--allowlist-type", - "android::c_interface::.*", - "--allowlist-type", - "AStatus", - "--allowlist-type", - "AIBinder_Class", - "--allowlist-type", - "AIBinder", - "--allowlist-type", - "AIBinder_Weak", - "--allowlist-type", - "AIBinder_DeathRecipient", - "--allowlist-type", - "AParcel", - "--allowlist-type", - "binder_status_t", - "--allowlist-function", - ".*", + "libbinder_ndk_bindgen_flags.txt", ], shared_libs: [ "libbinder_ndk", diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 2d2bf7c582..1dc0b2471d 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -103,7 +103,12 @@ impl BinderAsyncPool for Tokio { // // This shouldn't cause issues with blocking the thread as only one task will run in a // call to `block_on`, so there aren't other tasks to block. - let result = spawn_me(); + // + // If the `block_in_place` call fails, then you are driving a current-thread runtime on + // the binder threadpool. Instead, it is recommended to use `TokioRuntime<Handle>` when + // the runtime is a current-thread runtime, as the current-thread runtime can be driven + // only by `Runtime::block_on` calls and not by `Handle::block_on`. + let result = tokio::task::block_in_place(spawn_me); Box::pin(after_spawn(result)) } else { let handle = tokio::task::spawn_blocking(spawn_me); diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt new file mode 100644 index 0000000000..551c59f671 --- /dev/null +++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt @@ -0,0 +1,11 @@ +--default-enum-style=rust_non_exhaustive +--constified-enum=android::c_interface::consts::.* +--allowlist-type=android::c_interface::.* +--allowlist-type=AStatus +--allowlist-type=AIBinder_Class +--allowlist-type=AIBinder +--allowlist-type=AIBinder_Weak +--allowlist-type=AIBinder_DeathRecipient +--allowlist-type=AParcel +--allowlist-type=binder_status_t +--allowlist-function=.* diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 0067a20484..788abc4617 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -75,7 +75,6 @@ rust_bindgen { visibility: [":__subpackages__"], source_stem: "bindings", bindgen_flags: [ - "--size_t-is-usize", "--blocklist-type", "AIBinder", "--raw-line", diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index c87876ac15..6fda878d07 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -33,9 +33,9 @@ foreign_type! { pub struct RpcServerRef; } -/// SAFETY - The opaque handle can be cloned freely. +/// SAFETY: The opaque handle can be cloned freely. unsafe impl Send for RpcServer {} -/// SAFETY - The underlying C++ RpcServer class is thread-safe. +/// SAFETY: The underlying C++ RpcServer class is thread-safe. unsafe impl Sync for RpcServer {} impl RpcServer { @@ -57,26 +57,21 @@ impl RpcServer { } /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// socket file name. The socket should be initialized in init.rc with the same name. - pub fn new_init_unix_domain( + /// socket file descriptor. The socket should be bound to an address before calling this + /// function. + pub fn new_bound_socket( mut service: SpIBinder, - socket_name: &str, + socket_fd: OwnedFd, ) -> Result<RpcServer, Error> { - let socket_name = match CString::new(socket_name) { - Ok(s) => s, - Err(e) => { - log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e); - return Err(Error::from(ErrorKind::InvalidInput)); - } - }; let service = service.as_native_mut(); // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. + // The server takes ownership of the socket FD. unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain( + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( service, - socket_name.as_ptr(), + socket_fd.into_raw_fd(), )) } } @@ -129,7 +124,9 @@ impl RpcServer { if ptr.is_null() { return Err(Error::new(ErrorKind::Other, "Failed to start server")); } - Ok(RpcServer::from_ptr(ptr)) + // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not + // null. + Ok(unsafe { RpcServer::from_ptr(ptr) }) } } @@ -139,7 +136,7 @@ impl RpcServerRef { &self, modes: &[FileDescriptorTransportMode], ) { - // SAFETY - Does not keep the pointer after returning does, nor does it + // SAFETY: Does not keep the pointer after returning does, nor does it // read past its boundary. Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( @@ -152,18 +149,21 @@ impl RpcServerRef { /// Starts a new background thread and calls join(). Returns immediately. pub fn start(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; } /// Joins the RpcServer thread. The call blocks until the server terminates. /// This must be called from exactly one thread. pub fn join(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; } /// Shuts down the running RpcServer. Can be called multiple times and from /// multiple threads. Called automatically during drop(). pub fn shutdown(&self) -> Result<(), Error> { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { Ok(()) } else { diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 28c5390665..79a951073e 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -36,15 +36,15 @@ foreign_type! { pub struct RpcSessionRef; } -/// SAFETY - The opaque handle can be cloned freely. +/// SAFETY: The opaque handle can be cloned freely. unsafe impl Send for RpcSession {} -/// SAFETY - The underlying C++ RpcSession class is thread-safe. +/// SAFETY: The underlying C++ RpcSession class is thread-safe. unsafe impl Sync for RpcSession {} impl RpcSession { /// Allocates a new RpcSession object. pub fn new() -> RpcSession { - // SAFETY - Takes ownership of the returned handle, which has correct refcount. + // SAFETY: Takes ownership of the returned handle, which has correct refcount. unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) } } } @@ -58,7 +58,7 @@ impl Default for RpcSession { impl RpcSessionRef { /// Sets the file descriptor transport mode for this session. pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode( self.as_ptr(), @@ -69,7 +69,7 @@ impl RpcSessionRef { /// Sets the maximum number of incoming threads. pub fn set_max_incoming_threads(&self, threads: usize) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads) }; @@ -77,7 +77,7 @@ impl RpcSessionRef { /// Sets the maximum number of outgoing connections. pub fn set_max_outgoing_connections(&self, connections: usize) { - // SAFETY - Only passes the 'self' pointer as an opaque handle. + // SAFETY: Only passes the 'self' pointer as an opaque handle. unsafe { binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections( self.as_ptr(), @@ -210,10 +210,10 @@ impl RpcSessionRef { type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>; unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { + let request_fd_ptr = param as *mut RequestFd; // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the // BinderFdFactory reference, with param being a properly aligned non-null pointer to an // initialized instance. - let request_fd_ptr = param as *mut RequestFd; - let request_fd = request_fd_ptr.as_mut().unwrap(); + let request_fd = unsafe { request_fd_ptr.as_mut().unwrap() }; request_fd().unwrap_or(-1) } diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index d0e35de3f7..463c210316 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -97,8 +97,8 @@ where /// Interface stability promise /// -/// An interface can promise to be a stable vendor interface ([`Vintf`]), or -/// makes no stability guarantees ([`Local`]). [`Local`] is +/// An interface can promise to be a stable vendor interface ([`Stability::Vintf`]), +/// or makes no stability guarantees ([`Stability::Local`]). [`Stability::Local`] is /// currently the default stability. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum Stability { @@ -139,8 +139,8 @@ impl TryFrom<i32> for Stability { /// via `Binder::new(object)`. /// /// This is a low-level interface that should normally be automatically -/// generated from AIDL via the [`declare_binder_interface!`] macro. When using -/// the AIDL backend, users need only implement the high-level AIDL-defined +/// generated from AIDL via the [`crate::declare_binder_interface!`] macro. +/// When using the AIDL backend, users need only implement the high-level AIDL-defined /// interface. The AIDL compiler then generates a container struct that wraps /// the user-defined service and implements `Remotable`. pub trait Remotable: Send + Sync { @@ -260,7 +260,14 @@ pub trait IBinder { /// Trying to use this function on a local binder will result in an /// INVALID_OPERATION code being returned and nothing happening. /// - /// This link always holds a weak reference to its recipient. + /// This link only holds a weak reference to its recipient. If the + /// `DeathRecipient` is dropped then it will be unlinked. + /// + /// Note that the notifications won't work if you don't first start at least + /// one Binder thread by calling + /// [`ProcessState::start_thread_pool`](crate::ProcessState::start_thread_pool) + /// or + /// [`ProcessState::join_thread_pool`](crate::ProcessState::join_thread_pool). fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>; /// Remove a previously registered death notification. @@ -290,18 +297,17 @@ impl InterfaceClass { /// Note: the returned pointer will not be constant. Calling this method /// multiple times for the same type will result in distinct class /// pointers. A static getter for this value is implemented in - /// [`declare_binder_interface!`]. + /// [`crate::declare_binder_interface!`]. pub fn new<I: InterfaceClassMethods>() -> InterfaceClass { let descriptor = CString::new(I::get_descriptor()).unwrap(); + // Safety: `AIBinder_Class_define` expects a valid C string, and three + // valid callback functions, all non-null pointers. The C string is + // copied and need not be valid for longer than the call, so we can drop + // it after the call. We can safely assign null to the onDump and + // handleShellCommand callbacks as long as the class pointer was + // non-null. Rust None for a Option<fn> is guaranteed to be a NULL + // pointer. Rust retains ownership of the pointer after it is defined. let ptr = unsafe { - // Safety: `AIBinder_Class_define` expects a valid C string, and - // three valid callback functions, all non-null pointers. The C - // string is copied and need not be valid for longer than the call, - // so we can drop it after the call. We can safely assign null to - // the onDump and handleShellCommand callbacks as long as the class - // pointer was non-null. Rust None for a Option<fn> is guaranteed to - // be a NULL pointer. Rust retains ownership of the pointer after it - // is defined. let class = sys::AIBinder_Class_define( descriptor.as_ptr(), Some(I::on_create), @@ -331,13 +337,12 @@ impl InterfaceClass { /// Get the interface descriptor string of this class. pub fn get_descriptor(&self) -> String { + // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor is + // always a two-byte null terminated sequence of u16s. Thus, we can + // continue reading from the pointer until we hit a null value, and this + // pointer can be a valid slice if the slice length is <= the number of + // u16 elements before the null terminator. unsafe { - // SAFETY: The descriptor returned by AIBinder_Class_getDescriptor - // is always a two-byte null terminated sequence of u16s. Thus, we - // can continue reading from the pointer until we hit a null value, - // and this pointer can be a valid slice if the slice length is <= - // the number of u16 elements before the null terminator. - let raw_descriptor: *const c_char = sys::AIBinder_Class_getDescriptor(self.0); CStr::from_ptr(raw_descriptor) .to_str() @@ -535,17 +540,15 @@ macro_rules! binder_fn_get_class { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None; + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. CLASS_INIT.call_once(|| unsafe { - // Safety: This assignment is guarded by the `CLASS_INIT` `Once` - // variable, and therefore is thread-safe, as it can only occur - // once. CLASS = Some($constructor); }); - unsafe { - // Safety: The `CLASS` variable can only be mutated once, above, - // and is subsequently safe to read from any thread. - CLASS.unwrap() - } + // Safety: The `CLASS` variable can only be mutated once, above, and + // is subsequently safe to read from any thread. + unsafe { CLASS.unwrap() } } }; } @@ -657,6 +660,8 @@ pub unsafe trait AsNative<T> { fn as_native_mut(&mut self) -> *mut T; } +// Safety: If V is a valid Android C++ type then we can either use that or a +// null pointer. unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> { fn as_native(&self) -> *const T { self.as_ref().map_or(ptr::null(), |v| v.as_native()) @@ -917,15 +922,15 @@ macro_rules! declare_binder_interface { static CLASS_INIT: std::sync::Once = std::sync::Once::new(); static mut CLASS: Option<$crate::binder_impl::InterfaceClass> = None; + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. CLASS_INIT.call_once(|| unsafe { - // Safety: This assignment is guarded by the `CLASS_INIT` `Once` - // variable, and therefore is thread-safe, as it can only occur - // once. CLASS = Some($crate::binder_impl::InterfaceClass::new::<$crate::binder_impl::Binder<$native>>()); }); + // Safety: The `CLASS` variable can only be mutated once, above, + // and is subsequently safe to read from any thread. unsafe { - // Safety: The `CLASS` variable can only be mutated once, above, - // and is subsequently safe to read from any thread. CLASS.unwrap() } } @@ -1018,17 +1023,7 @@ macro_rules! declare_binder_interface { } if ibinder.associate_class(<$native as $crate::binder_impl::Remotable>::get_class()) { - let service: std::result::Result<$crate::binder_impl::Binder<$native>, $crate::StatusCode> = - std::convert::TryFrom::try_from(ibinder.clone()); - if let Ok(service) = service { - // We were able to associate with our expected class and - // the service is local. - todo!() - //return Ok($crate::Strong::new(Box::new(service))); - } else { - // Service is remote - return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?))); - } + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::binder_impl::Proxy>::from_binder(ibinder)?))); } Err($crate::StatusCode::BAD_TYPE.into()) @@ -1122,6 +1117,10 @@ macro_rules! declare_binder_enum { } impl $crate::binder_impl::Deserialize for $enum { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } + fn deserialize(parcel: &$crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<Self, $crate::StatusCode> { parcel.read().map(Self) } diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs index f6b09ed8fe..eb04cc3153 100644 --- a/libs/binder/rust/src/error.rs +++ b/libs/binder/rust/src/error.rs @@ -20,6 +20,7 @@ use crate::sys; use std::error; use std::ffi::{CStr, CString}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::ptr; use std::result; pub use sys::binder_status_t as status_t; @@ -92,7 +93,7 @@ fn parse_exception_code(code: i32) -> ExceptionCode { /// track of and chain binder errors along with service specific errors. /// /// Used in AIDL transactions to represent failed transactions. -pub struct Status(*mut sys::AStatus); +pub struct Status(ptr::NonNull<sys::AStatus>); // Safety: The `AStatus` that the `Status` points to must have an entirely thread-safe API for the // duration of the `Status` object's lifetime. We ensure this by not allowing mutation of a `Status` @@ -111,43 +112,37 @@ fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> { impl Status { /// Create a status object representing a successful transaction. pub fn ok() -> Self { - let ptr = unsafe { - // Safety: `AStatus_newOk` always returns a new, heap allocated - // pointer to an `ASTatus` object, so we know this pointer will be - // valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_newOk() - }; - Self(ptr) + // Safety: `AStatus_newOk` always returns a new, heap allocated + // pointer to an `ASTatus` object, so we know this pointer will be + // valid. + // + // Rust takes ownership of the returned pointer. + let ptr = unsafe { sys::AStatus_newOk() }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Create a status object from a service specific error pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status { let ptr = if let Some(message) = message { - unsafe { - // Safety: Any i32 is a valid service specific error for the - // error code parameter. We construct a valid, null-terminated - // `CString` from the message, which must be a valid C-style - // string to pass as the message. This function always returns a - // new, heap allocated pointer to an `AStatus` object, so we - // know the returned pointer will be valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) - } + // Safety: Any i32 is a valid service specific error for the + // error code parameter. We construct a valid, null-terminated + // `CString` from the message, which must be a valid C-style + // string to pass as the message. This function always returns a + // new, heap allocated pointer to an `AStatus` object, so we + // know the returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + unsafe { sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) } } else { - unsafe { - // Safety: Any i32 is a valid service specific error for the - // error code parameter. This function always returns a new, - // heap allocated pointer to an `AStatus` object, so we know the - // returned pointer will be valid. - // - // Rust takes ownership of the returned pointer. - sys::AStatus_fromServiceSpecificError(err) - } + // Safety: Any i32 is a valid service specific error for the + // error code parameter. This function always returns a new, + // heap allocated pointer to an `AStatus` object, so we know the + // returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + unsafe { sys::AStatus_fromServiceSpecificError(err) } }; - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Creates a status object from a service specific error. @@ -158,10 +153,12 @@ impl Status { /// Create a status object from an exception code pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status { if let Some(message) = message { + // Safety: the C string pointer is valid and not retained by the + // function. let ptr = unsafe { sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr()) }; - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } else { exception.into() } @@ -181,42 +178,36 @@ impl Status { /// /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`. pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self { - Self(ptr) + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } /// Returns `true` if this status represents a successful transaction. pub fn is_ok(&self) -> bool { - unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_isOk` here. - sys::AStatus_isOk(self.as_native()) - } + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_isOk` here. + unsafe { sys::AStatus_isOk(self.as_native()) } } /// Returns a description of the status. pub fn get_description(&self) -> String { - let description_ptr = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getDescription` - // here. - // - // `AStatus_getDescription` always returns a valid pointer to a null - // terminated C string. Rust is responsible for freeing this pointer - // via `AStatus_deleteDescription`. - sys::AStatus_getDescription(self.as_native()) - }; - let description = unsafe { - // Safety: `AStatus_getDescription` always returns a valid C string, - // which can be safely converted to a `CStr`. - CStr::from_ptr(description_ptr) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getDescription` + // here. + // + // `AStatus_getDescription` always returns a valid pointer to a null + // terminated C string. Rust is responsible for freeing this pointer + // via `AStatus_deleteDescription`. + let description_ptr = unsafe { sys::AStatus_getDescription(self.as_native()) }; + // Safety: `AStatus_getDescription` always returns a valid C string, + // which can be safely converted to a `CStr`. + let description = unsafe { CStr::from_ptr(description_ptr) }; let description = description.to_string_lossy().to_string(); + // Safety: `description_ptr` was returned from + // `AStatus_getDescription` above, and must be freed via + // `AStatus_deleteDescription`. We must not access the pointer after + // this call, so we copy it into an owned string above and return + // that string. unsafe { - // Safety: `description_ptr` was returned from - // `AStatus_getDescription` above, and must be freed via - // `AStatus_deleteDescription`. We must not access the pointer after - // this call, so we copy it into an owned string above and return - // that string. sys::AStatus_deleteDescription(description_ptr); } description @@ -224,12 +215,10 @@ impl Status { /// Returns the exception code of the status. pub fn exception_code(&self) -> ExceptionCode { - let code = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getExceptionCode` - // here. - sys::AStatus_getExceptionCode(self.as_native()) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getExceptionCode` + // here. + let code = unsafe { sys::AStatus_getExceptionCode(self.as_native()) }; parse_exception_code(code) } @@ -240,11 +229,9 @@ impl Status { /// exception or a service specific error. To find out if this transaction /// as a whole is okay, use [`is_ok`](Self::is_ok) instead. pub fn transaction_error(&self) -> StatusCode { - let code = unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to `AStatus_getStatus` here. - sys::AStatus_getStatus(self.as_native()) - }; + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getStatus` here. + let code = unsafe { sys::AStatus_getStatus(self.as_native()) }; parse_status_code(code) } @@ -257,12 +244,10 @@ impl Status { /// find out if this transaction as a whole is okay, use /// [`is_ok`](Self::is_ok) instead. pub fn service_specific_error(&self) -> i32 { - unsafe { - // Safety: `Status` always contains a valid `AStatus` pointer, so we - // are always passing a valid pointer to - // `AStatus_getServiceSpecificError` here. - sys::AStatus_getServiceSpecificError(self.as_native()) - } + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to + // `AStatus_getServiceSpecificError` here. + unsafe { sys::AStatus_getServiceSpecificError(self.as_native()) } } /// Calls `op` if the status was ok, otherwise returns an `Err` value of @@ -320,25 +305,21 @@ impl From<StatusCode> for Status { impl From<status_t> for Status { fn from(status: status_t) -> Status { - let ptr = unsafe { - // Safety: `AStatus_fromStatus` expects any `status_t` integer, so - // this is a safe FFI call. Unknown values will be coerced into - // UNKNOWN_ERROR. - sys::AStatus_fromStatus(status) - }; - Self(ptr) + // Safety: `AStatus_fromStatus` expects any `status_t` integer, so + // this is a safe FFI call. Unknown values will be coerced into + // UNKNOWN_ERROR. + let ptr = unsafe { sys::AStatus_fromStatus(status) }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } } impl From<ExceptionCode> for Status { fn from(code: ExceptionCode) -> Status { - let ptr = unsafe { - // Safety: `AStatus_fromExceptionCode` expects any - // `binder_exception_t` (i32) integer, so this is a safe FFI call. - // Unknown values will be coerced into EX_TRANSACTION_FAILED. - sys::AStatus_fromExceptionCode(code as i32) - }; - Self(ptr) + // Safety: `AStatus_fromExceptionCode` expects any + // `binder_exception_t` (i32) integer, so this is a safe FFI call. + // Unknown values will be coerced into EX_TRANSACTION_FAILED. + let ptr = unsafe { sys::AStatus_fromExceptionCode(code as i32) }; + Self(ptr::NonNull::new(ptr).expect("Unexpected null AStatus pointer")) } } @@ -362,30 +343,118 @@ impl From<Status> for status_t { impl Drop for Status { fn drop(&mut self) { + // Safety: `Status` manages the lifetime of its inner `AStatus` + // pointee, so we need to delete it here. We know that the pointer + // will be valid here since `Status` always contains a valid pointer + // while it is alive. unsafe { - // Safety: `Status` manages the lifetime of its inner `AStatus` - // pointee, so we need to delete it here. We know that the pointer - // will be valid here since `Status` always contains a valid pointer - // while it is alive. - sys::AStatus_delete(self.0); + sys::AStatus_delete(self.0.as_mut()); } } } -/// # Safety -/// -/// `Status` always contains a valid pointer to an `AStatus` object, so we can -/// trivially convert it to a correctly-typed raw pointer. +/// Safety: `Status` always contains a valid pointer to an `AStatus` object, so +/// we can trivially convert it to a correctly-typed raw pointer. /// /// Care must be taken that the returned pointer is only dereferenced while the /// `Status` object is still alive. unsafe impl AsNative<sys::AStatus> for Status { fn as_native(&self) -> *const sys::AStatus { - self.0 + self.0.as_ptr() } fn as_native_mut(&mut self) -> *mut sys::AStatus { - self.0 + // Safety: The pointer will be valid here since `Status` always contains + // a valid and initialized pointer while it is alive. + unsafe { self.0.as_mut() } + } +} + +/// A conversion from `std::result::Result<T, E>` to `binder::Result<T>`. If this type is `Ok(T)`, +/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be +/// either a general binder exception, or a service-specific exception. +/// +/// # Examples +/// +/// ``` +/// // std::io::Error is formatted as the exception's message +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // A custom function is used to create the exception's message +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_service_specific_exception_with(NOT_FOUND, +/// |e| format!("file {} not found: {:?}", name, e))? +/// } +/// +/// // anyhow::Error is formatted as the exception's message +/// use anyhow::{Context, Result}; +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .context("file {} not found") +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // General binder exceptions can be created similarly +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)? +/// } +/// ``` +pub trait IntoBinderResult<T, E> { + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by formatting the error for debugging. + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>; + + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by lazily evaluating the `op` function. + fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result<T, Status>; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by formatting the error for debugging. + fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by lazily evaluating the `op` function. + fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result<T, Status>; +} + +impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> { + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> { + self.or_binder_exception_with(exception, |e| format!("{:?}", e)) + } + + fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result<T, Status> { + self.map_err(|e| Status::new_exception_str(exception, Some(op(e)))) + } + + fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> { + self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e)) + } + + fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result<T, Status> { + self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e)))) } } @@ -425,4 +494,66 @@ mod tests { assert_eq!(status.service_specific_error(), 0); assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string()); } + + #[test] + fn convert_to_service_specific_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_service_specific_exception(-42); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_service_specific_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e)); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| { + format!("outer message: {:?}", e) + }); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string() + ); + } } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 0c8b48f1f5..8841fe640b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -106,7 +106,7 @@ use binder_ndk_sys as sys; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; -pub use error::{ExceptionCode, Status, StatusCode}; +pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use native::{ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, LazyServiceGuard, diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index 5557168055..b248f5eb28 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -42,7 +42,7 @@ pub struct Binder<T: Remotable> { rust_object: *mut T, } -/// # Safety +/// Safety: /// /// A `Binder<T>` is a pair of unique owning pointers to two values: /// * a C++ ABBinder which the C++ API guarantees can be passed between threads @@ -54,7 +54,7 @@ pub struct Binder<T: Remotable> { /// to how `Box<T>` is `Send` if `T` is `Send`. unsafe impl<T: Remotable> Send for Binder<T> {} -/// # Safety +/// Safety: /// /// A `Binder<T>` is a pair of unique owning pointers to two values: /// * a C++ ABBinder which is thread-safe, i.e. `Send + Sync` @@ -89,15 +89,13 @@ impl<T: Remotable> Binder<T> { pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> { let class = T::get_class(); let rust_object = Box::into_raw(Box::new(rust_object)); - let ibinder = unsafe { - // Safety: `AIBinder_new` expects a valid class pointer (which we - // initialize via `get_class`), and an arbitrary pointer - // argument. The caller owns the returned `AIBinder` pointer, which - // is a strong reference to a `BBinder`. This reference should be - // decremented via `AIBinder_decStrong` when the reference lifetime - // ends. - sys::AIBinder_new(class.into(), rust_object as *mut c_void) - }; + // Safety: `AIBinder_new` expects a valid class pointer (which we + // initialize via `get_class`), and an arbitrary pointer + // argument. The caller owns the returned `AIBinder` pointer, which + // is a strong reference to a `BBinder`. This reference should be + // decremented via `AIBinder_decStrong` when the reference lifetime + // ends. + let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; let mut binder = Binder { ibinder, rust_object }; binder.mark_stability(stability); binder @@ -176,15 +174,14 @@ impl<T: Remotable> Binder<T> { /// } /// # } pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> { - let status = unsafe { - // Safety: `AIBinder_setExtension` expects two valid, mutable - // `AIBinder` pointers. We are guaranteed that both `self` and - // `extension` contain valid `AIBinder` pointers, because they - // cannot be initialized without a valid - // pointer. `AIBinder_setExtension` does not take ownership of - // either parameter. - sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) - }; + let status = + // Safety: `AIBinder_setExtension` expects two valid, mutable + // `AIBinder` pointers. We are guaranteed that both `self` and + // `extension` contain valid `AIBinder` pointers, because they + // cannot be initialized without a valid + // pointer. `AIBinder_setExtension` does not take ownership of + // either parameter. + unsafe { sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) }; status_result(status) } @@ -199,9 +196,9 @@ impl<T: Remotable> Binder<T> { match stability { Stability::Local => self.mark_local_stability(), Stability::Vintf => { + // Safety: Self always contains a valid `AIBinder` pointer, so + // we can always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markVintfStability(self.as_native_mut()); } } @@ -212,9 +209,9 @@ impl<T: Remotable> Binder<T> { /// building for android_vendor and system otherwise. #[cfg(android_vendor)] fn mark_local_stability(&mut self) { + // Safety: Self always contains a valid `AIBinder` pointer, so we can + // always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markVendorStability(self.as_native_mut()); } } @@ -223,9 +220,9 @@ impl<T: Remotable> Binder<T> { /// building for android_vendor and system otherwise. #[cfg(not(android_vendor))] fn mark_local_stability(&mut self) { + // Safety: Self always contains a valid `AIBinder` pointer, so we can + // always call this C API safely. unsafe { - // Safety: Self always contains a valid `AIBinder` pointer, so - // we can always call this C API safely. sys::AIBinder_markSystemStability(self.as_native_mut()); } } @@ -239,13 +236,13 @@ impl<T: Remotable> Interface for Binder<T> { /// remotable object, which will prevent the object from being dropped while /// the `SpIBinder` is alive. fn as_binder(&self) -> SpIBinder { + // Safety: `self.ibinder` is guaranteed to always be a valid pointer + // to an `AIBinder` by the `Binder` constructor. We are creating a + // copy of the `self.ibinder` strong reference, but + // `SpIBinder::from_raw` assumes it receives an owned pointer with + // its own strong reference. We first increment the reference count, + // so that the new `SpIBinder` will be tracked as a new reference. unsafe { - // Safety: `self.ibinder` is guaranteed to always be a valid pointer - // to an `AIBinder` by the `Binder` constructor. We are creating a - // copy of the `self.ibinder` strong reference, but - // `SpIBinder::from_raw` assumes it receives an owned pointer with - // its own strong reference. We first increment the reference count, - // so that the new `SpIBinder` will be tracked as a new reference. sys::AIBinder_incStrong(self.ibinder); SpIBinder::from_raw(self.ibinder).unwrap() } @@ -275,10 +272,20 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { reply: *mut sys::AParcel, ) -> status_t { let res = { - let mut reply = BorrowedParcel::from_raw(reply).unwrap(); - let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap(); - let object = sys::AIBinder_getUserData(binder); - let binder: &T = &*(object as *const T); + // Safety: The caller must give us a parcel pointer which is either + // null or valid at least for the duration of this function call. We + // don't keep the resulting value beyond the function. + let mut reply = unsafe { BorrowedParcel::from_raw(reply).unwrap() }; + // Safety: The caller must give us a parcel pointer which is either + // null or valid at least for the duration of this function call. We + // don't keep the resulting value beyond the function. + let data = unsafe { BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap() }; + // Safety: Our caller promised that `binder` is a non-null, valid + // pointer to a local `AIBinder`. + let object = unsafe { sys::AIBinder_getUserData(binder) }; + // Safety: Our caller promised that the binder has a `T` pointer in + // its user data. + let binder: &T = unsafe { &*(object as *const T) }; binder.on_transact(code, &data, &mut reply) }; match res { @@ -295,7 +302,9 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { /// Must be called with a valid pointer to a `T` object. After this call, /// the pointer will be invalid and should not be dereferenced. unsafe extern "C" fn on_destroy(object: *mut c_void) { - drop(Box::from_raw(object as *mut T)); + // Safety: Our caller promised that `object` is a valid pointer to a + // `T`. + drop(unsafe { Box::from_raw(object as *mut T) }); } /// Called whenever a new, local `AIBinder` object is needed of a specific @@ -320,7 +329,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { /// Must be called with a non-null, valid pointer to a local `AIBinder` that /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string - /// poiinters with length num_args. + /// pointers with length num_args. unsafe extern "C" fn on_dump( binder: *mut sys::AIBinder, fd: i32, @@ -330,8 +339,9 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { if fd < 0 { return StatusCode::UNEXPECTED_NULL as status_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)); + // Safety: Our caller promised that fd is a file descriptor. We don't + // own this file descriptor, so we need to be careful not to drop it. + let file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) }; if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; @@ -340,14 +350,22 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { 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() + // Safety: Our caller promised that `args` is an array of + // null-terminated string pointers with length `num_args`. + unsafe { + 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); + // Safety: Our caller promised that `binder` is a non-null, valid + // pointer to a local `AIBinder`. + let object = unsafe { sys::AIBinder_getUserData(binder) }; + // Safety: Our caller promised that the binder has a `T` pointer in its + // user data. + let binder: &T = unsafe { &*(object as *const T) }; let res = binder.on_dump(&file, &args); match res { @@ -363,11 +381,11 @@ impl<T: Remotable> Drop for Binder<T> { // actually destroys the object, it calls `on_destroy` and we can drop the // `rust_object` then. fn drop(&mut self) { + // Safety: When `self` is dropped, we can no longer access the + // reference, so can decrement the reference count. `self.ibinder` is + // always a valid `AIBinder` pointer, so is valid to pass to + // `AIBinder_decStrong`. unsafe { - // Safety: When `self` is dropped, we can no longer access the - // reference, so can decrement the reference count. `self.ibinder` - // is always a valid `AIBinder` pointer, so is valid to pass to - // `AIBinder_decStrong`. sys::AIBinder_decStrong(self.ibinder); } } @@ -377,14 +395,11 @@ impl<T: Remotable> Deref for Binder<T> { type Target = T; fn deref(&self) -> &Self::Target { - unsafe { - // Safety: While `self` is alive, the reference count of the - // underlying object is > 0 and therefore `on_destroy` cannot be - // called. Therefore while `self` is alive, we know that - // `rust_object` is still a valid pointer to a heap allocated object - // of type `T`. - &*self.rust_object - } + // Safety: While `self` is alive, the reference count of the underlying + // object is > 0 and therefore `on_destroy` cannot be called. Therefore + // while `self` is alive, we know that `rust_object` is still a valid + // pointer to a heap allocated object of type `T`. + unsafe { &*self.rust_object } } } @@ -405,13 +420,10 @@ impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> { if Some(class) != ibinder.get_class() { return Err(StatusCode::BAD_TYPE); } - let userdata = unsafe { - // Safety: `SpIBinder` always holds a valid pointer pointer to an - // `AIBinder`, which we can safely pass to - // `AIBinder_getUserData`. `ibinder` retains ownership of the - // returned pointer. - sys::AIBinder_getUserData(ibinder.as_native_mut()) - }; + // Safety: `SpIBinder` always holds a valid pointer pointer to an + // `AIBinder`, which we can safely pass to `AIBinder_getUserData`. + // `ibinder` retains ownership of the returned pointer. + let userdata = unsafe { sys::AIBinder_getUserData(ibinder.as_native_mut()) }; if userdata.is_null() { return Err(StatusCode::UNEXPECTED_NULL); } @@ -422,12 +434,10 @@ impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> { } } -/// # Safety -/// -/// The constructor for `Binder` guarantees that `self.ibinder` will contain a -/// valid, non-null pointer to an `AIBinder`, so this implementation is type -/// safe. `self.ibinder` will remain valid for the entire lifetime of `self` -/// because we hold a strong reference to the `AIBinder` until `self` is +/// Safety: The constructor for `Binder` guarantees that `self.ibinder` will +/// contain a valid, non-null pointer to an `AIBinder`, so this implementation +/// is type safe. `self.ibinder` will remain valid for the entire lifetime of +/// `self` because we hold a strong reference to the `AIBinder` until `self` is /// dropped. unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> { fn as_native(&self) -> *const sys::AIBinder { @@ -447,14 +457,12 @@ unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> { /// This function will panic if the identifier contains a 0 byte (NUL). pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { let instance = CString::new(identifier).unwrap(); - let status = unsafe { - // Safety: `AServiceManager_addService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both - // pointers. `AServiceManager_addService` creates a new strong reference - // and copies the string, so both pointers need only be valid until the - // call returns. - sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) - }; + let status = + // Safety: `AServiceManager_addService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both pointers. + // `AServiceManager_addService` creates a new strong reference and copies + // the string, so both pointers need only be valid until the call returns. + unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) }; status_result(status) } @@ -470,13 +478,12 @@ pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { /// This function will panic if the identifier contains a 0 byte (NUL). pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { let instance = CString::new(identifier).unwrap(); + // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both + // pointers. `AServiceManager_registerLazyService` creates a new strong reference + // and copies the string, so both pointers need only be valid until the + // call returns. let status = unsafe { - // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both - // pointers. `AServiceManager_registerLazyService` creates a new strong reference - // and copies the string, so both pointers need only be valid until the - // call returns. - sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr()) }; status_result(status) @@ -491,10 +498,8 @@ pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result< /// /// Consider using [`LazyServiceGuard`] rather than calling this directly. pub fn force_lazy_services_persist(persist: bool) { - unsafe { - // Safety: No borrowing or transfer of ownership occurs here. - sys::AServiceManager_forceLazyServicesPersist(persist) - } + // Safety: No borrowing or transfer of ownership occurs here. + unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) } } /// An RAII object to ensure a process which registers lazy services is not killed. During the @@ -576,8 +581,6 @@ impl Interface for () {} /// Determine whether the current thread is currently executing an incoming /// transaction. pub fn is_handling_transaction() -> bool { - unsafe { - // Safety: This method is always safe to call. - sys::AIBinder_isHandlingTransaction() - } + // Safety: This method is always safe to call. + unsafe { sys::AIBinder_isHandlingTransaction() } } diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index e4c568eab2..3c615edbc0 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -52,11 +52,8 @@ pub struct Parcel { ptr: NonNull<sys::AParcel>, } -/// # Safety -/// -/// This type guarantees that it owns the AParcel and that all access to -/// the AParcel happens through the Parcel, so it is ok to send across -/// threads. +/// Safety: This type guarantees that it owns the AParcel and that all access to +/// the AParcel happens through the Parcel, so it is ok to send across threads. unsafe impl Send for Parcel {} /// Container for a message (data and object references) that can be sent @@ -73,11 +70,9 @@ pub struct BorrowedParcel<'a> { impl Parcel { /// Create a new empty `Parcel`. pub fn new() -> Parcel { - let ptr = unsafe { - // Safety: If `AParcel_create` succeeds, it always returns - // a valid pointer. If it fails, the process will crash. - sys::AParcel_create() - }; + // Safety: If `AParcel_create` succeeds, it always returns + // a valid pointer. If it fails, the process will crash. + let ptr = unsafe { sys::AParcel_create() }; Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") } } @@ -171,10 +166,8 @@ impl<'a> BorrowedParcel<'a> { } } -/// # Safety -/// -/// The `Parcel` constructors guarantee that a `Parcel` object will always -/// contain a valid pointer to an `AParcel`. +/// Safety: The `Parcel` constructors guarantee that a `Parcel` object will +/// always contain a valid pointer to an `AParcel`. unsafe impl AsNative<sys::AParcel> for Parcel { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() @@ -185,10 +178,8 @@ unsafe impl AsNative<sys::AParcel> for Parcel { } } -/// # Safety -/// -/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object -/// will always contain a valid pointer to an `AParcel`. +/// Safety: The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` +/// object will always contain a valid pointer to an `AParcel`. unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { fn as_native(&self) -> *const sys::AParcel { self.ptr.as_ptr() @@ -203,10 +194,8 @@ unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { impl<'a> BorrowedParcel<'a> { /// Data written to parcelable is zero'd before being deleted or reallocated. pub fn mark_sensitive(&mut self) { - unsafe { - // Safety: guaranteed to have a parcel object, and this method never fails - sys::AParcel_markSensitive(self.as_native()) - } + // Safety: guaranteed to have a parcel object, and this method never fails + unsafe { sys::AParcel_markSensitive(self.as_native()) } } /// Write a type that implements [`Serialize`] to the parcel. @@ -265,11 +254,15 @@ impl<'a> BorrowedParcel<'a> { f(&mut subparcel)?; } let end = self.get_data_position(); + // Safety: start is less than the current size of the parcel data + // buffer, because we just got it with `get_data_position`. unsafe { self.set_data_position(start)?; } assert!(end >= start); self.write(&(end - start))?; + // Safety: end is less than the current size of the parcel data + // buffer, because we just got it with `get_data_position`. unsafe { self.set_data_position(end)?; } @@ -278,20 +271,16 @@ impl<'a> BorrowedParcel<'a> { /// Returns the current position in the parcel data. pub fn get_data_position(&self) -> i32 { - unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`, and this call is otherwise safe. - sys::AParcel_getDataPosition(self.as_native()) - } + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + unsafe { sys::AParcel_getDataPosition(self.as_native()) } } /// Returns the total size of the parcel. pub fn get_data_size(&self) -> i32 { - unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`, and this call is otherwise safe. - sys::AParcel_getDataSize(self.as_native()) - } + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + unsafe { sys::AParcel_getDataSize(self.as_native()) } } /// Move the current read/write position in the parcel. @@ -304,7 +293,9 @@ impl<'a> BorrowedParcel<'a> { /// accesses are bounds checked, this call is still safe, but we can't rely /// on that. pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { - status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and the caller guarantees that `pos` is within bounds. + status_result(unsafe { sys::AParcel_setDataPosition(self.as_native(), pos) }) } /// Append a subset of another parcel. @@ -317,10 +308,10 @@ impl<'a> BorrowedParcel<'a> { start: i32, size: i32, ) -> Result<()> { + // Safety: `Parcel::appendFrom` from C++ checks that `start` + // and `size` are in bounds, and returns an error otherwise. + // Both `self` and `other` always contain valid pointers. let status = unsafe { - // Safety: `Parcel::appendFrom` from C++ checks that `start` - // and `size` are in bounds, and returns an error otherwise. - // Both `self` and `other` always contain valid pointers. sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size) }; status_result(status) @@ -418,7 +409,9 @@ impl Parcel { /// accesses are bounds checked, this call is still safe, but we can't rely /// on that. pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { - self.borrowed_ref().set_data_position(pos) + // Safety: We have the same safety requirements as + // `BorrowedParcel::set_data_position`. + unsafe { self.borrowed_ref().set_data_position(pos) } } /// Append a subset of another parcel. @@ -461,7 +454,7 @@ impl<'a> BorrowedParcel<'a> { /// and call a closure with the sub-parcel as its parameter. /// The closure can keep reading data from the sub-parcel /// until it runs out of input data. The closure is responsible - /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// for calling `ReadableSubParcel::has_more_data` to check for /// more data before every read, at least until Rust generators /// are stabilized. /// After the closure returns, skip to the end of the current @@ -504,7 +497,10 @@ impl<'a> BorrowedParcel<'a> { f(subparcel)?; // Advance the data position to the actual end, - // in case the closure read less data than was available + // in case the closure read less data than was available. + // + // Safety: end must be less than the current size of the parcel, because + // we checked above against `get_data_size`. unsafe { self.set_data_position(end)?; } @@ -595,7 +591,7 @@ impl Parcel { /// and call a closure with the sub-parcel as its parameter. /// The closure can keep reading data from the sub-parcel /// until it runs out of input data. The closure is responsible - /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// for calling `ReadableSubParcel::has_more_data` to check for /// more data before every read, at least until Rust generators /// are stabilized. /// After the closure returns, skip to the end of the current @@ -649,17 +645,17 @@ impl Parcel { // Internal APIs impl<'a> BorrowedParcel<'a> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return + // null or a valid pointer to an `AIBinder`, both of which are + // valid, safe inputs to `AParcel_writeStrongBinder`. + // + // This call does not take ownership of the binder. However, it does + // require a mutable pointer, which we cannot extract from an + // immutable reference, so we clone the binder, incrementing the + // refcount before the call. The refcount will be immediately + // decremented when this temporary is dropped. unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return - // null or a valid pointer to an `AIBinder`, both of which are - // valid, safe inputs to `AParcel_writeStrongBinder`. - // - // This call does not take ownership of the binder. However, it does - // require a mutable pointer, which we cannot extract from an - // immutable reference, so we clone the binder, incrementing the - // refcount before the call. The refcount will be immediately - // decremented when this temporary is dropped. status_result(sys::AParcel_writeStrongBinder( self.as_native_mut(), binder.cloned().as_native_mut(), @@ -669,33 +665,28 @@ impl<'a> BorrowedParcel<'a> { pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> { let mut binder = ptr::null_mut(); - let status = unsafe { - // Safety: `BorrowedParcel` always contains a valid pointer to an - // `AParcel`. We pass a valid, mutable out pointer to the `binder` - // parameter. After this call, `binder` will be either null or a - // valid pointer to an `AIBinder` owned by the caller. - sys::AParcel_readStrongBinder(self.as_native(), &mut binder) - }; + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable out pointer to the `binder` + // parameter. After this call, `binder` will be either null or a + // valid pointer to an `AIBinder` owned by the caller. + let status = unsafe { sys::AParcel_readStrongBinder(self.as_native(), &mut binder) }; status_result(status)?; - Ok(unsafe { - // Safety: `binder` is either null or a valid, owned pointer at this - // point, so can be safely passed to `SpIBinder::from_raw`. - SpIBinder::from_raw(binder) - }) + // Safety: `binder` is either null or a valid, owned pointer at this + // point, so can be safely passed to `SpIBinder::from_raw`. + Ok(unsafe { SpIBinder::from_raw(binder) }) } } impl Drop for Parcel { fn drop(&mut self) { // Run the C++ Parcel complete object destructor - unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. Since we own the parcel, we can safely delete it - // here. - sys::AParcel_delete(self.ptr.as_ptr()) - } + // + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. Since we own the parcel, we can safely delete it + // here. + unsafe { sys::AParcel_delete(self.ptr.as_ptr()) } } } @@ -732,6 +723,8 @@ fn test_read_write() { parcel.write(&1i32).unwrap(); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { parcel.set_data_position(start).unwrap(); } @@ -748,6 +741,8 @@ fn test_read_data() { parcel.write(&b"Hello, Binder!\0"[..]).unwrap(); // Skip over string length + // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(str_start).is_ok()); } @@ -756,42 +751,56 @@ fn test_read_data() { assert!(parcel.read::<bool>().unwrap()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<i8>().unwrap(), 72i8); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<u16>().unwrap(), 25928); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<i32>().unwrap(), 1819043144); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<u32>().unwrap(), 1819043144); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -799,6 +808,8 @@ fn test_read_data() { assert_eq!(parcel.read::<f32>().unwrap(), 1143139100000000000000000000.0); assert_eq!(parcel.read::<f32>().unwrap(), 40.043392); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -806,6 +817,8 @@ fn test_read_data() { assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815); // Skip back to before the string length + // SAFETY: str_start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(str_start).is_ok()); } @@ -819,15 +832,21 @@ fn test_utf8_utf16_conversions() { let start = parcel.get_data_position(); assert!(parcel.write("Hello, Binder!").is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<Option<String>>().unwrap().unwrap(), "Hello, Binder!",); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(parcel.write("Embedded null \0 inside a string").is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -835,6 +854,8 @@ fn test_utf8_utf16_conversions() { parcel.read::<Option<String>>().unwrap().unwrap(), "Embedded null \0 inside a string", ); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -849,6 +870,8 @@ fn test_utf8_utf16_conversions() { let s3 = "Some more text here."; assert!(parcel.write(&[s1, s2, s3][..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -874,6 +897,8 @@ fn test_sized_write() { assert_eq!(parcel.get_data_position(), start + expected_len); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { parcel.set_data_position(start).unwrap(); } @@ -893,6 +918,8 @@ fn test_append_from() { assert_eq!(4, parcel2.get_data_size()); assert_eq!(Ok(()), parcel2.append_all_from(&parcel1)); assert_eq!(8, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } @@ -903,6 +930,8 @@ fn test_append_from() { assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2)); assert_eq!(4, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } @@ -911,6 +940,8 @@ fn test_append_from() { let mut parcel2 = Parcel::new(); assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2)); assert_eq!(2, parcel2.get_data_size()); + // SAFETY: 0 is less than the current size of the parcel data buffer, because the parcel is not + // empty. unsafe { parcel2.set_data_position(0).unwrap(); } diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index de6d64934a..5c688fa71b 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -73,14 +73,12 @@ impl Eq for ParcelFileDescriptor {} impl Serialize for ParcelFileDescriptor { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { let fd = self.0.as_raw_fd(); - let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a - // valid file, so we can borrow a valid file - // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take - // ownership of the fd, so we need not duplicate it first. - sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) - }; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a + // valid file, so we can borrow a valid file + // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take + // ownership of the fd, so we need not duplicate it first. + let status = unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) }; status_result(status) } } @@ -92,13 +90,12 @@ impl SerializeOption for ParcelFileDescriptor { if let Some(f) = this { f.serialize(parcel) } else { - let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the - // value `-1` as the file descriptor to signify serializing a - // null file descriptor. - sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) - }; + let status = + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the + // value `-1` as the file descriptor to signify serializing a + // null file descriptor. + unsafe { sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) }; status_result(status) } } @@ -107,31 +104,37 @@ impl SerializeOption for ParcelFileDescriptor { impl DeserializeOption for ParcelFileDescriptor { fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let mut fd = -1i32; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid mutable pointer to an i32, which + // `AParcel_readParcelFileDescriptor` assigns the valid file + // descriptor into, or `-1` if deserializing a null file + // descriptor. The read function passes ownership of the file + // descriptor to its caller if it was non-null, so we must take + // ownership of the file and ensure that it is eventually closed. unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a valid mutable pointer to an i32, which - // `AParcel_readParcelFileDescriptor` assigns the valid file - // descriptor into, or `-1` if deserializing a null file - // descriptor. The read function passes ownership of the file - // descriptor to its caller if it was non-null, so we must take - // ownership of the file and ensure that it is eventually closed. status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?; } if fd < 0 { Ok(None) } else { - let file = unsafe { - // Safety: At this point, we know that the file descriptor was - // not -1, so must be a valid, owned file descriptor which we - // can safely turn into a `File`. - File::from_raw_fd(fd) - }; + // Safety: At this point, we know that the file descriptor was + // not -1, so must be a valid, owned file descriptor which we + // can safely turn into a `File`. + let file = unsafe { File::from_raw_fd(fd) }; Ok(Some(ParcelFileDescriptor::new(file))) } } } impl Deserialize for ParcelFileDescriptor { + type UninitType = Option<Self>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 4b658fc74d..9008a3cc0e 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::binder::{AsNative, FromIBinder, Stability, Strong}; +use crate::binder::{AsNative, FromIBinder, Interface, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; use crate::parcel::BorrowedParcel; use crate::proxy::SpIBinder; @@ -22,7 +22,7 @@ use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; -use std::mem::{self, ManuallyDrop, MaybeUninit}; +use std::mem::{self, ManuallyDrop}; use std::os::raw::c_char; use std::ptr; use std::slice; @@ -50,20 +50,40 @@ pub trait Parcelable { fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>; } -/// A struct whose instances can be written to a [`Parcel`]. +/// A struct whose instances can be written to a [`crate::parcel::Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { - /// Serialize this instance into the given [`Parcel`]. + /// Serialize this instance into the given [`crate::parcel::Parcel`]. fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; } -/// A struct whose instances can be restored from a [`Parcel`]. +/// A struct whose instances can be restored from a [`crate::parcel::Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Deserialize: Sized { - /// Deserialize an instance from the given [`Parcel`]. + /// Type for the uninitialized value of this type. Will be either `Self` + /// if the type implements `Default`, `Option<Self>` otherwise. + type UninitType; + + /// Assert at compile-time that `Self` and `Self::UninitType` have the same + /// size and alignment. This will either fail to compile or evaluate to `true`. + /// The only two macros that work here are `panic!` and `assert!`, so we cannot + /// use `assert_eq!`. + const ASSERT_UNINIT_SIZE_AND_ALIGNMENT: bool = { + assert!(std::mem::size_of::<Self>() == std::mem::size_of::<Self::UninitType>()); + assert!(std::mem::align_of::<Self>() == std::mem::align_of::<Self::UninitType>()); + true + }; + + /// Return an uninitialized or default-initialized value for this type. + fn uninit() -> Self::UninitType; + + /// Convert an initialized value of type `Self` into `Self::UninitType`. + fn from_init(value: Self) -> Self::UninitType; + + /// Deserialize an instance from the given [`crate::parcel::Parcel`]. fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>; - /// Deserialize an instance from the given [`Parcel`] onto the + /// Deserialize an instance from the given [`crate::parcel::Parcel`] onto the /// current object. This operation will overwrite the old value /// partially or completely, depending on how much data is available. fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { @@ -82,8 +102,8 @@ pub trait Deserialize: Sized { pub trait SerializeArray: Serialize + Sized { /// Serialize an array of this type into the given parcel. fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: Safe FFI, slice will always be a safe pointer to pass. let res = unsafe { - // Safety: Safe FFI, slice will always be a safe pointer to pass. sys::AParcel_writeParcelableArray( parcel.as_native_mut(), slice.as_ptr() as *const c_void, @@ -97,7 +117,9 @@ pub trait SerializeArray: Serialize + Sized { /// Callback to serialize an element of a generic parcelable array. /// -/// Safety: We are relying on binder_ndk to not overrun our slice. As long as it +/// # Safety +/// +/// We are relying on binder_ndk to not overrun our slice. As long as it /// doesn't provide an index larger than the length of the original slice in /// serialize_array, this operation is safe. The index provided is zero-based. unsafe extern "C" fn serialize_element<T: Serialize>( @@ -105,9 +127,14 @@ unsafe extern "C" fn serialize_element<T: Serialize>( array: *const c_void, index: usize, ) -> status_t { - let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1); - - let mut parcel = match BorrowedParcel::from_raw(parcel) { + // Safety: The caller guarantees that `array` is a valid pointer of the + // appropriate type. + let slice: &[T] = unsafe { slice::from_raw_parts(array.cast(), index + 1) }; + + // Safety: The caller must give us a parcel pointer which is either null or + // valid at least for the duration of this function call. We don't keep the + // resulting value beyond the function. + let mut parcel = match unsafe { BorrowedParcel::from_raw(parcel) } { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -121,10 +148,10 @@ unsafe extern "C" fn serialize_element<T: Serialize>( pub trait DeserializeArray: Deserialize { /// Deserialize an array of type from the given parcel. fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { - let mut vec: Option<Vec<MaybeUninit<Self>>> = None; + let mut vec: Option<Vec<Self::UninitType>> = None; + // Safety: Safe FFI, vec is the correct opaque type expected by + // allocate_vec and deserialize_element. let res = unsafe { - // Safety: Safe FFI, vec is the correct opaque type expected by - // allocate_vec and deserialize_element. sys::AParcel_readParcelableArray( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -133,36 +160,41 @@ pub trait DeserializeArray: Deserialize { ) }; status_result(res)?; - let vec: Option<Vec<Self>> = unsafe { - // Safety: We are assuming that the NDK correctly initialized every - // element of the vector by now, so we know that all the - // MaybeUninits are now properly initialized. We can transmute from - // Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> has the same - // alignment and size as T, so the pointer to the vector allocation - // will be compatible. - mem::transmute(vec) - }; + // Safety: We are assuming that the NDK correctly initialized every + // element of the vector by now, so we know that all the + // UninitTypes are now properly initialized. We can transmute from + // Vec<T::UninitType> to Vec<T> because T::UninitType has the same + // alignment and size as T, so the pointer to the vector allocation + // will be compatible. + let vec: Option<Vec<Self>> = unsafe { mem::transmute(vec) }; Ok(vec) } } /// Callback to deserialize a parcelable element. /// +/// # Safety +/// /// The opaque array data pointer must be a mutable pointer to an -/// `Option<Vec<MaybeUninit<T>>>` with at least enough elements for `index` to be valid +/// `Option<Vec<T::UninitType>>` with at least enough elements for `index` to be valid /// (zero-based). unsafe extern "C" fn deserialize_element<T: Deserialize>( parcel: *const sys::AParcel, array: *mut c_void, index: usize, ) -> status_t { - let vec = &mut *(array as *mut Option<Vec<MaybeUninit<T>>>); + // Safety: The caller guarantees that `array` is a valid pointer of the + // appropriate type. + let vec = unsafe { &mut *(array as *mut Option<Vec<T::UninitType>>) }; let vec = match vec { Some(v) => v, None => return StatusCode::BAD_INDEX as status_t, }; - let parcel = match BorrowedParcel::from_raw(parcel as *mut _) { + // Safety: The caller must give us a parcel pointer which is either null or + // valid at least for the duration of this function call. We don't keep the + // resulting value beyond the function. + let parcel = match unsafe { BorrowedParcel::from_raw(parcel as *mut _) } { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -170,7 +202,7 @@ unsafe extern "C" fn deserialize_element<T: Deserialize>( Ok(e) => e, Err(code) => return code as status_t, }; - ptr::write(vec[index].as_mut_ptr(), element); + vec[index] = T::from_init(element); StatusCode::OK as status_t } @@ -233,17 +265,22 @@ pub trait DeserializeOption: Deserialize { /// # Safety /// /// The opaque data pointer passed to the array read function must be a mutable -/// pointer to an `Option<Vec<MaybeUninit<T>>>`. `buffer` will be assigned a mutable pointer -/// to the allocated vector data if this function returns true. -unsafe extern "C" fn allocate_vec_with_buffer<T>( +/// pointer to an `Option<Vec<T::UninitType>>`. `buffer` will be assigned a mutable pointer +/// to the allocated vector data if this function returns true. `buffer` must be a valid pointer. +unsafe extern "C" fn allocate_vec_with_buffer<T: Deserialize>( data: *mut c_void, len: i32, buffer: *mut *mut T, ) -> bool { - let res = allocate_vec::<T>(data, len); - let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>); + // Safety: We have the same safety requirements as `allocate_vec` for `data`. + let res = unsafe { allocate_vec::<T>(data, len) }; + // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type. + let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) }; if let Some(new_vec) = vec { - *buffer = new_vec.as_mut_ptr() as *mut T; + // Safety: The caller guarantees that `buffer` is a valid pointer. + unsafe { + *buffer = new_vec.as_mut_ptr() as *mut T; + } } res } @@ -253,22 +290,24 @@ unsafe extern "C" fn allocate_vec_with_buffer<T>( /// # Safety /// /// The opaque data pointer passed to the array read function must be a mutable -/// pointer to an `Option<Vec<MaybeUninit<T>>>`. -unsafe extern "C" fn allocate_vec<T>(data: *mut c_void, len: i32) -> bool { - let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>); +/// pointer to an `Option<Vec<T::UninitType>>`. +unsafe extern "C" fn allocate_vec<T: Deserialize>(data: *mut c_void, len: i32) -> bool { + // Safety: The caller guarantees that `data` is a valid mutable pointer to the appropriate type. + let vec = unsafe { &mut *(data as *mut Option<Vec<T::UninitType>>) }; if len < 0 { *vec = None; return true; } - let mut new_vec: Vec<MaybeUninit<T>> = Vec::with_capacity(len as usize); - // Safety: We are filling the vector with uninitialized data here, but this - // is safe because the vector contains MaybeUninit elements which can be - // uninitialized. We're putting off the actual unsafe bit, transmuting the - // vector to a Vec<T> until the contents are initialized. - new_vec.set_len(len as usize); + // Assert at compile time that `T` and `T::UninitType` have the same size and alignment. + let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT; + let mut new_vec: Vec<T::UninitType> = Vec::with_capacity(len as usize); + new_vec.resize_with(len as usize, T::uninit); - ptr::write(vec, Some(new_vec)); + // Safety: The caller guarantees that vec is a valid mutable pointer to the appropriate type. + unsafe { + ptr::write(vec, Some(new_vec)); + } true } @@ -283,22 +322,25 @@ macro_rules! parcelable_primitives { } /// Safety: All elements in the vector must be properly initialized. -unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> { - // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> - // has the same alignment and size as T, so the pointer to the vector - // allocation will be compatible. +unsafe fn vec_assume_init<T: Deserialize>(vec: Vec<T::UninitType>) -> Vec<T> { + // Assert at compile time that `T` and `T::UninitType` have the same size and alignment. + let _ = T::ASSERT_UNINIT_SIZE_AND_ALIGNMENT; + let mut vec = ManuallyDrop::new(vec); - Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) + // Safety: We can convert from Vec<T::UninitType> to Vec<T> because + // T::UninitType has the same alignment and size as T, so the pointer to the + // vector allocation will be compatible. + unsafe { Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) } } macro_rules! impl_parcelable { {Serialize, $ty:ty, $write_fn:path} => { impl Serialize for $ty { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`, and any `$ty` literal value is safe to pass to + // `$write_fn`. unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`, and any `$ty` literal value is safe to pass to - // `$write_fn`. status_result($write_fn(parcel.as_native_mut(), *self)) } } @@ -307,13 +349,16 @@ macro_rules! impl_parcelable { {Deserialize, $ty:ty, $read_fn:path} => { impl Deserialize for $ty { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut val = Self::default(); + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable pointer to `val`, a + // literal of type `$ty`, and `$read_fn` will write the + // value read into `val` if successful unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a valid, mutable pointer to `val`, a - // literal of type `$ty`, and `$read_fn` will write the - // value read into `val` if successful status_result($read_fn(parcel.as_native(), &mut val))? }; Ok(val) @@ -324,13 +369,13 @@ macro_rules! impl_parcelable { {SerializeArray, $ty:ty, $write_array_fn:path} => { impl SerializeArray for $ty { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` + // will be a valid pointer to an array of elements of type + // `$ty`. If the slice length is 0, `slice.as_ptr()` may be + // dangling, but this is safe since the pointer is not + // dereferenced if the length parameter is 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` - // will be a valid pointer to an array of elements of type - // `$ty`. If the slice length is 0, `slice.as_ptr()` may be - // dangling, but this is safe since the pointer is not - // dereferenced if the length parameter is 0. $write_array_fn( parcel.as_native_mut(), slice.as_ptr(), @@ -348,12 +393,12 @@ macro_rules! impl_parcelable { {DeserializeArray, $ty:ty, $read_array_fn:path} => { impl DeserializeArray for $ty { fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { - let mut vec: Option<Vec<MaybeUninit<Self>>> = None; + let mut vec: Option<Vec<Self::UninitType>> = None; + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `allocate_vec<T>` expects the opaque pointer to + // be of type `*mut Option<Vec<T::UninitType>>`, so `&mut vec` is + // correct for it. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `allocate_vec<T>` expects the opaque pointer to - // be of type `*mut Option<Vec<MaybeUninit<T>>>`, so `&mut vec` is - // correct for it. $read_array_fn( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -361,11 +406,11 @@ macro_rules! impl_parcelable { ) }; status_result(status)?; + // Safety: We are assuming that the NDK correctly + // initialized every element of the vector by now, so we + // know that all the UninitTypes are now properly + // initialized. let vec: Option<Vec<Self>> = unsafe { - // Safety: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) @@ -440,6 +485,14 @@ impl Serialize for u8 { } impl Deserialize for u8 { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i8::deserialize(parcel).map(|v| v as u8) } @@ -447,13 +500,13 @@ impl Deserialize for u8 { impl SerializeArray for u8 { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. sys::AParcel_writeByteArray( parcel.as_native_mut(), slice.as_ptr() as *const i8, @@ -471,6 +524,14 @@ impl Serialize for i16 { } impl Deserialize for i16 { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { u16::deserialize(parcel).map(|v| v as i16) } @@ -478,13 +539,13 @@ impl Deserialize for i16 { impl SerializeArray for i16 { fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. sys::AParcel_writeCharArray( parcel.as_native_mut(), slice.as_ptr() as *const u16, @@ -498,22 +559,22 @@ impl SerializeArray for i16 { impl SerializeOption for str { fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { match this { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the string pointer is null, + // `AParcel_writeString` requires that the length is -1 to + // indicate that we want to serialize a null string. None => unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If the string pointer is null, - // `AParcel_writeString` requires that the length is -1 to - // indicate that we want to serialize a null string. status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1)) }, + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8 + // string pointer of `length` bytes, which is what str in Rust + // is. The docstring for `AParcel_writeString` says that the + // string input should be null-terminated, but it doesn't + // actually rely on that fact in the code. If this ever becomes + // necessary, we will need to null-terminate the str buffer + // before sending it. Some(s) => unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8 - // string pointer of `length` bytes, which is what str in Rust - // is. The docstring for `AParcel_writeString` says that the - // string input should be null-terminated, but it doesn't - // actually rely on that fact in the code. If this ever becomes - // necessary, we will need to null-terminate the str buffer - // before sending it. status_result(sys::AParcel_writeString( parcel.as_native_mut(), s.as_ptr() as *const c_char, @@ -547,13 +608,21 @@ impl SerializeOption for String { } impl Deserialize for Option<String> { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut vec: Option<Vec<u8>> = None; + // Safety: `Parcel` always contains a valid pointer to an `AParcel`. + // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>` + // for `allocate_vec`, so `vec` is safe to pass as the opaque data + // pointer on platforms where char is signed. let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`. - // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>` - // for `allocate_vec`, so `vec` is safe to pass as the opaque data - // pointer on platforms where char is signed. sys::AParcel_readString( parcel.as_native(), &mut vec as *mut _ as *mut c_void, @@ -575,6 +644,14 @@ impl Deserialize for Option<String> { impl DeserializeArray for Option<String> {} impl Deserialize for String { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } @@ -611,6 +688,14 @@ impl<T: SerializeArray> SerializeOption for Vec<T> { } impl<T: DeserializeArray> Deserialize for Vec<T> { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeArray::deserialize_array(parcel) .transpose() @@ -640,6 +725,14 @@ impl<T: SerializeArray, const N: usize> SerializeOption for [T; N] { impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {} impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] { + type UninitType = [T::UninitType; N]; + fn uninit() -> Self::UninitType { + [(); N].map(|_| T::uninit()) + } + fn from_init(value: Self) -> Self::UninitType { + value.map(T::from_init) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let vec = DeserializeArray::deserialize_array(parcel) .transpose() @@ -664,6 +757,14 @@ impl Serialize for Stability { } impl Deserialize for Stability { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i32::deserialize(parcel).and_then(Stability::try_from) } @@ -671,34 +772,39 @@ impl Deserialize for Stability { impl Serialize for Status { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + // Safety: `Parcel` always contains a valid pointer to an `AParcel` + // and `Status` always contains a valid pointer to an `AStatus`, so + // both parameters are valid and safe. This call does not take + // ownership of either of its parameters. unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel` - // and `Status` always contains a valid pointer to an `AStatus`, so - // both parameters are valid and safe. This call does not take - // ownership of either of its parameters. status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native())) } } } impl Deserialize for Status { + type UninitType = Option<Self>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut status_ptr = ptr::null_mut(); - let ret_status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. We pass a mutable out pointer which will be - // assigned a valid `AStatus` pointer if the function returns - // status OK. This function passes ownership of the status - // pointer to the caller, if it was assigned. - sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) - }; + let ret_status = + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a mutable out pointer which will be + // assigned a valid `AStatus` pointer if the function returns + // status OK. This function passes ownership of the status + // pointer to the caller, if it was assigned. + unsafe { sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) }; status_result(ret_status)?; - Ok(unsafe { - // Safety: At this point, the return status of the read call was ok, - // so we know that `status_ptr` is a valid, owned pointer to an - // `AStatus`, from which we can safely construct a `Status` object. - Status::from_ptr(status_ptr) - }) + // Safety: At this point, the return status of the read call was ok, + // so we know that `status_ptr` is a valid, owned pointer to an + // `AStatus`, from which we can safely construct a `Status` object. + Ok(unsafe { Status::from_ptr(status_ptr) }) } } @@ -717,12 +823,29 @@ impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> { impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {} impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> { + type UninitType = Option<Strong<T>>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let ibinder: SpIBinder = parcel.read()?; FromIBinder::try_from(ibinder) } } +struct AssertIBinder; +impl Interface for AssertIBinder {} +impl FromIBinder for AssertIBinder { + // This is only needed so we can assert on the size of Strong<AssertIBinder> + fn try_from(_: SpIBinder) -> Result<Strong<Self>> { + unimplemented!() + } +} + impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> { fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let ibinder: Option<SpIBinder> = parcel.read()?; @@ -752,6 +875,14 @@ impl<T: SerializeOption> Serialize for Option<T> { } impl<T: DeserializeOption> Deserialize for Option<T> { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeOption::deserialize_option(parcel) } @@ -767,7 +898,6 @@ impl<T: DeserializeOption> Deserialize for Option<T> { /// `Serialize`, `SerializeArray` and `SerializeOption` for /// structured parcelables. The target type must implement the /// `Parcelable` trait. -/// ``` #[macro_export] macro_rules! impl_serialize_for_parcelable { ($parcelable:ident) => { @@ -821,6 +951,9 @@ macro_rules! impl_deserialize_for_parcelable { }; ($parcelable:ident < $( $param:ident ),* > ) => { impl < $($param: Default),* > $crate::binder_impl::Deserialize for $parcelable < $($param),* > { + type UninitType = Self; + fn uninit() -> Self::UninitType { Self::UninitType::default() } + fn from_init(value: Self) -> Self::UninitType { value } fn deserialize( parcel: &$crate::binder_impl::BorrowedParcel<'_>, ) -> std::result::Result<Self, $crate::StatusCode> { @@ -876,6 +1009,14 @@ impl<T: Serialize> Serialize for Box<T> { } impl<T: Deserialize> Deserialize for Box<T> { + type UninitType = Option<Self>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel).map(Box::new) } @@ -900,6 +1041,7 @@ mod tests { #[test] fn test_custom_parcelable() { + #[derive(Default)] struct Custom(u32, bool, String, Vec<String>); impl Serialize for Custom { @@ -912,6 +1054,14 @@ mod tests { } impl Deserialize for Custom { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Ok(Custom( parcel.read()?, @@ -937,6 +1087,8 @@ mod tests { assert!(custom.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -959,6 +1111,8 @@ mod tests { assert!(bools.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -968,6 +1122,8 @@ mod tests { assert_eq!(parcel.read::<u32>().unwrap(), 0); assert_eq!(parcel.read::<u32>().unwrap(), 0); assert_eq!(parcel.read::<u32>().unwrap(), 1); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -983,12 +1139,17 @@ mod tests { assert!(parcel.write(&u8s[..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -998,18 +1159,25 @@ mod tests { let i8s = [-128i8, 127, 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(parcel.write(&i8s[..]).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1019,10 +1187,14 @@ mod tests { let u16s = [u16::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u16s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1032,6 +1204,9 @@ mod tests { assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1042,10 +1217,14 @@ mod tests { let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i16s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1055,6 +1234,9 @@ mod tests { assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1065,10 +1247,14 @@ mod tests { let u32s = [u32::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1078,6 +1264,9 @@ mod tests { assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1088,10 +1277,14 @@ mod tests { let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1101,6 +1294,9 @@ mod tests { assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 + + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1111,10 +1307,14 @@ mod tests { let u64s = [u64::max_value(), 12_345, 42, 117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(u64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1125,10 +1325,14 @@ mod tests { let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(i64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1139,10 +1343,14 @@ mod tests { let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(f32s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1155,10 +1363,14 @@ mod tests { let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(f64s.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -1176,10 +1388,14 @@ mod tests { let strs = [s1, s2, s3, s4]; + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } assert!(strs.serialize(&mut parcel.borrowed()).is_ok()); + // SAFETY: start is less than the current size of the parcel data buffer, because we haven't + // made it any shorter since we got the position. unsafe { assert!(parcel.set_data_position(start).is_ok()); } diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index c829d37dca..f90611361f 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -133,8 +133,8 @@ impl ParcelableHolder { } } ParcelableHolderData::Parcel(ref mut parcel) => { + // Safety: 0 should always be a valid position. unsafe { - // Safety: 0 should always be a valid position. parcel.set_data_position(0)?; } @@ -161,6 +161,15 @@ impl ParcelableHolder { } } +impl Clone for ParcelableHolder { + fn clone(&self) -> ParcelableHolder { + ParcelableHolder { + data: Mutex::new(self.data.lock().unwrap().clone()), + stability: self.stability, + } + } +} + impl Serialize for ParcelableHolder { fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> { parcel.write(&NON_NULL_PARCELABLE_FLAG)?; @@ -169,6 +178,14 @@ impl Serialize for ParcelableHolder { } impl Deserialize for ParcelableHolder { + type UninitType = Self; + fn uninit() -> Self::UninitType { + Self::new(Default::default()) + } + fn from_init(value: Self) -> Self::UninitType { + value + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self, StatusCode> { let status: i32 = parcel.read()?; if status == NULL_PARCELABLE_FLAG { @@ -197,15 +214,15 @@ impl Parcelable for ParcelableHolder { parcelable.write_to_parcel(parcel)?; let end = parcel.get_data_position(); + // Safety: we got the position from `get_data_position`. unsafe { - // Safety: we got the position from `get_data_position`. parcel.set_data_position(length_start)?; } assert!(end >= data_start); parcel.write(&(end - data_start))?; + // Safety: we got the position from `get_data_position`. unsafe { - // Safety: we got the position from `get_data_position`. parcel.set_data_position(end)?; } @@ -243,11 +260,11 @@ impl Parcelable for ParcelableHolder { new_parcel.append_from(parcel, data_start, data_size)?; *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel); + // Safety: `append_from` checks if `data_size` overflows + // `parcel` and returns `BAD_VALUE` if that happens. We also + // explicitly check for negative and zero `data_size` above, + // so `data_end` is guaranteed to be greater than `data_start`. unsafe { - // Safety: `append_from` checks if `data_size` overflows - // `parcel` and returns `BAD_VALUE` if that happens. We also - // explicitly check for negative and zero `data_size` above, - // so `data_end` is guaranteed to be greater than `data_start`. parcel.set_data_position(data_end)?; } diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 254efaed9a..dad3379bdc 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -49,14 +49,12 @@ impl fmt::Debug for SpIBinder { } } -/// # Safety -/// -/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe +/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Send for SpIBinder {} -/// # Safety -/// -/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe +/// Safety: An `SpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Sync for SpIBinder {} impl SpIBinder { @@ -97,11 +95,9 @@ impl SpIBinder { /// Return true if this binder object is hosted in a different process than /// the current one. pub fn is_remote(&self) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. - sys::AIBinder_isRemote(self.as_native()) - } + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. + unsafe { sys::AIBinder_isRemote(self.as_native()) } } /// Try to convert this Binder object into a trait object for the given @@ -116,12 +112,12 @@ impl SpIBinder { /// Return the interface class of this binder object, if associated with /// one. pub fn get_class(&mut self) -> Option<InterfaceClass> { + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. `AIBinder_getClass` returns either a null + // pointer or a valid pointer to an `AIBinder_Class`. After mapping + // null to None, we can safely construct an `InterfaceClass` if the + // pointer was non-null. unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. `AIBinder_getClass` returns either a null - // pointer or a valid pointer to an `AIBinder_Class`. After mapping - // null to None, we can safely construct an `InterfaceClass` if the - // pointer was non-null. let class = sys::AIBinder_getClass(self.as_native_mut()); class.as_ref().map(|p| InterfaceClass::from_ptr(p)) } @@ -152,7 +148,8 @@ pub mod unstable_api { /// /// See `SpIBinder::from_raw`. pub unsafe fn new_spibinder(ptr: *mut sys::AIBinder) -> Option<SpIBinder> { - SpIBinder::from_raw(ptr) + // Safety: The caller makes the same guarantees as this requires. + unsafe { SpIBinder::from_raw(ptr) } } } @@ -171,30 +168,24 @@ pub trait AssociateClass { impl AssociateClass for SpIBinder { fn associate_class(&mut self, class: InterfaceClass) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that it always contains a valid - // `AIBinder` pointer. An `InterfaceClass` can always be converted - // into a valid `AIBinder_Class` pointer, so these parameters are - // always safe. - sys::AIBinder_associateClass(self.as_native_mut(), class.into()) - } + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. An `InterfaceClass` can always be converted + // into a valid `AIBinder_Class` pointer, so these parameters are + // always safe. + unsafe { sys::AIBinder_associateClass(self.as_native_mut(), class.into()) } } } impl Ord for SpIBinder { fn cmp(&self, other: &Self) -> Ordering { - let less_than = unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so - // this pointer is always safe to pass to `AIBinder_lt` (null is - // also safe to pass to this function, but we should never do that). - sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) - }; - let greater_than = unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so - // this pointer is always safe to pass to `AIBinder_lt` (null is - // also safe to pass to this function, but we should never do that). - sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) - }; + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this + // pointer is always safe to pass to `AIBinder_lt` (null is also safe to + // pass to this function, but we should never do that). + let less_than = unsafe { sys::AIBinder_lt(self.0.as_ptr(), other.0.as_ptr()) }; + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so this + // pointer is always safe to pass to `AIBinder_lt` (null is also safe to + // pass to this function, but we should never do that). + let greater_than = unsafe { sys::AIBinder_lt(other.0.as_ptr(), self.0.as_ptr()) }; if !less_than && !greater_than { Ordering::Equal } else if less_than { @@ -221,10 +212,10 @@ impl Eq for SpIBinder {} impl Clone for SpIBinder { fn clone(&self) -> Self { + // Safety: Cloning a strong reference must increment the reference + // count. We are guaranteed by the `SpIBinder` constructor + // invariants that `self.0` is always a valid `AIBinder` pointer. unsafe { - // Safety: Cloning a strong reference must increment the reference - // count. We are guaranteed by the `SpIBinder` constructor - // invariants that `self.0` is always a valid `AIBinder` pointer. sys::AIBinder_incStrong(self.0.as_ptr()); } Self(self.0) @@ -235,9 +226,9 @@ impl Drop for SpIBinder { // We hold a strong reference to the IBinder in SpIBinder and need to give up // this reference on drop. fn drop(&mut self) { + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we + // know this pointer is safe to pass to `AIBinder_decStrong` here. unsafe { - // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we - // know this pointer is safe to pass to `AIBinder_decStrong` here. sys::AIBinder_decStrong(self.as_native_mut()); } } @@ -246,26 +237,24 @@ impl Drop for SpIBinder { impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { fn prepare_transact(&self) -> Result<Parcel> { let mut input = ptr::null_mut(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. It is safe to cast from an + // immutable pointer to a mutable pointer here, because + // `AIBinder_prepareTransaction` only calls immutable `AIBinder` + // methods but the parameter is unfortunately not marked as const. + // + // After the call, input will be either a valid, owned `AParcel` + // pointer, or null. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. It is safe to cast from an - // immutable pointer to a mutable pointer here, because - // `AIBinder_prepareTransaction` only calls immutable `AIBinder` - // methods but the parameter is unfortunately not marked as const. - // - // After the call, input will be either a valid, owned `AParcel` - // pointer, or null. sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input) }; status_result(status)?; - unsafe { - // Safety: At this point, `input` is either a valid, owned `AParcel` - // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, - // taking ownership of the parcel. - Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) - } + // Safety: At this point, `input` is either a valid, owned `AParcel` + // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, + // taking ownership of the parcel. + unsafe { Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) } } fn submit_transact( @@ -275,23 +264,23 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { flags: TransactionFlags, ) -> Result<Parcel> { let mut reply = ptr::null_mut(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. Although `IBinder::transact` is + // not a const method, it is still safe to cast our immutable + // pointer to mutable for the call. First, `IBinder::transact` is + // thread-safe, so concurrency is not an issue. The only way that + // `transact` can affect any visible, mutable state in the current + // process is by calling `onTransact` for a local service. However, + // in order for transactions to be thread-safe, this method must + // dynamically lock its data before modifying it. We enforce this + // property in Rust by requiring `Sync` for remotable objects and + // only providing `on_transact` with an immutable reference to + // `self`. + // + // This call takes ownership of the `data` parcel pointer, and + // passes ownership of the `reply` out parameter to its caller. It + // does not affect ownership of the `binder` parameter. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. Although `IBinder::transact` is - // not a const method, it is still safe to cast our immutable - // pointer to mutable for the call. First, `IBinder::transact` is - // thread-safe, so concurrency is not an issue. The only way that - // `transact` can affect any visible, mutable state in the current - // process is by calling `onTransact` for a local service. However, - // in order for transactions to be thread-safe, this method must - // dynamically lock its data before modifying it. We enforce this - // property in Rust by requiring `Sync` for remotable objects and - // only providing `on_transact` with an immutable reference to - // `self`. - // - // This call takes ownership of the `data` parcel pointer, and - // passes ownership of the `reply` out parameter to its caller. It - // does not affect ownership of the `binder` parameter. sys::AIBinder_transact( self.as_native() as *mut sys::AIBinder, code, @@ -302,45 +291,45 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { }; status_result(status)?; - unsafe { - // Safety: `reply` is either a valid `AParcel` pointer or null - // after the call to `AIBinder_transact` above, so we can - // construct a `Parcel` out of it. `AIBinder_transact` passes - // ownership of the `reply` parcel to Rust, so we need to - // construct an owned variant. - Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) - } + // Safety: `reply` is either a valid `AParcel` pointer or null + // after the call to `AIBinder_transact` above, so we can + // construct a `Parcel` out of it. `AIBinder_transact` passes + // ownership of the `reply` parcel to Rust, so we need to + // construct an owned variant. + unsafe { Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) } } fn is_binder_alive(&self) -> bool { - unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. - // - // This call does not affect ownership of its pointer parameter. - sys::AIBinder_isAlive(self.as_native()) - } + // Safety: `SpIBinder` guarantees that `self` always contains a valid + // pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + unsafe { sys::AIBinder_isAlive(self.as_native()) } } #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool) { + // Safety: `SpIBinder` guarantees that `self` always contains a valid + // pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) }; } fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> { let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect(); let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect(); + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the + // file descriptor parameter is always be a valid open file. The + // `args` pointer parameter is a valid pointer to an array of C + // strings that will outlive the call since `args` lives for the + // whole function scope. + // + // This call does not affect ownership of its binder pointer + // parameter and does not take ownership of the file or args array + // parameters. let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the - // file descriptor parameter is always be a valid open file. The - // `args` pointer parameter is a valid pointer to an array of C - // strings that will outlive the call since `args` lives for the - // whole function scope. - // - // This call does not affect ownership of its binder pointer - // parameter and does not take ownership of the file or args array - // parameters. sys::AIBinder_dump( self.as_native_mut(), fp.as_raw_fd(), @@ -353,22 +342,18 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { fn get_extension(&mut self) -> Result<Option<SpIBinder>> { let mut out = ptr::null_mut(); - let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. After this call, the `out` - // parameter will be either null, or a valid pointer to an - // `AIBinder`. - // - // This call passes ownership of the out pointer to its caller - // (assuming it is set to a non-null value). - sys::AIBinder_getExtension(self.as_native_mut(), &mut out) - }; - let ibinder = unsafe { - // Safety: The call above guarantees that `out` is either null or a - // valid, owned pointer to an `AIBinder`, both of which are safe to - // pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(out) - }; + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. After this call, the `out` + // parameter will be either null, or a valid pointer to an + // `AIBinder`. + // + // This call passes ownership of the out pointer to its caller + // (assuming it is set to a non-null value). + let status = unsafe { sys::AIBinder_getExtension(self.as_native_mut(), &mut out) }; + // Safety: The call above guarantees that `out` is either null or a + // valid, owned pointer to an `AIBinder`, both of which are safe to + // pass to `SpIBinder::from_raw`. + let ibinder = unsafe { SpIBinder::from_raw(out) }; status_result(status)?; Ok(ibinder) @@ -377,17 +362,17 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { impl<T: AsNative<sys::AIBinder>> IBinder for T { fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeathRecipient`. + // + // The cookie is also the correct pointer, and by calling new_cookie, + // we have created a new ref-count to the cookie, which linkToDeath + // takes ownership of. Once the DeathRecipient is unlinked for any + // reason (including if this call fails), the onUnlinked callback + // will consume that ref-count. status_result(unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `recipient` can always be - // converted into a valid pointer to an - // `AIBinder_DeathRecipient`. - // - // The cookie is also the correct pointer, and by calling new_cookie, - // we have created a new ref-count to the cookie, which linkToDeath - // takes ownership of. Once the DeathRecipient is unlinked for any - // reason (including if this call fails), the onUnlinked callback - // will consume that ref-count. sys::AIBinder_linkToDeath( self.as_native_mut(), recipient.as_native_mut(), @@ -397,13 +382,13 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { } fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeathRecipient`. Any value is safe to pass as the + // cookie, although we depend on this value being set by + // `get_cookie` when the death recipient callback is called. status_result(unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. `recipient` can always be - // converted into a valid pointer to an - // `AIBinder_DeathRecipient`. Any value is safe to pass as the - // cookie, although we depend on this value being set by - // `get_cookie` when the death recipient callback is called. sys::AIBinder_unlinkToDeath( self.as_native_mut(), recipient.as_native_mut(), @@ -413,13 +398,11 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { } fn ping_binder(&mut self) -> Result<()> { - let status = unsafe { - // Safety: `SpIBinder` guarantees that `self` always contains a - // valid pointer to an `AIBinder`. - // - // This call does not affect ownership of its pointer parameter. - sys::AIBinder_ping(self.as_native_mut()) - }; + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + let status = unsafe { sys::AIBinder_ping(self.as_native_mut()) }; status_result(status) } } @@ -439,6 +422,14 @@ impl SerializeOption for SpIBinder { impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { + type UninitType = Option<Self>; + fn uninit() -> Self::UninitType { + Self::UninitType::default() + } + fn from_init(value: Self) -> Self::UninitType { + Some(value) + } + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> { parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } @@ -464,35 +455,31 @@ impl fmt::Debug for WpIBinder { } } -/// # Safety -/// -/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe. +/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Send for WpIBinder {} -/// # Safety -/// -/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe. +/// Safety: A `WpIBinder` is an immutable handle to a C++ IBinder, which is +/// thread-safe. unsafe impl Sync for WpIBinder {} impl WpIBinder { /// Create a new weak reference from an object that can be converted into a /// raw `AIBinder` pointer. fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder { - let ptr = unsafe { - // Safety: `SpIBinder` guarantees that `binder` always contains a - // valid pointer to an `AIBinder`. - sys::AIBinder_Weak_new(binder.as_native_mut()) - }; + // Safety: `SpIBinder` guarantees that `binder` always contains a valid + // pointer to an `AIBinder`. + let ptr = unsafe { sys::AIBinder_Weak_new(binder.as_native_mut()) }; Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_new")) } /// Promote this weak reference to a strong reference to the binder object. pub fn promote(&self) -> Option<SpIBinder> { + // Safety: `WpIBinder` always contains a valid weak reference, so we can + // pass this pointer to `AIBinder_Weak_promote`. Returns either null or + // an AIBinder owned by the caller, both of which are valid to pass to + // `SpIBinder::from_raw`. unsafe { - // Safety: `WpIBinder` always contains a valid weak reference, so we - // can pass this pointer to `AIBinder_Weak_promote`. Returns either - // null or an AIBinder owned by the caller, both of which are valid - // to pass to `SpIBinder::from_raw`. let ptr = sys::AIBinder_Weak_promote(self.0.as_ptr()); SpIBinder::from_raw(ptr) } @@ -501,35 +488,27 @@ impl WpIBinder { impl Clone for WpIBinder { fn clone(&self) -> Self { - let ptr = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_clone` - // (although null is also a safe value to pass to this API). - // - // We get ownership of the returned pointer, so can construct a new - // WpIBinder object from it. - sys::AIBinder_Weak_clone(self.0.as_ptr()) - }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_clone` + // (although null is also a safe value to pass to this API). + // + // We get ownership of the returned pointer, so can construct a new + // WpIBinder object from it. + let ptr = unsafe { sys::AIBinder_Weak_clone(self.0.as_ptr()) }; Self(ptr::NonNull::new(ptr).expect("Unexpected null pointer from AIBinder_Weak_clone")) } } impl Ord for WpIBinder { fn cmp(&self, other: &Self) -> Ordering { - let less_than = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_lt` - // (null is also safe to pass to this function, but we should never - // do that). - sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) - }; - let greater_than = unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, - // so this pointer is always safe to pass to `AIBinder_Weak_lt` - // (null is also safe to pass to this function, but we should never - // do that). - sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) - }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is + // also safe to pass to this function, but we should never do that). + let less_than = unsafe { sys::AIBinder_Weak_lt(self.0.as_ptr(), other.0.as_ptr()) }; + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so + // this pointer is always safe to pass to `AIBinder_Weak_lt` (null is + // also safe to pass to this function, but we should never do that). + let greater_than = unsafe { sys::AIBinder_Weak_lt(other.0.as_ptr(), self.0.as_ptr()) }; if !less_than && !greater_than { Ordering::Equal } else if less_than { @@ -556,9 +535,9 @@ impl Eq for WpIBinder {} impl Drop for WpIBinder { fn drop(&mut self) { + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we + // know this pointer is safe to pass to `AIBinder_Weak_delete` here. unsafe { - // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we - // know this pointer is safe to pass to `AIBinder_Weak_delete` here. sys::AIBinder_Weak_delete(self.0.as_ptr()); } } @@ -566,7 +545,7 @@ impl Drop for WpIBinder { /// Rust wrapper around DeathRecipient objects. /// -/// The cookie in this struct represents an Arc<F> for the owned callback. +/// The cookie in this struct represents an `Arc<F>` for the owned callback. /// This struct owns a ref-count of it, and so does every binder that we /// have been linked with. /// @@ -584,17 +563,13 @@ struct DeathRecipientVtable { cookie_decr_refcount: unsafe extern "C" fn(*mut c_void), } -/// # Safety -/// -/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer -/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and +/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As /// `AIBinder_DeathRecipient` is threadsafe, this structure is too. unsafe impl Send for DeathRecipient {} -/// # Safety -/// -/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer -/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// Safety: A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and +/// a pointer to a `Fn` which is `Sync` and `Send` (the cookie field). As /// `AIBinder_DeathRecipient` is threadsafe, this structure is too. unsafe impl Sync for DeathRecipient {} @@ -606,19 +581,17 @@ impl DeathRecipient { F: Fn() + Send + Sync + 'static, { let callback: *const F = Arc::into_raw(Arc::new(callback)); - let recipient = unsafe { - // Safety: The function pointer is a valid death recipient callback. - // - // This call returns an owned `AIBinder_DeathRecipient` pointer - // which must be destroyed via `AIBinder_DeathRecipient_delete` when - // no longer needed. - sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>)) - }; + // Safety: The function pointer is a valid death recipient callback. + // + // This call returns an owned `AIBinder_DeathRecipient` pointer which + // must be destroyed via `AIBinder_DeathRecipient_delete` when no longer + // needed. + let recipient = unsafe { sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>)) }; + // Safety: The function pointer is a valid onUnlinked callback. + // + // All uses of linkToDeath in this file correctly increment the + // ref-count that this onUnlinked callback will decrement. unsafe { - // Safety: The function pointer is a valid onUnlinked callback. - // - // All uses of linkToDeath in this file correctly increment the - // ref-count that this onUnlinked callback will decrement. sys::AIBinder_DeathRecipient_setOnUnlinked( recipient, Some(Self::cookie_decr_refcount::<F>), @@ -640,7 +613,12 @@ impl DeathRecipient { /// /// The caller must handle the returned ref-count correctly. unsafe fn new_cookie(&self) -> *mut c_void { - (self.vtable.cookie_incr_refcount)(self.cookie); + // Safety: `cookie_incr_refcount` points to + // `Self::cookie_incr_refcount`, and `self.cookie` is the cookie for an + // Arc<F>. + unsafe { + (self.vtable.cookie_incr_refcount)(self.cookie); + } // Return a raw pointer with ownership of a ref-count self.cookie @@ -659,13 +637,14 @@ impl DeathRecipient { /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc<F> and + /// The `cookie` parameter must be the cookie for an `Arc<F>` and /// the caller must hold a ref-count to it. unsafe extern "C" fn binder_died<F>(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - let callback = (cookie as *const F).as_ref().unwrap(); + // Safety: The caller promises that `cookie` is for an Arc<F>. + let callback = unsafe { (cookie as *const F).as_ref().unwrap() }; callback(); } @@ -674,34 +653,34 @@ impl DeathRecipient { /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc<F> and + /// The `cookie` parameter must be the cookie for an `Arc<F>` and /// the owner must give up a ref-count to it. unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - drop(Arc::from_raw(cookie as *const F)); + // Safety: The caller promises that `cookie` is for an Arc<F>. + drop(unsafe { Arc::from_raw(cookie as *const F) }); } /// Callback that increments the ref-count. /// /// # Safety /// - /// The `cookie` parameter must be the cookie for an Arc<F> and + /// The `cookie` parameter must be the cookie for an `Arc<F>` and /// the owner must handle the created ref-count properly. unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void) where F: Fn() + Send + Sync + 'static, { - let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F)); + // Safety: The caller promises that `cookie` is for an Arc<F>. + let arc = mem::ManuallyDrop::new(unsafe { Arc::from_raw(cookie as *const F) }); mem::forget(Arc::clone(&arc)); } } -/// # Safety -/// -/// A `DeathRecipient` is always constructed with a valid raw pointer to an -/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this +/// Safety: A `DeathRecipient` is always constructed with a valid raw pointer to +/// an `AIBinder_DeathRecipient`, so it is always type-safe to extract this /// pointer. unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient { fn as_native(&self) -> *const sys::AIBinder_DeathRecipient { @@ -715,18 +694,19 @@ unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient { impl Drop for DeathRecipient { fn drop(&mut self) { + // Safety: `self.recipient` is always a valid, owned + // `AIBinder_DeathRecipient` pointer returned by + // `AIBinder_DeathRecipient_new` when `self` was created. This delete + // method can only be called once when `self` is dropped. unsafe { - // Safety: `self.recipient` is always a valid, owned - // `AIBinder_DeathRecipient` pointer returned by - // `AIBinder_DeathRecipient_new` when `self` was created. This - // delete method can only be called once when `self` is dropped. sys::AIBinder_DeathRecipient_delete(self.recipient); + } - // Safety: We own a ref-count to the cookie, and so does every - // linked binder. This call gives up our ref-count. The linked - // binders should already have given up their ref-count, or should - // do so shortly. - (self.vtable.cookie_decr_refcount)(self.cookie) + // Safety: We own a ref-count to the cookie, and so does every linked + // binder. This call gives up our ref-count. The linked binders should + // already have given up their ref-count, or should do so shortly. + unsafe { + (self.vtable.cookie_decr_refcount)(self.cookie); } } } @@ -746,11 +726,9 @@ pub trait Proxy: Sized + Interface { fn from_binder(binder: SpIBinder) -> Result<Self>; } -/// # Safety -/// -/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow -/// invocation of `IBinder` methods directly from `Interface` objects. It shares -/// the same safety as the implementation for `SpIBinder`. +/// Safety: This is a convenience method that wraps `AsNative` for `SpIBinder` +/// to allow invocation of `IBinder` methods directly from `Interface` objects. +/// It shares the same safety as the implementation for `SpIBinder`. unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T { fn as_native(&self) -> *const sys::AIBinder { self.as_binder().as_native() @@ -765,24 +743,20 @@ unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T { /// exist. pub fn get_service(name: &str) -> Option<SpIBinder> { let name = CString::new(name).ok()?; - unsafe { - // Safety: `AServiceManager_getService` returns either a null pointer or - // a valid pointer to an owned `AIBinder`. Either of these values is - // safe to pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) - } + // Safety: `AServiceManager_getService` returns either a null pointer or a + // valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) } } /// Retrieve an existing service, or start it if it is configured as a dynamic /// service and isn't yet started. pub fn wait_for_service(name: &str) -> Option<SpIBinder> { let name = CString::new(name).ok()?; - unsafe { - // Safety: `AServiceManager_waitforService` returns either a null - // pointer or a valid pointer to an owned `AIBinder`. Either of these - // values is safe to pass to `SpIBinder::from_raw`. - SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) - } + // Safety: `AServiceManager_waitforService` returns either a null pointer or + // a valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) } } /// Retrieve an existing service for a particular interface, blocking for a few @@ -801,12 +775,10 @@ pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong< pub fn is_declared(interface: &str) -> Result<bool> { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; - unsafe { - // Safety: `interface` is a valid null-terminated C-style string and is - // only borrowed for the lifetime of the call. The `interface` local - // outlives this call as it lives for the function scope. - Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) - } + // Safety: `interface` is a valid null-terminated C-style string and is only + // borrowed for the lifetime of the call. The `interface` local outlives + // this call as it lives for the function scope. + unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } } /// Retrieve all declared instances for a particular interface @@ -819,11 +791,13 @@ pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { // CString, and outlives this callback. The null handling here is just // to avoid the possibility of unwinding across C code if this crate is // ever compiled with panic=unwind. - if let Some(instances) = opaque.cast::<Vec<CString>>().as_mut() { + if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } { // Safety: instance is a valid null-terminated C string with a // lifetime at least as long as this function, and we immediately // copy it into an owned CString. - instances.push(CStr::from_ptr(instance).to_owned()); + unsafe { + instances.push(CStr::from_ptr(instance).to_owned()); + } } else { eprintln!("Opaque pointer was null in get_declared_instances callback!"); } @@ -831,10 +805,10 @@ pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; let mut instances: Vec<CString> = vec![]; + // Safety: `interface` and `instances` are borrowed for the length of this + // call and both outlive the call. `interface` is guaranteed to be a valid + // null-terminated C-style string. unsafe { - // Safety: `interface` and `instances` are borrowed for the length of - // this call and both outlive the call. `interface` is guaranteed to be - // a valid null-terminated C-style string. sys::AServiceManager_forEachDeclaredInstance( interface.as_ptr(), &mut instances as *mut _ as *mut c_void, @@ -852,10 +826,8 @@ pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { }) } -/// # Safety -/// -/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an -/// `AIBinder`, so we can trivially extract this pointer here. +/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer +/// to an `AIBinder`, so we can trivially extract this pointer here. unsafe impl AsNative<sys::AIBinder> for SpIBinder { fn as_native(&self) -> *const sys::AIBinder { self.0.as_ptr() diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index cc18741a4e..a3a2562eb1 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -22,30 +22,48 @@ use libc::{pid_t, uid_t}; pub struct ProcessState; impl ProcessState { - /// Start the Binder IPC thread pool + /// Starts the Binder IPC thread pool. + /// + /// Starts 1 thread, plus allows the kernel to lazily start up to + /// `num_threads` additional threads as specified by + /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count). + /// + /// This should be done before creating any Binder client or server. If + /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are + /// called, then some things (such as callbacks and + /// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently + /// not work: the callbacks will be queued but never called as there is no + /// thread to call them on. pub fn start_thread_pool() { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_startThreadPool(); } } - /// Set the maximum number of threads that can be started in the threadpool. + /// Sets the maximum number of threads that can be started in the + /// threadpool. /// - /// By default, after startThreadPool is called, this is 15. If it is called - /// additional times, it will only prevent the kernel from starting new - /// threads and will not delete already existing threads. + /// By default, after [`start_thread_pool`](Self::start_thread_pool) is + /// called, this is 15. If it is called additional times, the thread pool + /// size can only be increased. pub fn set_thread_pool_max_thread_count(num_threads: u32) { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads); } } - /// Block on the Binder IPC thread pool + /// Blocks on the Binder IPC thread pool by adding the current thread to the + /// pool. + /// + /// Note that this adds the current thread in addition to those that are + /// created by + /// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count) + /// and [`start_thread_pool`](Self::start_thread_pool). pub fn join_thread_pool() { + // Safety: Safe FFI unsafe { - // Safety: Safe FFI sys::ABinderProcess_joinThreadPool(); } } @@ -68,10 +86,8 @@ impl ThreadState { /// \return calling uid or the current process's UID if this thread isn't /// processing a transaction. pub fn get_calling_uid() -> uid_t { - unsafe { - // Safety: Safe FFI - sys::AIBinder_getCallingUid() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_getCallingUid() } } /// This returns the calling PID assuming that this thread is called from a @@ -93,10 +109,8 @@ impl ThreadState { /// If the transaction being processed is a oneway transaction, then this /// method will return 0. pub fn get_calling_pid() -> pid_t { - unsafe { - // Safety: Safe FFI - sys::AIBinder_getCallingPid() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_getCallingPid() } } /// Determine whether the current thread is currently executing an incoming transaction. @@ -104,10 +118,8 @@ impl ThreadState { /// \return true if the current thread is currently executing an incoming transaction, and false /// otherwise. pub fn is_handling_transaction() -> bool { - unsafe { - // Safety: Safe FFI - sys::AIBinder_isHandlingTransaction() - } + // Safety: Safe FFI + unsafe { sys::AIBinder_isHandlingTransaction() } } /// This function makes the client's security context available to the diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs index 1d1a295221..c5c847b874 100644 --- a/libs/binder/rust/sys/lib.rs +++ b/libs/binder/rust/sys/lib.rs @@ -19,7 +19,20 @@ use std::error::Error; use std::fmt; -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +#[cfg(not(target_os = "trusty"))] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +// Trusty puts the full path to the auto-generated file in BINDGEN_INC_FILE +// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)] +#[cfg(target_os = "trusty")] +#[allow(bad_style)] +mod bindings { + include!(env!("BINDGEN_INC_FILE")); +} + +pub use bindings::*; impl Error for android_c_interface_StatusCode {} diff --git a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp index 59ca6edf12..663b9bbe37 100644 --- a/libs/binder/rust/tests/binderRustNdkInteropTest.cpp +++ b/libs/binder/rust/tests/binderRustNdkInteropTest.cpp @@ -54,14 +54,12 @@ TEST(RustNdkInterop, NdkCanCallRust) { EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); auto interface = aidl::IBinderRustNdkInteropTest::fromBinder(binder); - // TODO(b/167723746): this test requires that fromBinder allow association - // with an already associated local binder by treating it as remote. - EXPECT_EQ(interface, nullptr); + EXPECT_NE(interface, nullptr); - // std::string in("testing"); - // std::string out; - // EXPECT_TRUE(interface->echo(in, &out).isOk()); - // EXPECT_EQ(in, out); + std::string in("testing"); + std::string out; + EXPECT_TRUE(interface->echo(in, &out).isOk()); + EXPECT_EQ(in, out); } int main(int argc, char** argv) { diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index ca2cedc19d..c049b807df 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -545,6 +545,11 @@ mod tests { } fn get_expected_selinux_context() -> &'static str { + // SAFETY: The pointer we pass to `getcon` is valid because it comes from a reference, and + // `getcon` doesn't retain it after it returns. If `getcon` succeeds then `out_ptr` will + // point to a valid C string, otherwise it will remain null. We check for null, so the + // pointer we pass to `CStr::from_ptr` must be a valid pointer to a C string. There is a + // memory leak as we don't call `freecon`, but that's fine because this is just a test. unsafe { let mut out_ptr = ptr::null_mut(); assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs index 415ede1a7b..37f182eb08 100644 --- a/libs/binder/rust/tests/ndk_rust_interop.rs +++ b/libs/binder/rust/tests/ndk_rust_interop.rs @@ -28,10 +28,11 @@ use std::os::raw::{c_char, c_int}; /// /// # Safety /// -/// service_name must be a valid, non-null C-style string (null-terminated). +/// service_name must be a valid, non-null C-style string (nul-terminated). #[no_mangle] pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { - let service_name = CStr::from_ptr(service_name).to_str().unwrap(); + // SAFETY: Our caller promises that service_name is a valid C string. + let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap(); // The Rust class descriptor pointer will not match the NDK one, but the // descriptor strings match so this needs to still associate. @@ -85,10 +86,11 @@ impl IBinderRustNdkInteropTest for Service { /// /// # Safety /// -/// service_name must be a valid, non-null C-style string (null-terminated). +/// service_name must be a valid, non-null C-style string (nul-terminated). #[no_mangle] pub unsafe extern "C" fn rust_start_service(service_name: *const c_char) -> c_int { - let service_name = CStr::from_ptr(service_name).to_str().unwrap(); + // SAFETY: Our caller promises that service_name is a valid C string. + let service_name = unsafe { CStr::from_ptr(service_name) }.to_str().unwrap(); let service = BnBinderRustNdkInteropTest::new_binder(Service, BinderFeatures::default()); match binder::add_service(service_name, service.as_binder()) { Ok(_) => StatusCode::OK as c_int, diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp index df8a2afb03..6eb707bcf7 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -3,25 +3,34 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -rust_fuzz { - name: "parcel_fuzzer_rs", - srcs: [ - "parcel_fuzzer.rs", - ], +rust_defaults { + name: "service_fuzzer_defaults_rs", rustlibs: [ - "libarbitrary", - "libnum_traits", "libbinder_rs", "libbinder_random_parcel_rs", - "binderReadParcelIface-rust", ], - fuzz_config: { cc: [ "waghpawan@google.com", "smoreland@google.com", ], + triage_assignee: "waghpawan@google.com", // hotlist "AIDL fuzzers bugs" on buganizer hotlists: ["4637097"], }, } + +rust_fuzz { + name: "parcel_fuzzer_rs", + srcs: [ + "parcel_fuzzer.rs", + ], + defaults: [ + "service_fuzzer_defaults_rs", + ], + rustlibs: [ + "libarbitrary", + "libnum_traits", + "binderReadParcelIface-rust", + ], +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs index 29bf92cb97..ce0f742934 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs +++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs @@ -105,9 +105,9 @@ fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) { for operation in read_operations { match operation { ReadOperation::SetDataPosition { pos } => { + // Safety: Safe if pos is less than current size of the parcel. + // It relies on C++ code for bound checks unsafe { - // Safety: Safe if pos is less than current size of the parcel. - // It relies on C++ code for bound checks match parcel.set_data_position(pos) { Ok(result) => result, Err(e) => println!("error occurred while setting data position: {:?}", e), diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp index 43a309409d..5cac6475cf 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp @@ -11,7 +11,6 @@ rust_bindgen { source_stem: "bindings", visibility: [":__subpackages__"], bindgen_flags: [ - "--size_t-is-usize", "--allowlist-function", "createRandomParcel", "--allowlist-function", diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp index 5cb406afc2..84130c17e1 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp @@ -19,17 +19,10 @@ rust_fuzz { srcs: [ "service_fuzzer.rs", ], + defaults: [ + "service_fuzzer_defaults_rs", + ], rustlibs: [ - "libbinder_rs", - "libbinder_random_parcel_rs", "testServiceInterface-rust", ], - fuzz_config: { - cc: [ - "waghpawan@google.com", - "smoreland@google.com", - ], - // hotlist "AIDL fuzzers bugs" on buganizer - hotlists: ["4637097"], - }, } diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index 6220db4b28..2b6c282530 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -26,7 +26,7 @@ use binder::{ use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode}; use std::ffi::{c_void, CStr, CString}; -use std::sync::Once; +use std::sync::OnceLock; #[allow( non_camel_case_types, @@ -70,20 +70,18 @@ macro_rules! assert { }; } -static SERVICE_ONCE: Once = Once::new(); -static mut SERVICE: Option<SpIBinder> = None; +static SERVICE: OnceLock<SpIBinder> = OnceLock::new(); /// Start binder service and return a raw AIBinder pointer to it. /// /// Safe to call multiple times, only creates the service once. #[no_mangle] pub extern "C" fn rust_service() -> *mut c_void { - unsafe { - SERVICE_ONCE.call_once(|| { - SERVICE = Some(BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); - }); - SERVICE.as_ref().unwrap().as_raw().cast() - } + let service = SERVICE + .get_or_init(|| BnReadParcelTest::new_binder((), BinderFeatures::default()).as_binder()); + // SAFETY: The SpIBinder will remain alive as long as the program is running because it is in + // the static SERVICE, so the pointer is valid forever. + unsafe { service.as_raw().cast() } } /// Empty interface just to use the declare_binder_interface macro @@ -113,11 +111,13 @@ fn on_transact( bindings::Transaction_TEST_BOOL => { assert!(parcel.read::<bool>()?); assert!(!parcel.read::<bool>()?); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None); reply.write(&true)?; reply.write(&false)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_BOOL }[..])?; reply.write(&(None as Option<Vec<bool>>))?; } @@ -125,14 +125,18 @@ fn on_transact( assert_eq!(parcel.read::<i8>()?, 0); assert_eq!(parcel.read::<i8>()?, 1); assert_eq!(parcel.read::<i8>()?, i8::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 }); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<u8>>()?, unsafe { bindings::TESTDATA_U8 }); assert_eq!(parcel.read::<Option<Vec<i8>>>()?, None); reply.write(&0i8)?; reply.write(&1i8)?; reply.write(&i8::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U8 }[..])?; reply.write(&(None as Option<Vec<i8>>))?; } @@ -140,12 +144,14 @@ fn on_transact( assert_eq!(parcel.read::<u16>()?, 0); assert_eq!(parcel.read::<u16>()?, 1); assert_eq!(parcel.read::<u16>()?, u16::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None); reply.write(&0u16)?; reply.write(&1u16)?; reply.write(&u16::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?; reply.write(&(None as Option<Vec<u16>>))?; } @@ -153,12 +159,14 @@ fn on_transact( assert_eq!(parcel.read::<i32>()?, 0); assert_eq!(parcel.read::<i32>()?, 1); assert_eq!(parcel.read::<i32>()?, i32::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None); reply.write(&0i32)?; reply.write(&1i32)?; reply.write(&i32::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?; reply.write(&(None as Option<Vec<i32>>))?; } @@ -166,12 +174,14 @@ fn on_transact( assert_eq!(parcel.read::<i64>()?, 0); assert_eq!(parcel.read::<i64>()?, 1); assert_eq!(parcel.read::<i64>()?, i64::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None); reply.write(&0i64)?; reply.write(&1i64)?; reply.write(&i64::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?; reply.write(&(None as Option<Vec<i64>>))?; } @@ -179,12 +189,14 @@ fn on_transact( assert_eq!(parcel.read::<u64>()?, 0); assert_eq!(parcel.read::<u64>()?, 1); assert_eq!(parcel.read::<u64>()?, u64::max_value()); + // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None); reply.write(&0u64)?; reply.write(&1u64)?; reply.write(&u64::max_value())?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?; reply.write(&(None as Option<Vec<u64>>))?; } @@ -192,10 +204,12 @@ fn on_transact( assert_eq!(parcel.read::<f32>()?, 0f32); let floats = parcel.read::<Vec<f32>>()?; assert!(floats[0].is_nan()); + // SAFETY: Just reading an extern constant. assert_eq!(floats[1..], unsafe { bindings::TESTDATA_FLOAT }[1..]); assert_eq!(parcel.read::<Option<Vec<f32>>>()?, None); reply.write(&0f32)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_FLOAT }[..])?; reply.write(&(None as Option<Vec<f32>>))?; } @@ -203,10 +217,12 @@ fn on_transact( assert_eq!(parcel.read::<f64>()?, 0f64); let doubles = parcel.read::<Vec<f64>>()?; assert!(doubles[0].is_nan()); + // SAFETY: Just reading an extern constant. assert_eq!(doubles[1..], unsafe { bindings::TESTDATA_DOUBLE }[1..]); assert_eq!(parcel.read::<Option<Vec<f64>>>()?, None); reply.write(&0f64)?; + // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_DOUBLE }[..])?; reply.write(&(None as Option<Vec<f64>>))?; } @@ -216,14 +232,17 @@ fn on_transact( let s: Option<String> = parcel.read()?; assert_eq!(s, None); let s: Option<Vec<Option<String>>> = parcel.read()?; + // SAFETY: Just reading an extern constant. for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) { let expected = + // SAFETY: Just reading an extern constant. unsafe { expected.as_ref().and_then(|e| CStr::from_ptr(e).to_str().ok()) }; assert_eq!(s.as_deref(), expected); } let s: Option<Vec<Option<String>>> = parcel.read()?; assert_eq!(s, None); + // SAFETY: Just reading an extern constant. let strings: Vec<Option<String>> = unsafe { bindings::TESTDATA_STRS .iter() @@ -258,8 +277,7 @@ fn on_transact( assert!(ibinders[1].is_none()); assert!(parcel.read::<Option<Vec<Option<SpIBinder>>>>()?.is_none()); - let service = - unsafe { SERVICE.as_ref().expect("Global binder service not initialized").clone() }; + let service = SERVICE.get().expect("Global binder service not initialized").clone(); reply.write(&service)?; reply.write(&(None as Option<&SpIBinder>))?; reply.write(&[Some(&service), None][..])?; diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 873e9550f9..cd3e7c0fef 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -32,28 +32,8 @@ cc_defaults { } cc_test { - name: "binderDriverInterfaceTest_IPC_32", - defaults: ["binder_test_defaults"], - srcs: ["binderDriverInterfaceTest.cpp"], - header_libs: ["libbinder_headers"], - compile_multilib: "32", - multilib: { - lib32: { - suffix: "", - }, - }, - cflags: ["-DBINDER_IPC_32BIT=1"], - test_suites: ["vts"], -} - -cc_test { name: "binderDriverInterfaceTest", defaults: ["binder_test_defaults"], - product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], - }, - }, header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], test_suites: [ @@ -62,30 +42,6 @@ cc_test { ], } -cc_test { - name: "binderLibTest_IPC_32", - defaults: ["binder_test_defaults"], - srcs: ["binderLibTest.cpp"], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libutils", - ], - static_libs: [ - "libgmock", - ], - compile_multilib: "32", - multilib: { - lib32: { - suffix: "", - }, - }, - cflags: ["-DBINDER_IPC_32BIT=1"], - test_suites: ["vts"], - require_root: true, -} - // unit test only, which can run on host and doesn't use /dev/binder cc_test { name: "binderUnitTest", @@ -111,13 +67,41 @@ cc_test { } cc_test { - name: "binderLibTest", - defaults: ["binder_test_defaults"], - product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], + name: "binderRecordReplayTest", + srcs: ["binderRecordReplayTest.cpp"], + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + ], + static_libs: [ + "binderRecordReplayTestIface-cpp", + "binderReadParcelIface-cpp", + "libbinder_random_parcel_seeds", + "libbinder_random_parcel", + ], + test_suites: ["general-tests"], + require_root: true, +} + +aidl_interface { + name: "binderRecordReplayTestIface", + unstable: true, + srcs: [ + "IBinderRecordReplayTest.aidl", + ], + imports: ["binderReadParcelIface"], + backend: { + java: { + enabled: true, + platform_apis: true, }, }, +} + +cc_test { + name: "binderLibTest", + defaults: ["binder_test_defaults"], srcs: ["binderLibTest.cpp"], shared_libs: [ @@ -716,6 +700,7 @@ cc_benchmark { "liblog", "libutils", ], + test_suites: ["general-tests"], } cc_test_host { @@ -818,3 +803,15 @@ cc_defaults { hotlists: ["4637097"], }, } + +cc_defaults { + name: "fuzzer_disable_leaks", + fuzz_config: { + asan_options: [ + "detect_leaks=0", + ], + hwasan_options: [ + "detect_leaks=0", + ], + }, +} diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl new file mode 100644 index 0000000000..bd6b03c6e0 --- /dev/null +++ b/libs/binder/tests/IBinderRecordReplayTest.aidl @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 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. + */ +import parcelables.SingleDataParcelable; + +interface IBinderRecordReplayTest { + void setByte(byte input); + byte getByte(); + + void setChar(char input); + char getChar(); + + void setBoolean(boolean input); + boolean getBoolean(); + + void setInt(int input); + int getInt(); + + void setFloat(float input); + float getFloat(); + + void setLong(long input); + long getLong(); + + void setDouble(double input); + double getDouble(); + + void setString(String input); + String getString(); + + void setSingleDataParcelable(in SingleDataParcelable p); + SingleDataParcelable getSingleDataParcelable(); + + void setByteArray(in byte[] input); + byte[] getByteArray(); + + void setCharArray(in char[] input); + char[] getCharArray(); + + void setBooleanArray(in boolean[] input); + boolean[] getBooleanArray(); + + void setIntArray(in int[] input); + int[] getIntArray(); + + void setFloatArray(in float[] input); + float[] getFloatArray(); + + void setLongArray(in long[] input); + long[] getLongArray(); + + void setDoubleArray(in double[] input); + double[] getDoubleArray(); + + void setStringArray(in String[] input); + String[] getStringArray(); + + void setSingleDataParcelableArray(in SingleDataParcelable[] input); + SingleDataParcelable[] getSingleDataParcelableArray(); +} diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h deleted file mode 100644 index 369b55dc22..0000000000 --- a/libs/binder/tests/binderAbiHelper.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2020 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 <stdlib.h> -#include <iostream> - -#ifdef BINDER_IPC_32BIT -static constexpr bool kBuild32Abi = true; -#else -static constexpr bool kBuild32Abi = false; -#endif - -// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported -static inline bool ReadKernelConfigIs32BitAbi() { - // failure case implies we run with standard ABI - return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\""); -} - -static inline void ExitIfWrongAbi() { - bool runtime32Abi = ReadKernelConfigIs32BitAbi(); - - if (kBuild32Abi != runtime32Abi) { - std::cout << "[==========] Running 1 test from 1 test suite." << std::endl; - std::cout << "[----------] Global test environment set-up." << std::endl; - std::cout << "[----------] 1 tests from BinderLibTest" << std::endl; - std::cout << "[ RUN ] BinderTest.AbortForWrongAbi" << std::endl; - std::cout << "[ INFO ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl; - std::cout << "[ OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl; - std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl; - std::cout << "" << std::endl; - std::cout << "[----------] Global test environment tear-down" << std::endl; - std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl; - std::cout << "[ PASSED ] 1 tests." << std::endl; - exit(0); - } -} - diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index bc40864020..6712c9cece 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -216,16 +216,16 @@ TEST(RpcBinderAllocation, SetupRpcServer) { auto server = RpcServer::make(); server->setRootObject(sp<BBinder>::make()); - CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())); + ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); std::thread([server]() { server->join(); }).detach(); - status_t status; auto session = RpcSession::make(); - status = session->setupUnixDomainClient(addr.c_str()); - CHECK_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str(); + status_t status = session->setupUnixDomainClient(addr.c_str()); + ASSERT_EQ(status, OK) << "Could not connect: " << addr << ": " << statusToString(status).c_str(); auto remoteBinder = session->getRootObject(); + ASSERT_NE(remoteBinder, nullptr); size_t mallocs = 0, totalBytes = 0; { @@ -233,7 +233,7 @@ TEST(RpcBinderAllocation, SetupRpcServer) { mallocs++; totalBytes += bytes; }); - CHECK_EQ(OK, remoteBinder->pingBinder()); + ASSERT_EQ(OK, remoteBinder->pingBinder()); } EXPECT_EQ(mallocs, 1); EXPECT_EQ(totalBytes, 40); diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 8cc3054f80..cf23a4658c 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -25,8 +25,6 @@ #include <sys/mman.h> #include <poll.h> -#include "binderAbiHelper.h" - #define BINDER_DEV_NAME "/dev/binder" testing::Environment* binder_env; @@ -362,8 +360,7 @@ TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { binderTestReadEmpty(); } -int main(int argc, char **argv) { - ExitIfWrongAbi(); +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv()); diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 8974ad745d..e021af0264 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -48,7 +48,6 @@ #include <sys/un.h> #include "../binder_module.h" -#include "binderAbiHelper.h" #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) @@ -83,7 +82,7 @@ static char binderserverarg[] = "--binderserver"; static constexpr int kSchedPolicy = SCHED_RR; static constexpr int kSchedPriority = 7; static constexpr int kSchedPriorityMore = 8; -static constexpr int kKernelThreads = 15; +static constexpr int kKernelThreads = 17; // anything different than the default static String16 binderLibTestServiceName = String16("test.binderLib"); @@ -1358,17 +1357,20 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), StatusEq(NO_ERROR)); int32_t replyi = reply.readInt32(); - // Expect 16 threads: kKernelThreads = 15 + Pool thread == 16 - EXPECT_TRUE(replyi == kKernelThreads || replyi == kKernelThreads + 1); + // see getThreadPoolMaxTotalThreadCount for why there is a race + EXPECT_TRUE(replyi == kKernelThreads + 1 || replyi == kKernelThreads + 2) << replyi; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_LOCK, data, &reply), NO_ERROR); /* - * This will use all threads in the pool expect the main pool thread. - * The service should run fine without locking, and the thread count should - * not exceed 16 (15 Max + pool thread). + * This will use all threads in the pool but one. There are actually kKernelThreads+2 + * available in the other process (startThreadPool, joinThreadPool, + the kernel- + * started threads from setThreadPoolMaxThreadCount + * + * Adding one more will cause it to deadlock. */ std::vector<std::thread> ts; - for (size_t i = 0; i < kKernelThreads; i++) { + for (size_t i = 0; i < kKernelThreads + 1; i++) { ts.push_back(std::thread([&] { Parcel local_reply; EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), @@ -1376,8 +1378,13 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { })); } - data.writeInt32(500); - // Give a chance for all threads to be used + // make sure all of the above calls will be queued in parallel. Otherwise, most of + // the time, the below call will pre-empt them (presumably because we have the + // scheduler timeslice already + scheduler hint). + sleep(1); + + data.writeInt32(1000); + // Give a chance for all threads to be used (kKernelThreads + 1 thread in use) EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); for (auto &t : ts) { @@ -1387,7 +1394,7 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), StatusEq(NO_ERROR)); replyi = reply.readInt32(); - EXPECT_EQ(replyi, kKernelThreads + 1); + EXPECT_EQ(replyi, kKernelThreads + 2); } TEST_F(BinderLibTest, ThreadPoolStarted) { @@ -2022,9 +2029,7 @@ int run_server(int index, int readypipefd, bool usePoll) return 1; /* joinThreadPool should not return */ } -int main(int argc, char **argv) { - ExitIfWrongAbi(); - +int main(int argc, char** argv) { if (argc == 4 && !strcmp(argv[1], "--servername")) { binderservername = argv[2]; } else { diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index 359c783de5..0a0dae0f59 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -29,6 +29,7 @@ using android::sp; using android::status_t; using android::String16; using android::String8; +using android::base::unique_fd; using android::binder::Status; TEST(Parcel, NonNullTerminatedString8) { @@ -112,6 +113,166 @@ TEST(Parcel, DebugReadAllFds) { EXPECT_EQ(ret[1], STDIN_FILENO); } +TEST(Parcel, AppendFromEmpty) { + Parcel p1; + Parcel p2; + p2.writeInt32(2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(2, p1.readInt32()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendPlainData) { + Parcel p1; + p1.writeInt32(1); + Parcel p2; + p2.writeInt32(2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(2, p1.readInt32()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendPlainDataPartial) { + Parcel p1; + p1.writeInt32(1); + Parcel p2; + p2.writeInt32(2); + p2.writeInt32(3); + p2.writeInt32(4); + + // only copy 8 bytes (two int32's worth) + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(3, p1.readInt32()); + ASSERT_EQ(0, p1.readInt32()); // not 4, end of Parcel + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); +} + +TEST(Parcel, AppendWithBinder) { + sp<IBinder> b1 = sp<BBinder>::make(); + sp<IBinder> b2 = sp<BBinder>::make(); + + Parcel p1; + p1.writeInt32(1); + p1.writeStrongBinder(b1); + Parcel p2; + p2.writeInt32(2); + p2.writeStrongBinder(b2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(b1, p1.readStrongBinder()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(b2, p1.readStrongBinder()); + ASSERT_EQ(2, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_EQ(b2, p2.readStrongBinder()); +} + +TEST(Parcel, AppendWithBinderPartial) { + sp<IBinder> b1 = sp<BBinder>::make(); + sp<IBinder> b2 = sp<BBinder>::make(); + + Parcel p1; + p1.writeInt32(1); + p1.writeStrongBinder(b1); + Parcel p2; + p2.writeInt32(2); + p2.writeStrongBinder(b2); + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into strong binder + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_EQ(b1, p1.readStrongBinder()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(1935813253, p1.readInt32()); // whatever garbage that is there (ABI) + ASSERT_EQ(1, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_EQ(b2, p2.readStrongBinder()); +} + +TEST(Parcel, AppendWithFd) { + unique_fd fd1 = unique_fd(dup(0)); + unique_fd fd2 = unique_fd(dup(0)); + + Parcel p1; + p1.writeInt32(1); + p1.writeDupFileDescriptor(0); // with ownership + p1.writeFileDescriptor(fd1.get()); // without ownership + Parcel p2; + p2.writeInt32(2); + p2.writeDupFileDescriptor(0); // with ownership + p2.writeFileDescriptor(fd2.get()); // without ownership + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, p2.dataSize())); + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(4, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); +} + +TEST(Parcel, AppendWithFdPartial) { + unique_fd fd1 = unique_fd(dup(0)); + unique_fd fd2 = unique_fd(dup(0)); + + Parcel p1; + p1.writeInt32(1); + p1.writeDupFileDescriptor(0); // with ownership + p1.writeFileDescriptor(fd1.get()); // without ownership + Parcel p2; + p2.writeInt32(2); + p2.writeDupFileDescriptor(0); // with ownership + p2.writeFileDescriptor(fd2.get()); // without ownership + + ASSERT_EQ(OK, p1.appendFrom(&p2, 0, 8)); // BAD: 4 bytes into binder + + p1.setDataPosition(0); + ASSERT_EQ(1, p1.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_EQ(2, p1.readInt32()); + ASSERT_EQ(1717840517, p1.readInt32()); // whatever garbage that is there (ABI) + ASSERT_EQ(2, p1.objectsCount()); + + p2.setDataPosition(0); + ASSERT_EQ(2, p2.readInt32()); + ASSERT_NE(-1, p1.readFileDescriptor()); + ASSERT_NE(-1, p1.readFileDescriptor()); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) { diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp new file mode 100644 index 0000000000..6773c95ed6 --- /dev/null +++ b/libs/binder/tests/binderRecordReplayTest.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2023 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 <BnBinderRecordReplayTest.h> +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/RecordedTransaction.h> + +#include <fuzzbinder/libbinder_driver.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <fuzzseeds/random_parcel_seeds.h> + +#include <gtest/gtest.h> + +#include <sys/prctl.h> + +#include "parcelables/SingleDataParcelable.h" + +using namespace android; +using android::generateSeedsFromRecording; +using android::binder::Status; +using android::binder::debug::RecordedTransaction; +using parcelables::SingleDataParcelable; + +const String16 kServerName = String16("binderRecordReplay"); + +#define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \ + Status set##name(T input) { \ + m##name = input; \ + return Status::ok(); \ + } \ + \ + Status get##name(T* output) { \ + *output = m##name; \ + return Status::ok(); \ + } \ + T m##name + +#define GENERATE_GETTER_SETTER(name, T) \ + Status set##name(const T& input) { \ + m##name = input; \ + return Status::ok(); \ + } \ + \ + Status get##name(T* output) { \ + *output = m##name; \ + return Status::ok(); \ + } \ + T m##name + +class MyRecordReplay : public BnBinderRecordReplayTest { +public: + GENERATE_GETTER_SETTER_PRIMITIVE(Boolean, bool); + GENERATE_GETTER_SETTER_PRIMITIVE(Byte, int8_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Int, int); + GENERATE_GETTER_SETTER_PRIMITIVE(Char, char16_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Long, int64_t); + GENERATE_GETTER_SETTER_PRIMITIVE(Float, float); + GENERATE_GETTER_SETTER_PRIMITIVE(Double, double); + + GENERATE_GETTER_SETTER(String, String16); + GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable); + + GENERATE_GETTER_SETTER(BooleanArray, std::vector<bool>); + GENERATE_GETTER_SETTER(ByteArray, std::vector<uint8_t>); + GENERATE_GETTER_SETTER(IntArray, std::vector<int>); + GENERATE_GETTER_SETTER(CharArray, std::vector<char16_t>); + GENERATE_GETTER_SETTER(LongArray, std::vector<int64_t>); + GENERATE_GETTER_SETTER(FloatArray, std::vector<float>); + GENERATE_GETTER_SETTER(DoubleArray, std::vector<double>); + GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>); + GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>); +}; + +std::vector<uint8_t> retrieveData(base::borrowed_fd fd) { + struct stat fdStat; + EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1); + EXPECT_TRUE(fdStat.st_size != 0); + + std::vector<uint8_t> buffer(fdStat.st_size); + auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size); + EXPECT_TRUE(readResult != 0); + return std::move(buffer); +} + +void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) { + base::unique_fd seedFd(open("/data/local/tmp/replayFuzzService", + O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666)); + ASSERT_TRUE(seedFd.ok()); + + // generate corpus from this transaction. + generateSeedsFromRecording(seedFd, transaction); + + // Read the data which has been written to seed corpus + ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET)); + std::vector<uint8_t> seedData = retrieveData(seedFd); + + // use fuzzService to replay the corpus + FuzzedDataProvider provider(seedData.data(), seedData.size()); + fuzzService(binder, std::move(provider)); +} + +void replayBinder(const sp<BpBinder>& binder, const RecordedTransaction& transaction) { + // TODO: move logic to replay RecordedTransaction into RecordedTransaction + Parcel data; + data.setData(transaction.getDataParcel().data(), transaction.getDataParcel().dataSize()); + auto result = binder->transact(transaction.getCode(), data, nullptr, transaction.getFlags()); + + // make sure recording does the thing we expect it to do + EXPECT_EQ(OK, result); +} + +class BinderRecordReplayTest : public ::testing::Test { +public: + void SetUp() override { + // get the remote service + auto binder = defaultServiceManager()->getService(kServerName); + ASSERT_NE(nullptr, binder); + mInterface = interface_cast<IBinderRecordReplayTest>(binder); + mBpBinder = binder->remoteBinder(); + ASSERT_NE(nullptr, mBpBinder); + } + + template <typename T, typename U> + void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue, + Status (IBinderRecordReplayTest::*get)(U*), U changedValue) { + auto replayFunctions = {&replayBinder, &replayFuzzService}; + for (auto replayFunc : replayFunctions) { + base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec", + O_RDWR | O_CREAT | O_CLOEXEC, 0666)); + ASSERT_TRUE(fd.ok()); + + // record a transaction + mBpBinder->startRecordingBinder(fd); + auto status = (*mInterface.*set)(recordedValue); + EXPECT_TRUE(status.isOk()); + mBpBinder->stopRecordingBinder(); + + // test transaction does the thing we expect it to do + U output; + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(output, recordedValue); + + // write over the existing state + status = (*mInterface.*set)(changedValue); + EXPECT_TRUE(status.isOk()); + + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + + EXPECT_EQ(output, changedValue); + + // replay transaction + ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET)); + std::optional<RecordedTransaction> transaction = RecordedTransaction::fromFile(fd); + ASSERT_NE(transaction, std::nullopt); + + const RecordedTransaction& recordedTransaction = *transaction; + // call replay function with recorded transaction + (*replayFunc)(mBpBinder, recordedTransaction); + + status = (*mInterface.*get)(&output); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(output, recordedValue); + } + } + +private: + sp<BpBinder> mBpBinder; + sp<IBinderRecordReplayTest> mInterface; +}; + +TEST_F(BinderRecordReplayTest, ReplayByte) { + recordReplay(&IBinderRecordReplayTest::setByte, int8_t{122}, &IBinderRecordReplayTest::getByte, + int8_t{90}); +} + +TEST_F(BinderRecordReplayTest, ReplayBoolean) { + recordReplay(&IBinderRecordReplayTest::setBoolean, true, &IBinderRecordReplayTest::getBoolean, + false); +} + +TEST_F(BinderRecordReplayTest, ReplayChar) { + recordReplay(&IBinderRecordReplayTest::setChar, char16_t{'G'}, + &IBinderRecordReplayTest::getChar, char16_t{'K'}); +} + +TEST_F(BinderRecordReplayTest, ReplayInt) { + recordReplay(&IBinderRecordReplayTest::setInt, 3, &IBinderRecordReplayTest::getInt, 5); +} + +TEST_F(BinderRecordReplayTest, ReplayFloat) { + recordReplay(&IBinderRecordReplayTest::setFloat, 1.1f, &IBinderRecordReplayTest::getFloat, + 22.0f); +} + +TEST_F(BinderRecordReplayTest, ReplayLong) { + recordReplay(&IBinderRecordReplayTest::setLong, int64_t{1LL << 55}, + &IBinderRecordReplayTest::getLong, int64_t{1LL << 12}); +} + +TEST_F(BinderRecordReplayTest, ReplayDouble) { + recordReplay(&IBinderRecordReplayTest::setDouble, 0.00, &IBinderRecordReplayTest::getDouble, + 1.11); +} + +TEST_F(BinderRecordReplayTest, ReplayString) { + const ::android::String16& input1 = String16("This is saved string"); + const ::android::String16& input2 = String16("This is changed string"); + recordReplay(&IBinderRecordReplayTest::setString, input1, &IBinderRecordReplayTest::getString, + input2); +} + +TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelable) { + SingleDataParcelable saved, changed; + saved.data = 3; + changed.data = 5; + recordReplay(&IBinderRecordReplayTest::setSingleDataParcelable, saved, + &IBinderRecordReplayTest::getSingleDataParcelable, changed); +} + +TEST_F(BinderRecordReplayTest, ReplayByteArray) { + std::vector<uint8_t> savedArray = {uint8_t{255}, uint8_t{0}, uint8_t{127}}; + std::vector<uint8_t> changedArray = {uint8_t{2}, uint8_t{7}, uint8_t{117}}; + recordReplay(&IBinderRecordReplayTest::setByteArray, savedArray, + &IBinderRecordReplayTest::getByteArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayBooleanArray) { + std::vector<bool> savedArray = {true, false, true}; + std::vector<bool> changedArray = {false, true, false}; + recordReplay(&IBinderRecordReplayTest::setBooleanArray, savedArray, + &IBinderRecordReplayTest::getBooleanArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayCharArray) { + std::vector<char16_t> savedArray = {char16_t{'G'}, char16_t{'L'}, char16_t{'K'}, char16_t{'T'}}; + std::vector<char16_t> changedArray = {char16_t{'X'}, char16_t{'Y'}, char16_t{'Z'}}; + recordReplay(&IBinderRecordReplayTest::setCharArray, savedArray, + &IBinderRecordReplayTest::getCharArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayIntArray) { + std::vector<int> savedArray = {12, 45, 178}; + std::vector<int> changedArray = {32, 14, 78, 1899}; + recordReplay(&IBinderRecordReplayTest::setIntArray, savedArray, + &IBinderRecordReplayTest::getIntArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayFloatArray) { + std::vector<float> savedArray = {12.14f, 45.56f, 123.178f}; + std::vector<float> changedArray = {0.00f, 14.0f, 718.1f, 1899.122f, 3268.123f}; + recordReplay(&IBinderRecordReplayTest::setFloatArray, savedArray, + &IBinderRecordReplayTest::getFloatArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayLongArray) { + std::vector<int64_t> savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}}; + std::vector<int64_t> changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33}, + int64_t{1LL << 62}}; + recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray, + &IBinderRecordReplayTest::getLongArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayDoubleArray) { + std::vector<double> savedArray = {12.1412313, 45.561232, 123.1781111}; + std::vector<double> changedArray = {0.00111, 14.32130, 712312318.19, 1899212.122, + 322168.122123}; + recordReplay(&IBinderRecordReplayTest::setDoubleArray, savedArray, + &IBinderRecordReplayTest::getDoubleArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplayStringArray) { + std::vector<String16> savedArray = {String16("This is saved value"), String16(), + String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")}; + + std::vector<String16> changedArray = {String16("This is changed value"), + String16("\xF0\x90\x90\xB7\xE2\x82\xAC")}; + recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray, + &IBinderRecordReplayTest::getStringArray, changedArray); +} + +TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) { + SingleDataParcelable s1, s2, s3, s4, s5; + s1.data = 5213; + s2.data = 1512; + s3.data = 4233; + s4.data = 123124; + s5.data = 0; + std::vector<SingleDataParcelable> saved = {s1, s2, s3}; + std::vector<SingleDataParcelable> changed = {s4, s5}; + + recordReplay(&IBinderRecordReplayTest::setSingleDataParcelableArray, saved, + &IBinderRecordReplayTest::getSingleDataParcelableArray, changed); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + + auto server = sp<MyRecordReplay>::make(); + android::defaultServiceManager()->addService(kServerName, server.get()); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + // not racey, but getService sleeps for 1s + usleep(100000); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp index 593927306e..9c96c4182d 100644 --- a/libs/binder/tests/binderRpcBenchmark.cpp +++ b/libs/binder/tests/binderRpcBenchmark.cpp @@ -129,12 +129,33 @@ static sp<IBinder> getBinderForOptions(benchmark::State& state) { } } +static void SetLabel(benchmark::State& state) { + Transport transport = static_cast<Transport>(state.range(0)); + switch (transport) { +#ifdef __BIONIC__ + case KERNEL: + state.SetLabel("kernel"); + break; +#endif + case RPC: + state.SetLabel("rpc"); + break; + case RPC_TLS: + state.SetLabel("rpc_tls"); + break; + default: + LOG(FATAL) << "Unknown transport value: " << transport; + } +} + void BM_pingTransaction(benchmark::State& state) { sp<IBinder> binder = getBinderForOptions(state); while (state.KeepRunning()) { CHECK_EQ(OK, binder->pingBinder()); } + + SetLabel(state); } BENCHMARK(BM_pingTransaction)->ArgsProduct({kTransportList}); @@ -164,6 +185,8 @@ void BM_repeatTwoPageString(benchmark::State& state) { Status ret = iface->repeatString(str, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_repeatTwoPageString)->ArgsProduct({kTransportList}); @@ -182,6 +205,8 @@ void BM_throughputForTransportAndBytes(benchmark::State& state) { Status ret = iface->repeatBytes(bytes, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_throughputForTransportAndBytes) ->ArgsProduct({kTransportList, @@ -201,6 +226,8 @@ void BM_repeatBinder(benchmark::State& state) { Status ret = iface->repeatBinder(binder, &out); CHECK(ret.isOk()) << ret; } + + SetLabel(state); } BENCHMARK(BM_repeatBinder)->ArgsProduct({kTransportList}); @@ -228,11 +255,6 @@ int main(int argc, char** argv) { ::benchmark::Initialize(&argc, argv); if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; - std::cerr << "Tests suffixes:" << std::endl; - std::cerr << "\t.../" << Transport::KERNEL << " is KERNEL" << std::endl; - std::cerr << "\t.../" << Transport::RPC << " is RPC" << std::endl; - std::cerr << "\t.../" << Transport::RPC_TLS << " is RPC with TLS" << std::endl; - #ifdef __BIONIC__ if (0 == fork()) { prctl(PR_SET_PDEATHSIG, SIGHUP); // racey, okay diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 8d1300779a..4c3c68e2e7 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -249,12 +249,12 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions); } - 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()); + SocketType socketType = GetParam().type; + RpcSecurity rpcSecurity = GetParam().security; + uint32_t clientVersion = GetParam().clientVersion; + uint32_t serverVersion = GetParam().serverVersion; + bool singleThreaded = GetParam().singleThreaded; + bool noKernel = GetParam().noKernel; std::string path = android::base::GetExecutableDirectory(); auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(), @@ -461,8 +461,11 @@ static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCall EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs); - // Potential flake, but make sure calls are handled in parallel. - EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs); + // Potential flake, but make sure calls are handled in parallel. Due + // to past flakes, this only checks that the amount of time taken has + // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested + // check this more exactly. + EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs); } TEST_P(BinderRpc, ThreadPoolOverSaturated) { @@ -671,7 +674,7 @@ TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { // session 0 - will check for leaks in destrutor of proc // session 1 - we want to make sure it gets deleted when we drop all references to it auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, .numIncomingConnectionsBySession = {0, 1}, .numSessions = 2}); + {.numThreads = 1, .numSessions = 2, .numIncomingConnectionsBySession = {0, 1}}); wp<RpcSession> session = proc.proc->sessions.at(1).session; @@ -687,6 +690,12 @@ TEST_P(BinderRpc, SessionWithIncomingThreadpoolDoesntLeak) { } EXPECT_EQ(nullptr, session.promote()); + + // now that it has died, wait for the remote session to shutdown + std::vector<int32_t> remoteCounts; + do { + EXPECT_OK(proc.rootIface->countBinders(&remoteCounts)); + } while (remoteCounts.size() > 1); } TEST_P(BinderRpc, SingleDeathRecipient) { @@ -1112,15 +1121,30 @@ TEST_P(BinderRpc, Fds) { } #ifdef BINDER_RPC_TO_TRUSTY_TEST -INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, - ::testing::Combine(::testing::Values(SocketType::TIPC), - ::testing::Values(RpcSecurity::RAW), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(true), ::testing::Values(true)), + +static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() { + std::vector<BinderRpc::ParamType> ret; + + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + ret.push_back(BinderRpc::ParamType{ + .type = SocketType::TIPC, + .security = RpcSecurity::RAW, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + .singleThreaded = true, + .noKernel = true, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()), BinderRpc::PrintParamInfo); #else // BINDER_RPC_TO_TRUSTY_TEST -static bool testSupportVsockLoopback() { +bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); @@ -1220,7 +1244,15 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); +#ifdef __BIONIC__ + // Devices may not have vsock support. AVF tests will verify whether they do, but + // we can't require it due to old kernels for the time being. static bool hasVsockLoopback = testSupportVsockLoopback(); +#else + // On host machines, we always assume we have vsock loopback. If we don't, the + // subsequent failures will be more clear than showing one now. + static bool hasVsockLoopback = true; +#endif if (hasVsockLoopback) { ret.push_back(SocketType::VSOCK); @@ -1229,13 +1261,47 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } -INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, - ::testing::Combine(::testing::ValuesIn(testSocketTypes()), - ::testing::ValuesIn(RpcSecurityValues()), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(false, true), - ::testing::Values(false, true)), +static std::vector<BinderRpc::ParamType> getBinderRpcParams() { + std::vector<BinderRpc::ParamType> ret; + + constexpr bool full = false; + + for (const auto& type : testSocketTypes()) { + if (full || type == SocketType::UNIX) { + for (const auto& security : RpcSecurityValues()) { + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + for (bool singleThreaded : {false, true}) { + for (bool noKernel : {false, true}) { + ret.push_back(BinderRpc::ParamType{ + .type = type, + .security = security, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + .singleThreaded = singleThreaded, + .noKernel = noKernel, + }); + } + } + } + } + } + } else { + ret.push_back(BinderRpc::ParamType{ + .type = type, + .security = RpcSecurity::RAW, + .clientVersion = RPC_WIRE_PROTOCOL_VERSION, + .serverVersion = RPC_WIRE_PROTOCOL_VERSION, + .singleThreaded = false, + .noKernel = false, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1353,7 +1419,7 @@ TEST_P(BinderRpcServerOnly, SetExternalServerTest) { base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam()))); - server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam()))); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); @@ -1369,7 +1435,7 @@ TEST_P(BinderRpcServerOnly, Shutdown) { auto addr = allocateSocketAddress(); auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam()))); - server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam()))); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared<OneOffSignal>(); @@ -1418,7 +1484,9 @@ public: std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity)); - rpcServer->setProtocolVersion(serverVersion); + if (!rpcServer->setProtocolVersion(serverVersion)) { + return AssertionFailure() << "Invalid protocol version: " << serverVersion; + } switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h index 6cde9f7f8d..2c9646b30e 100644 --- a/libs/binder/tests/binderRpcTestFixture.h +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -79,6 +79,7 @@ struct BinderRpcTestProcessSession { expectAlreadyShutdown = true; } + BinderRpcTestProcessSession(std::unique_ptr<ProcessSession> proc) : proc(std::move(proc)){}; BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; ~BinderRpcTestProcessSession() { if (!expectAlreadyShutdown) { @@ -105,15 +106,23 @@ struct BinderRpcTestProcessSession { } }; -class BinderRpc : public ::testing::TestWithParam< - std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { +struct BinderRpcParam { + SocketType type; + RpcSecurity security; + uint32_t clientVersion; + uint32_t serverVersion; + bool singleThreaded; + bool noKernel; +}; +class BinderRpc : public ::testing::TestWithParam<BinderRpcParam> { 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 serverSingleThreaded() const { return std::get<4>(GetParam()); } - bool noKernel() const { return std::get<5>(GetParam()); } + // TODO: avoid unnecessary layer of indirection + SocketType socketType() const { return GetParam().type; } + RpcSecurity rpcSecurity() const { return GetParam().security; } + uint32_t clientVersion() const { return GetParam().clientVersion; } + uint32_t serverVersion() const { return GetParam().serverVersion; } + bool serverSingleThreaded() const { return GetParam().singleThreaded; } + bool noKernel() const { return GetParam().noKernel; } bool clientOrServerSingleThreaded() const { return !kEnableRpcThreads || serverSingleThreaded(); @@ -138,9 +147,7 @@ public: } BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { - BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcessEtc(options), - }; + BinderRpcTestProcessSession ret(createRpcTestSocketServerProcessEtc(options)); ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root; ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); @@ -149,15 +156,16 @@ public: } static 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) { + auto ret = PrintToString(info.param.type) + "_" + + newFactory(info.param.security)->toCString() + "_clientV" + + std::to_string(info.param.clientVersion) + "_serverV" + + std::to_string(info.param.serverVersion); + if (info.param.singleThreaded) { ret += "_single_threaded"; } else { ret += "_multi_threaded"; } - if (noKernel) { + if (info.param.noKernel) { ret += "_no_kernel"; } else { ret += "_with_kernel"; diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index a9736d5ecd..7435f308d1 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -118,7 +118,7 @@ int main(int argc, char* argv[]) { auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier)); - server->setProtocolVersion(serverConfig.serverVersion); + CHECK(server->setProtocolVersion(serverConfig.serverVersion)); server->setMaxThreads(serverConfig.numThreads); server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); @@ -139,7 +139,8 @@ int main(int argc, char* argv[]) { CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd))); break; case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort)); + CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort)) + << "Need `sudo modprobe vsock_loopback`?"; break; case SocketType::INET: { CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); @@ -164,7 +165,12 @@ int main(int argc, char* argv[]) { } } - server->setPerSessionRootObject([&](const void* addrPtr, size_t len) { + server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) { + { + sp<RpcSession> spSession = session.promote(); + CHECK_NE(nullptr, spSession.get()); + } + // UNIX sockets with abstract addresses return // sizeof(sa_family_t)==2 in addrlen CHECK_GE(len, sizeof(sa_family_t)); diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp index 85573895e9..cb632e95bf 100644 --- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp +++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp @@ -90,15 +90,18 @@ int main(void) { auto server = std::move(*serverOrErr); serverInfo.server = server; - serverInfo.server->setProtocolVersion(serverVersion); - serverInfo.server->setPerSessionRootObject([=](const void* /*addrPtr*/, size_t /*len*/) { - auto service = sp<MyBinderRpcTestTrusty>::make(); - // Assign a unique connection identifier to service->port so - // getClientPort returns a unique value per connection - service->port = ++gConnectionCounter; - service->server = server; - return service; - }); + if (!serverInfo.server->setProtocolVersion(serverVersion)) { + return EXIT_FAILURE; + } + serverInfo.server->setPerSessionRootObject( + [=](wp<RpcSession> /*session*/, const void* /*addrPtr*/, size_t /*len*/) { + auto service = sp<MyBinderRpcTestTrusty>::make(); + // Assign a unique connection identifier to service->port so + // getClientPort returns a unique value per connection + service->port = ++gConnectionCounter; + service->server = server; + return service; + }); servers.push_back(std::move(serverInfo)); } diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp index 28be10db76..fcb83bdabd 100644 --- a/libs/binder/tests/binderRpcTestTrusty.cpp +++ b/libs/binder/tests/binderRpcTestTrusty.cpp @@ -57,9 +57,9 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( [](size_t n) { return n != 0; }), "Non-zero incoming connections on Trusty"); - RpcSecurity rpcSecurity = std::get<1>(GetParam()); - uint32_t clientVersion = std::get<2>(GetParam()); - uint32_t serverVersion = std::get<3>(GetParam()); + RpcSecurity rpcSecurity = GetParam().security; + uint32_t clientVersion = GetParam().clientVersion; + uint32_t serverVersion = GetParam().serverVersion; auto ret = std::make_unique<TrustyProcessSession>(); @@ -89,12 +89,27 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( return ret; } -INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, - ::testing::Combine(::testing::Values(SocketType::TIPC), - ::testing::Values(RpcSecurity::RAW), - ::testing::ValuesIn(testVersions()), - ::testing::ValuesIn(testVersions()), - ::testing::Values(false), ::testing::Values(true)), +static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() { + std::vector<BinderRpc::ParamType> ret; + + for (const auto& clientVersion : testVersions()) { + for (const auto& serverVersion : testVersions()) { + ret.push_back(BinderRpc::ParamType{ + .type = SocketType::TIPC, + .security = RpcSecurity::RAW, + .clientVersion = clientVersion, + .serverVersion = serverVersion, + // TODO: should we test both versions here? + .singleThreaded = false, + .noKernel = true, + }); + } + } + + return ret; +} + +INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc, ::testing::ValuesIn(getTrustyBinderRpcParams()), BinderRpc::PrintParamInfo); } // namespace android diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index 1f4601010c..e43508ee79 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -84,7 +84,7 @@ TEST_P(BinderRpc, SeparateRootObject) { GTEST_SKIP() << "This test requires a multi-threaded service"; } - SocketType type = std::get<0>(GetParam()); + SocketType type = GetParam().type; if (type == SocketType::PRECONNECTED || type == SocketType::UNIX || type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) { // we can't get port numbers for unix sockets diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index c857d62cb2..5e8a32a61b 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -35,6 +35,7 @@ #include <optional> +#include <inttypes.h> #include <sys/eventfd.h> #include <sys/prctl.h> @@ -686,10 +687,12 @@ TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) { // Determine the maximum number of fds this process can have open struct rlimit limit {}; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &limit)); - uint32_t maxFds = static_cast<uint32_t>(limit.rlim_cur); + uint64_t maxFds = limit.rlim_cur; + + ALOG(LOG_INFO, "SafeInterfaceTest", "%s max FDs: %" PRIu64, __PRETTY_FUNCTION__, maxFds); // Perform this test enough times to rule out fd leaks - for (uint32_t iter = 0; iter < (2 * maxFds); ++iter) { + for (uint32_t iter = 0; iter < (maxFds + 100); ++iter) { native_handle* handle = native_handle_create(1 /*numFds*/, 1 /*numInts*/); ASSERT_NE(nullptr, handle); handle->data[0] = dup(eventFd.get()); diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 35866adf20..383795eff5 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -104,3 +104,43 @@ cc_library_static { local_include_dirs: ["include_random_parcel"], export_include_dirs: ["include_random_parcel"], } + +cc_library { + name: "libbinder_random_parcel_seeds", + host_supported: true, + vendor_available: true, + target: { + darwin: { + enabled: false, + }, + }, + srcs: [ + "random_parcel_seeds.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + "libcutils", + "libutils", + ], + local_include_dirs: [ + "include_random_parcel_seeds", + ], + export_include_dirs: ["include_random_parcel_seeds"], +} + +cc_binary_host { + name: "binder2corpus", + static_libs: [ + "libbinder_random_parcel_seeds", + ], + srcs: [ + "binder2corpus/binder2corpus.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + ], +} diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md new file mode 100644 index 0000000000..59bf9f31c0 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md @@ -0,0 +1,31 @@ +# binder2corpus + +This tool converts recordings generated by record_binder tool to fuzzer seeds for fuzzService. + +# Steps to add corpus: + +## Start recording the service binder +ex. record_binder start manager + +## Run test on device or keep device idle +ex. atest servicemanager_test + +## Stop the recording +record_binder stop manager + +## Pull the recording on host +Recordings are present on device at /data/local/recordings/<service_name>. Use adb pull. +Use inspect command of record_binder to check if there are some transactions captured. +ex. record_binder inspect manager + +## run corpus generator tool +binder2corpus <recording_path> <dir_to_write_corpus> + +## Build fuzzer and sync data directory +ex. m servicemanager_fuzzer && adb sync data + +## Push corpus on device +ex. adb push servicemanager_fuzzer_corpus/ /data/fuzz/x86_64/servicemanager_fuzzer/ + +## Run fuzzer with corpus directory as argument +ex. adb shell /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer_corpus
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp new file mode 100644 index 0000000000..c0fdaea0a8 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 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 <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RecordedTransaction.h> + +#include <fuzzseeds/random_parcel_seeds.h> + +#include <sys/prctl.h> + +using android::generateSeedsFromRecording; +using android::status_t; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +status_t generateCorpus(const char* recordingPath, const char* corpusDir) { + unique_fd fd(open(recordingPath, O_RDONLY)); + if (!fd.ok()) { + std::cerr << "Failed to open recording file at path " << recordingPath + << " with error: " << strerror(errno) << '\n'; + return android::BAD_VALUE; + } + + if (auto res = mkdir(corpusDir, 0766); res != 0) { + std::cerr + << "Failed to create corpus directory at path. Delete directory if already exists: " + << corpusDir << std::endl; + return android::BAD_VALUE; + } + + int transactionNumber = 0; + while (auto transaction = RecordedTransaction::fromFile(fd)) { + ++transactionNumber; + std::string filePath = std::string(corpusDir) + std::string("transaction_") + + std::to_string(transactionNumber); + constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC; + android::base::unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666)); + if (!corpusFd.ok()) { + std::cerr << "Failed to open fd. Path " << filePath + << " with error: " << strerror(errno) << std::endl; + return android::UNKNOWN_ERROR; + } + generateSeedsFromRecording(corpusFd, transaction.value()); + } + + if (transactionNumber == 0) { + std::cerr << "No valid transaction has been found in recording file: " << recordingPath + << std::endl; + return android::BAD_VALUE; + } + + return android::NO_ERROR; +} + +void printHelp(const char* toolName) { + std::cout << "Usage: \n\n" + << toolName + << " <recording_path> <destination_directory> \n\n*Use " + "record_binder tool for recording binder transactions." + << std::endl; +} + +int main(int argc, char** argv) { + if (argc != 3) { + printHelp(argv[0]); + return 1; + } + const char* sourcePath = argv[1]; + const char* corpusDir = argv[2]; + if (android::NO_ERROR != generateCorpus(sourcePath, corpusDir)) { + std::cerr << "Failed to generate fuzzer corpus." << std::endl; + return 1; + } + return 0; +} diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h index a9a6197439..cb37cfaa27 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h @@ -19,7 +19,17 @@ #include <binder/IBinder.h> #include <fuzzer/FuzzedDataProvider.h> +#include <vector> + namespace android { + +/** + * See fuzzService, but fuzzes multiple services at the same time. + * + * Consumes providers. + */ +void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider); + /** * Based on the random data in provider, construct an arbitrary number of * Parcel objects and send them to the service in serial. @@ -34,4 +44,5 @@ namespace android { * } */ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider); + } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h index f2b782337c..d8bf87a58c 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h @@ -16,10 +16,21 @@ #pragma once +#include <android/binder_auto_utils.h> #include <android/binder_parcel.h> #include <fuzzer/FuzzedDataProvider.h> +#include <vector> + namespace android { + +/** + * See fuzzService, but fuzzes multiple services at the same time. + * + * Consumes providers. + */ +void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider); + /** * Based on the random data in provider, construct an arbitrary number of * Parcel objects and send them to the service in serial. diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h new file mode 100644 index 0000000000..5755239c8b --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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 <android-base/file.h> +#include <android-base/hex.h> +#include <android-base/logging.h> + +#include <binder/Binder.h> +#include <binder/Parcel.h> +#include <binder/RecordedTransaction.h> + +#include <private/android_filesystem_config.h> + +#include <vector> + +using android::Parcel; +using android::base::HexString; +using std::vector; + +namespace android { +namespace impl { +// computes the bytes so that if they are passed to FuzzedDataProvider and +// provider.ConsumeIntegralInRange<T>(min, max) is called, it will return val +template <typename T> +void writeReversedBuffer(std::vector<std::byte>& integralBuffer, T min, T max, T val); + +// Calls writeInBuffer method with min and max numeric limits of type T. This method +// is reversal of ConsumeIntegral<T>() in FuzzedDataProvider +template <typename T> +void writeReversedBuffer(std::vector<std::byte>& integralBuffer, T val); +} // namespace impl +void generateSeedsFromRecording(base::borrowed_fd fd, + const binder::debug::RecordedTransaction& transaction); +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 8bef33f2ca..93ac1162ed 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -21,29 +21,59 @@ #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> +#include <private/android_filesystem_config.h> + namespace android { void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { - sp<IBinder> target; + fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider)); +} +void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) { RandomParcelOptions options{ - .extraBinders = {binder}, + .extraBinders = binders, .extraFds = {}, }; + // Reserved bytes so that we don't have to change fuzzers and seed corpus if + // we introduce anything new in fuzzService. + std::vector<uint8_t> reservedBytes = provider.ConsumeBytes<uint8_t>(8); + (void)reservedBytes; + + // always refresh the calling identity, because we sometimes set it below, but also, + // the code we're fuzzing might reset it + IPCThreadState::self()->clearCallingIdentity(); + + // Always take so that a perturbation of just the one ConsumeBool byte will always + // take the same path, but with a different UID. Without this, the fuzzer needs to + // guess both the change in value and the shift at the same time. + int64_t maybeSetUid = provider.PickValueInArray<int64_t>( + {static_cast<int64_t>(AID_ROOT) << 32, static_cast<int64_t>(AID_SYSTEM) << 32, + provider.ConsumeIntegralInRange<int64_t>(static_cast<int64_t>(AID_ROOT) << 32, + static_cast<int64_t>(AID_USER) << 32), + provider.ConsumeIntegral<int64_t>()}); + if (provider.ConsumeBool()) { // set calling uid - IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>()); + IPCThreadState::self()->restoreCallingIdentity(maybeSetUid); } while (provider.remaining_bytes() > 0) { // Most of the AIDL services will have small set of transaction codes. - uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>() - : provider.ConsumeIntegralInRange<uint32_t>(0, 100); + // TODO(b/295942369) : Add remaining transact codes from IBinder.h + uint32_t code = provider.ConsumeBool() + ? provider.ConsumeIntegral<uint32_t>() + : provider.PickValueInArray<int64_t>( + {provider.ConsumeIntegralInRange<uint32_t>(0, 100), + IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, + IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, + IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, + IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; // for increased fuzz coverage - data.setEnforceNoDataAvail(provider.ConsumeBool()); + data.setEnforceNoDataAvail(false); + data.setServiceFuzzing(); sp<IBinder> target = options.extraBinders.at( provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); @@ -61,7 +91,8 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { Parcel reply; // for increased fuzz coverage - reply.setEnforceNoDataAvail(provider.ConsumeBool()); + reply.setEnforceNoDataAvail(false); + reply.setServiceFuzzing(); (void)target->transact(code, data, &reply, flags); // feed back in binders and fds that are returned from the service, so that @@ -77,7 +108,6 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { } // invariants - auto ps = ProcessState::selfOrNull(); if (ps) { CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount()) diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp index a1fb70131e..0b0ca34586 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -24,6 +24,15 @@ namespace android { +void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider) { + std::vector<sp<IBinder>> cppBinders; + for (const auto& binder : binders) { + cppBinders.push_back(binder.get()->getBinder()); + } + + fuzzService(cppBinders, std::move(provider)); +} + void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { fuzzService(binder->getBinder(), std::move(provider)); } diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp new file mode 100644 index 0000000000..9e3e2aba77 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2023 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 <android-base/file.h> +#include <android-base/logging.h> + +#include <binder/RecordedTransaction.h> + +#include <fuzzseeds/random_parcel_seeds.h> + +using android::base::WriteFully; + +namespace android { +namespace impl { +template <typename T> +std::vector<uint8_t> reverseBytes(T min, T max, T val) { + uint64_t range = static_cast<uint64_t>(max) - min; + uint64_t result = val - min; + size_t offset = 0; + + std::vector<uint8_t> reverseData; + uint8_t reversed = 0; + reversed |= result; + + while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0) { + reverseData.push_back(reversed); + reversed = 0; + reversed |= (result >> CHAR_BIT); + result = result >> CHAR_BIT; + offset += CHAR_BIT; + } + + return std::move(reverseData); +} + +template <typename T> +void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T min, T max, T val) { + std::vector<uint8_t> reversedData = reverseBytes(min, max, val); + // ConsumeIntegral Calls read buffer from the end. Keep inserting at the front of the buffer + // so that we can align fuzzService operations with seed generation for readability. + integralBuffer.insert(integralBuffer.begin(), reversedData.begin(), reversedData.end()); +} + +template <typename T> +void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T val) { + // For ConsumeIntegral<T>() calls, FuzzedDataProvider uses numeric limits min and max + // as range + writeReversedBuffer(integralBuffer, std::numeric_limits<T>::min(), + std::numeric_limits<T>::max(), val); +} + +} // namespace impl + +void generateSeedsFromRecording(base::borrowed_fd fd, + const binder::debug::RecordedTransaction& transaction) { + // Write Reserved bytes for future use + std::vector<uint8_t> reservedBytes(8); + CHECK(WriteFully(fd, reservedBytes.data(), reservedBytes.size())) << fd.get(); + + std::vector<uint8_t> integralBuffer; + + // Write UID array : Array elements are initialized in the order that they are declared + // UID array index 2 element + // int64_t aidRoot = 0; + impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32, + static_cast<int64_t>(AID_USER) << 32, + static_cast<int64_t>(AID_ROOT) << 32); + + // UID array index 3 element + impl::writeReversedBuffer(integralBuffer, static_cast<int64_t>(AID_ROOT) << 32); + + // always pick AID_ROOT -> index 0 + size_t uidIndex = 0; + impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(3), + uidIndex); + + // Never set uid in seed corpus + uint8_t writeUid = 0; + impl::writeReversedBuffer(integralBuffer, writeUid); + + // Read random code. this will be from recorded transaction + uint8_t selectCode = 1; + impl::writeReversedBuffer(integralBuffer, selectCode); + + // Get from recorded transaction + uint32_t code = transaction.getCode(); + impl::writeReversedBuffer(integralBuffer, code); + + // Get from recorded transaction + uint32_t flags = transaction.getFlags(); + impl::writeReversedBuffer(integralBuffer, flags); + + // always fuzz primary binder + size_t extraBindersIndex = 0; + impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), static_cast<size_t>(0), + extraBindersIndex); + + const Parcel& dataParcel = transaction.getDataParcel(); + + // This buffer holds the bytes which will be used for fillRandomParcel API + std::vector<uint8_t> fillParcelBuffer; + + // Don't take rpc path + uint8_t rpcBranch = 0; + impl::writeReversedBuffer(fillParcelBuffer, rpcBranch); + + // Implicit branch on this path -> options->writeHeader(p, provider) + uint8_t writeHeaderInternal = 0; + impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal); + + // Choose to write data in parcel + size_t fillFuncIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + // Write parcel data size from recorded transaction + size_t toWrite = transaction.getDataParcel().dataBufferSize(); + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), toWrite, toWrite); + + // Write parcel data with size towrite from recorded transaction + CHECK(WriteFully(fd, dataParcel.data(), toWrite)) << fd.get(); + + // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data + size_t subDataSize = toWrite + fillParcelBuffer.size(); + impl::writeReversedBuffer(integralBuffer, static_cast<size_t>(0), subDataSize, subDataSize); + + // Write fill parcel buffer + CHECK(WriteFully(fd, fillParcelBuffer.data(), fillParcelBuffer.size())) << fd.get(); + + // Write the integralBuffer to data + CHECK(WriteFully(fd, integralBuffer.data(), integralBuffer.size())) << fd.get(); +} +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp new file mode 100644 index 0000000000..690c39afc9 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp @@ -0,0 +1,64 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +aidl_interface { + name: "testServiceIface", + host_supported: true, + unstable: true, + srcs: [ + "ITestService.aidl", + ], + backend: { + java: { + enabled: true, + platform_apis: true, + }, + rust: { + enabled: true, + }, + }, +} + +// Adding this fuzzer to test the fuzzService functionality +cc_fuzz { + name: "test_service_fuzzer_should_crash", + defaults: [ + "service_fuzzer_defaults", + ], + static_libs: [ + "liblog", + "testServiceIface-cpp", + ], + host_supported: true, + srcs: ["TestServiceFuzzer.cpp"], + fuzz_config: { + triage_assignee: "waghpawan@google.com", + + // This fuzzer should be used only test fuzzService locally + fuzz_on_haiku_host: false, + fuzz_on_haiku_device: false, + }, +} + +sh_test_host { + name: "fuzz_service_test", + src: "run_fuzz_service_test.sh", + filename: "run_fuzz_service_test.sh", + test_config: "fuzz_service_test_config.xml", + data_bins: [ + "test_service_fuzzer_should_crash", + ], + required: [ + "test_service_fuzzer_should_crash", + ], + target: { + linux_bionic: { + enabled: false, + }, + darwin: { + enabled: false, + }, + }, + test_suites: ["general-tests"], +} diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl new file mode 100644 index 0000000000..5089ae5004 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/ITestService.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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. + */ + +interface ITestService { + + void setIntData(int input); + + void setCharData(char input); + + void setBooleanData(boolean input); + + void setService(ITestService service); +} diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp new file mode 100644 index 0000000000..d2fa581822 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2023 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 <BnTestService.h> +#include <fuzzbinder/libbinder_driver.h> + +#include <binder/IPCThreadState.h> +#include <log/log.h> + +#include <private/android_filesystem_config.h> + +using android::binder::Status; + +namespace android { + +enum class CrashType { + NONE, + ON_PLAIN, + ON_BINDER, + ON_KNOWN_UID, + ON_SYSTEM_AID, + ON_ROOT_AID, + ON_DUMP_TRANSACT, + ON_SHELL_CMD_TRANSACT, + CRASH_ALWAYS, +}; + +// This service is to verify that fuzzService is functioning properly +class TestService : public BnTestService { +public: + TestService(CrashType crash) : mCrash(crash) {} + + void onData() { + switch (mCrash) { + case CrashType::ON_PLAIN: { + LOG_ALWAYS_FATAL("Expected crash, PLAIN."); + break; + } + case CrashType::ON_KNOWN_UID: { + if (IPCThreadState::self()->getCallingUid() == getuid()) { + LOG_ALWAYS_FATAL("Expected crash, KNOWN_UID."); + } + break; + } + case CrashType::ON_SYSTEM_AID: { + if (IPCThreadState::self()->getCallingUid() == AID_SYSTEM) { + LOG_ALWAYS_FATAL("Expected crash, AID_SYSTEM."); + } + break; + } + case CrashType::ON_ROOT_AID: { + if (IPCThreadState::self()->getCallingUid() == AID_ROOT) { + LOG_ALWAYS_FATAL("Expected crash, AID_ROOT."); + } + break; + } + default: + break; + } + } + + Status setIntData(int /*input*/) override { + onData(); + return Status::ok(); + } + + Status setCharData(char16_t /*input*/) override { + onData(); + return Status::ok(); + } + + Status setBooleanData(bool /*input*/) override { + onData(); + return Status::ok(); + } + + Status setService(const sp<ITestService>& service) override { + onData(); + if (mCrash == CrashType::ON_BINDER && service != nullptr) { + LOG_ALWAYS_FATAL("Expected crash, BINDER."); + } + return Status::ok(); + } + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { + if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, DUMP."); + } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT && + code == SHELL_COMMAND_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD."); + } + return BnTestService::onTransact(code, data, reply, flags); + } + +private: + CrashType mCrash; +}; + +CrashType gCrashType = CrashType::NONE; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + if (*argc < 2) { + // This fuzzer is also used as test fuzzer to check infra pipeline. + // It should always run and find a crash in TestService. + gCrashType = CrashType::CRASH_ALWAYS; + return 0; + } + + std::string arg = std::string((*argv)[1]); + + // ignore first argument, because we consume it + (*argv)[1] = (*argv[0]); + (*argc)--; + (*argv)++; + + if (arg == "PLAIN") { + gCrashType = CrashType::ON_PLAIN; + } else if (arg == "KNOWN_UID") { + gCrashType = CrashType::ON_KNOWN_UID; + } else if (arg == "AID_SYSTEM") { + gCrashType = CrashType::ON_SYSTEM_AID; + } else if (arg == "AID_ROOT") { + gCrashType = CrashType::ON_ROOT_AID; + } else if (arg == "BINDER") { + gCrashType = CrashType::ON_BINDER; + } else if (arg == "DUMP") { + gCrashType = CrashType::ON_DUMP_TRANSACT; + } else if (arg == "SHELL_CMD") { + gCrashType = CrashType::ON_SHELL_CMD_TRANSACT; + } else { + printf("INVALID ARG\n"); + exit(0); // success because this is a crash test + } + + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (gCrashType == CrashType::CRASH_ALWAYS) { + LOG_ALWAYS_FATAL("Expected crash, This fuzzer will always crash."); + } + auto service = sp<TestService>::make(gCrashType); + fuzzService(service, FuzzedDataProvider(data, size)); + return 0; +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml new file mode 100644 index 0000000000..19eb33a635 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/fuzz_service_test_config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> +<configuration description="Runs fuzzService test"> + <option name="null-device" value="true" /> + <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" > + <option name="binary" value="run_fuzz_service_test.sh"/> + <option name="relative-path-execution" value="true" /> + </test> +</configuration> diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh new file mode 100755 index 0000000000..c447bffbfd --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright (C) 2023 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. + +color_success=$'\E'"[0;32m" +color_failed=$'\E'"[0;31m" +color_reset=$'\E'"[00m" + +FUZZER_NAME=test_service_fuzzer_should_crash +FUZZER_OUT=fuzzer-output + +if [ ! -f "$FUZZER_NAME" ] +then + echo -e "${color_failed}Binary $FUZZER_NAME does not exist" + echo "${color_reset}" + exit 1 +fi + +for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do + echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE" + + ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT" + + echo "INFO: Searching fuzzer output for expected crashes" + if grep -q "Expected crash, $CRASH_TYPE." "$FUZZER_OUT" + then + echo -e "${color_success}Success: Found expected crash. fuzzService test successful!" + else + echo -e "${color_failed}Failed: Unable to find successful fuzzing output from test_service_fuzzer_should_crash" + echo "${color_reset}" + exit 1 + fi +done diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index b8ae84d5c0..dcc8b8ebf7 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -135,7 +135,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // b/260736889 - limit arbitrarily, due to thread resource exhaustion, which currently // aborts. Servers should consider RpcServer::setConnectionFilter instead. - constexpr size_t kMaxConnections = 1000; + constexpr size_t kMaxConnections = 10; while (provider.remaining_bytes() > 0) { if (connections.empty() || diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index 910c9dc25c..a6fd487fe5 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp @@ -51,8 +51,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { sp<RpcSession> session = RpcSession::make(); session->setMaxIncomingThreads(1); status_t status; - for (size_t tries = 0; tries < 5; tries++) { - usleep(10000); + + // b/274084938 - ASAN may be slow, wait a while + for (size_t tries = 0; tries < 50; tries++) { + usleep(100000); status = session->setupUnixDomainClient(addr.c_str()); if (status == OK) break; } diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp index 68b000849c..8f643230d1 100644 --- a/libs/binder/trusty/RpcServerTrusty.cpp +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -67,7 +67,7 @@ RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::stri // TODO(b/266741352): follow-up to prevent needing this in the future // Trusty needs to be set to the latest stable version that is in prebuilts there. - mRpcServer->setProtocolVersion(0); + LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0)); if (mPortAcl) { // Initialize the array of pointers to uuids. diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index d249b2e1eb..692f82d6cd 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -29,8 +29,6 @@ namespace android { -namespace { - // RpcTransport for Trusty. class RpcTransportTipcTrusty : public RpcTransport { public: @@ -282,8 +280,6 @@ public: std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; -} // namespace - std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const { return std::make_unique<RpcTransportCtxTipcTrusty>(); } diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json index d8b080f0d4..1cefac5a2b 100644 --- a/libs/binder/trusty/binderRpcTest/manifest.json +++ b/libs/binder/trusty/binderRpcTest/manifest.json @@ -1,6 +1,6 @@ { "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b", "app_name": "binderRpcTest", - "min_heap": 163840, + "min_heap": 262144, "min_stack": 16384 } diff --git a/libs/binder/trusty/fuzzer/Android.bp b/libs/binder/trusty/fuzzer/Android.bp new file mode 100644 index 0000000000..2f1f54b0a6 --- /dev/null +++ b/libs/binder/trusty/fuzzer/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2023 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_fuzz { + name: "trusty_binder_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"", + "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"", + "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"", + ], +} + +cc_fuzz { + name: "trusty_binder_rpc_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: [":trusty_tipc_fuzzer"], + cflags: [ + "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"", + "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"", + "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"", + ], +} diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 6678eb8fec..8924b3679f 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -59,14 +59,17 @@ public: size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr); - void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); } + [[nodiscard]] bool setProtocolVersion(uint32_t version) { + return mRpcServer->setProtocolVersion(version); + } void setSupportedFileDescriptorTransportModes( const std::vector<RpcSession::FileDescriptorTransportMode>& modes) { mRpcServer->setSupportedFileDescriptorTransportModes(modes); } 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) { + void setPerSessionRootObject( + std::function<sp<IBinder>(wp<RpcSession> session, const void*, size_t)>&& object) { mRpcServer->setPerSessionRootObject(std::move(object)); } sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); } diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk new file mode 100644 index 0000000000..672d9b75ec --- /dev/null +++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2023 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) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. +LIBBINDER_NDK_BINDGEN_FLAG_FILE := \ + $(LIBBINDER_DIR)/rust/libbinder_ndk_bindgen_flags.txt + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LIBBINDER_DIR)/rust/sys/lib.rs + +MODULE_CRATE_NAME := binder_ndk_sys + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + trusty/user/base/lib/trusty-sys \ + +MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp + +# Add the flags from the flag file +MODULE_BINDGEN_FLAGS += $(shell cat $(LIBBINDER_NDK_BINDGEN_FLAG_FILE)) +MODULE_SRCDEPS += $(LIBBINDER_NDK_BINDGEN_FLAG_FILE) + +include make/library.mk diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 96dcce11ea..38233934c8 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -17,6 +17,7 @@ cc_defaults { shared_libs: [ "libbinder", "libutils", + "liblog", ], target: { darwin: { @@ -40,3 +41,41 @@ cc_test_host { static_libs: ["libgmock"], local_include_dirs: ["include"], } + +rust_bindgen { + name: "libfakeservicemanager_bindgen", + crate_name: "fakeservicemanager_bindgen", + host_supported: true, + wrapper_src: "rust/wrappers/FakeServiceManagerWrapper.hpp", + source_stem: "bindings", + visibility: [":__subpackages__"], + bindgen_flags: [ + "--allowlist-function", + "setupFakeServiceManager", + "--allowlist-function", + "clearFakeServiceManager", + ], + shared_libs: [ + "libc++", + "libbinder", + "libfakeservicemanager", + ], +} + +rust_library { + name: "libfakeservicemanager_rs", + crate_name: "fakeservicemanager_rs", + host_supported: true, + srcs: [ + "rust/src/lib.rs", + ], + shared_libs: [ + "libc++", + "libfakeservicemanager", + ], + rustlibs: [ + "libfakeservicemanager_bindgen", + ], + lints: "none", + clippy_lints: "none", +} diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp index 3272bbc1aa..241c866558 100644 --- a/libs/fakeservicemanager/FakeServiceManager.cpp +++ b/libs/fakeservicemanager/FakeServiceManager.cpp @@ -16,6 +16,10 @@ #include "fakeservicemanager/FakeServiceManager.h" +using android::sp; +using android::FakeServiceManager; +using android::setDefaultServiceManager; + namespace android { FakeServiceManager::FakeServiceManager() {} @@ -26,6 +30,8 @@ sp<IBinder> FakeServiceManager::getService( const String16& name) const { } sp<IBinder> FakeServiceManager::checkService( const String16& name) const { + std::lock_guard<std::mutex> l(mMutex); + auto it = mNameToService.find(name); if (it == mNameToService.end()) { return nullptr; @@ -36,6 +42,8 @@ sp<IBinder> FakeServiceManager::checkService( const String16& name) const { status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service, bool /*allowIsolated*/, int /*dumpsysFlags*/) { + std::lock_guard<std::mutex> l(mMutex); + if (service == nullptr) { return UNEXPECTED_NULL; } @@ -44,6 +52,8 @@ status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& } Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) { + std::lock_guard<std::mutex> l(mMutex); + Vector<String16> services; for (auto const& [name, service] : mNameToService) { (void) service; @@ -61,10 +71,14 @@ sp<IBinder> FakeServiceManager::waitForService(const String16& name) { } bool FakeServiceManager::isDeclared(const String16& name) { + std::lock_guard<std::mutex> l(mMutex); + return mNameToService.find(name) != mNameToService.end(); } Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) { + std::lock_guard<std::mutex> l(mMutex); + Vector<String16> out; const String16 prefix = name + String16("/"); for (const auto& [registeredName, service] : mNameToService) { @@ -108,6 +122,29 @@ std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDeb } void FakeServiceManager::clear() { + std::lock_guard<std::mutex> l(mMutex); + mNameToService.clear(); } } // namespace android + +[[clang::no_destroy]] static sp<FakeServiceManager> gFakeServiceManager; +[[clang::no_destroy]] static std::once_flag gSmOnce; + +extern "C" { + +// Setup FakeServiceManager to mock dependencies in test using this API for rust backend +void setupFakeServiceManager() { + /* Create a FakeServiceManager instance and add required services */ + std::call_once(gSmOnce, [&]() { + gFakeServiceManager = new FakeServiceManager(); + android::setDefaultServiceManager(gFakeServiceManager); + }); +} + +// Clear existing services from Fake SM for rust backend +void clearFakeServiceManager() { + LOG_ALWAYS_FATAL_IF(gFakeServiceManager == nullptr, "Fake Service Manager is not available. Forgot to call setupFakeServiceManager?"); + gFakeServiceManager->clear(); +} +} //extern "C"
\ No newline at end of file diff --git a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h index 97add24ac8..f62241d9c2 100644 --- a/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h +++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h @@ -19,6 +19,7 @@ #include <binder/IServiceManager.h> #include <map> +#include <mutex> #include <optional> #include <vector> @@ -68,6 +69,7 @@ public: void clear(); private: + mutable std::mutex mMutex; std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/fakeservicemanager/rust/src/lib.rs b/libs/fakeservicemanager/rust/src/lib.rs new file mode 100644 index 0000000000..5b7e756914 --- /dev/null +++ b/libs/fakeservicemanager/rust/src/lib.rs @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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. + */ + +use fakeservicemanager_bindgen::{clearFakeServiceManager, setupFakeServiceManager}; +// Setup FakeServiceManager for testing and fuzzing purposes +pub fn setup_fake_service_manager() { + unsafe { + // Safety: This API creates a new FakeSm object which will be always valid and sets up + // defaultServiceManager + setupFakeServiceManager(); + } +} + +// Setup FakeServiceManager for testing and fuzzing purposes +pub fn clear_fake_service_manager() { + unsafe { + // Safety: This API clears all registered services with Fake SM. This should be only used + // setupFakeServiceManager is already called. + clearFakeServiceManager(); + } +} diff --git a/libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp b/libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp new file mode 100644 index 0000000000..1f5923afba --- /dev/null +++ b/libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 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 "fakeservicemanager/FakeServiceManager.h" + +extern "C" { + // Setup FakeServiceManager to mock dependencies in test using this API + void setupFakeServiceManager(); + + // Clear existing services from Fake SM. + void clearFakeServiceManager(); +} // extern "C" diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp index 6fc21c65d1..cb6784c04a 100644 --- a/libs/fakeservicemanager/test_sm.cpp +++ b/libs/fakeservicemanager/test_sm.cpp @@ -22,6 +22,7 @@ #include <binder/IServiceManager.h> #include "fakeservicemanager/FakeServiceManager.h" +#include "rust/wrappers/FakeServiceManagerWrapper.hpp" using android::sp; using android::BBinder; @@ -31,6 +32,7 @@ using android::status_t; using android::FakeServiceManager; using android::String16; using android::IServiceManager; +using android::defaultServiceManager; using testing::ElementsAre; static sp<IBinder> getBinder() { @@ -83,7 +85,7 @@ TEST(GetService, HappyHappy) { EXPECT_EQ(sm->getService(String16("foo")), service); } -TEST(GetService, NonExistant) { +TEST(GetService, NonExistent) { auto sm = new FakeServiceManager(); EXPECT_EQ(sm->getService(String16("foo")), nullptr); @@ -108,7 +110,7 @@ TEST(ListServices, AllServices) { String16("sd"))); } -TEST(WaitForService, NonExistant) { +TEST(WaitForService, NonExistent) { auto sm = new FakeServiceManager(); EXPECT_EQ(sm->waitForService(String16("foo")), nullptr); @@ -124,7 +126,7 @@ TEST(WaitForService, HappyHappy) { EXPECT_EQ(sm->waitForService(String16("foo")), service); } -TEST(IsDeclared, NonExistant) { +TEST(IsDeclared, NonExistent) { auto sm = new FakeServiceManager(); EXPECT_FALSE(sm->isDeclared(String16("foo"))); @@ -139,3 +141,31 @@ TEST(IsDeclared, HappyHappy) { EXPECT_TRUE(sm->isDeclared(String16("foo"))); } + +TEST(SetupFakeServiceManager, NonExistent) { + setupFakeServiceManager(); + + EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), nullptr); +} + +TEST(SetupFakeServiceManager, GetExistingService) { + setupFakeServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(defaultServiceManager()->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), service); + clearFakeServiceManager(); +} + +TEST(ClearFakeServiceManager, GetServiceAfterClear) { + setupFakeServiceManager(); + + sp<IBinder> service = getBinder(); + EXPECT_EQ(defaultServiceManager()->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + clearFakeServiceManager(); + EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), nullptr); +}
\ No newline at end of file diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 732ca36b44..4842476222 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -60,6 +60,17 @@ typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVe typedef bool (*fpANGLEFreeRulesHandle)(void* handle); typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); +namespace { +static bool isVndkEnabled() { +#ifdef __BIONIC__ + // TODO(b/290159430) Use ro.vndk.version to check if VNDK is enabled instead + static bool isVndkEnabled = !android::base::GetBoolProperty("ro.vndk.deprecate", false); + return isVndkEnabled; +#endif + return false; +} +} // namespace + namespace android { enum NativeLibrary { @@ -71,6 +82,8 @@ static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt", "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt"}; +static const char* kLlndkLibrariesTxtPath = "/system/etc/llndk.libraries.txt"; + static std::string vndkVersionStr() { #ifdef __BIONIC__ return base::GetProperty("ro.vndk.version", ""); @@ -108,8 +121,14 @@ static bool readConfig(const std::string& configFile, std::vector<std::string>* } static const std::string getSystemNativeLibraries(NativeLibrary type) { - std::string nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type]; - insertVndkVersionStr(&nativeLibrariesSystemConfig); + std::string nativeLibrariesSystemConfig = ""; + + if (!isVndkEnabled() && type == NativeLibrary::LLNDK) { + nativeLibrariesSystemConfig = kLlndkLibrariesTxtPath; + } else { + nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type]; + insertVndkVersionStr(&nativeLibrariesSystemConfig); + } std::vector<std::string> soNames; if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b9a8fb60aa..298838d816 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -132,13 +132,26 @@ cc_library_static { }, } -filegroup { +aidl_library { + name: "libgui_aidl_hdrs", + hdrs: [ + "android/gui/DisplayInfo.aidl", + "android/gui/FocusRequest.aidl", + "android/gui/InputApplicationInfo.aidl", + "android/gui/IWindowInfosListener.aidl", + "android/gui/IWindowInfosPublisher.aidl", + "android/gui/IWindowInfosReportedListener.aidl", + "android/gui/StalledTransactionInfo.aidl", + "android/gui/WindowInfo.aidl", + "android/gui/WindowInfosUpdate.aidl", + ], +} + +aidl_library { name: "libgui_aidl", srcs: ["aidl/**/*.aidl"], - path: "aidl/", - aidl: { - deps: [":android_gui_aidl"], - }, + strip_import_prefix: "aidl", + deps: ["libgui_aidl_hdrs"], } filegroup { @@ -150,9 +163,6 @@ filegroup { cc_library_static { name: "libgui_aidl_static", vendor_available: true, - srcs: [ - ":libgui_aidl", - ], shared_libs: [ "libbinder", @@ -178,9 +188,7 @@ cc_library_static { aidl: { export_aidl_headers: true, - include_dirs: [ - "frameworks/native/libs/gui", - ], + libs: ["libgui_aidl"], }, } @@ -250,6 +258,7 @@ cc_library_shared { shared_libs: [ "libbinder", + "libGLESv2", ], export_shared_lib_headers: [ @@ -375,7 +384,6 @@ cc_defaults { "libbase", "libcutils", "libEGL", - "libGLESv2", "libhidlbase", "liblog", "libnativewindow", diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index f50bc203e8..e6331e7282 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -24,11 +24,11 @@ #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> -#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 52172090af..5b34ba12c8 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -47,23 +47,23 @@ namespace android { // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) @@ -298,8 +298,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // decrease. mCore->mDequeueCondition.notify_all(); - ATRACE_INT(mCore->mConsumerName.string(), - static_cast<int32_t>(mCore->mQueue.size())); + ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif @@ -718,7 +717,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); - BQ_LOGV("setConsumerName: '%s'", name.string()); + BQ_LOGV("setConsumerName: '%s'", name.c_str()); std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 2930154ad4..648db67fc1 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -41,20 +41,20 @@ namespace android { // Macros for include BufferQueueCore information in log messages -#define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGV(x, ...) \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGD(x, ...) \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGI(x, ...) \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGW(x, ...) \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGE(x, ...) \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) static String8 getUniqueName() { @@ -146,23 +146,23 @@ BufferQueueCore::~BufferQueueCore() {} void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const { std::lock_guard<std::mutex> lock(mMutex); - outResult->appendFormat("%s- BufferQueue ", prefix.string()); + outResult->appendFormat("%s- BufferQueue ", prefix.c_str()); outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n", mMaxAcquiredBufferCount, mMaxDequeuedBufferCount); - outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(), + outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.c_str(), mDequeueBufferCannotBlock, mAsyncMode); - outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(), + outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.c_str(), mQueueBufferCanDrop, mLegacyBufferDrop); - outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(), + outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.c_str(), mDefaultWidth, mDefaultHeight, mDefaultBufferFormat); - outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(), + outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.c_str(), mTransformHint, mFrameCounter); - outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(), + outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.c_str(), mTransformHintInUse, mAutoPrerotation); - outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size()); + outResult->appendFormat("%sFIFO(%zu):\n", prefix.c_str(), mQueue.size()); - outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string()); + outResult->appendFormat("%s(mConsumerName=%s, ", prefix.c_str(), mConsumerName.c_str()); outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi, mConsumerUsageBits); @@ -173,12 +173,11 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const getProcessName(mConnectedPid, producerProcName); getProcessName(pid, consumerProcName); outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId, - mConnectedPid, producerProcName.string(), pid, - consumerProcName.string()); + mConnectedPid, producerProcName.c_str(), pid, consumerProcName.c_str()); Fifo::const_iterator current(mQueue.begin()); while (current != mQueue.end()) { double timestamp = current->mTimestamp / 1e9; - outResult->appendFormat("%s %02d:%p ", prefix.string(), current->mSlot, + outResult->appendFormat("%s %02d:%p ", prefix.c_str(), current->mSlot, current->mGraphicBuffer.get()); outResult->appendFormat("crop=[%d,%d,%d,%d] ", current->mCrop.left, current->mCrop.top, current->mCrop.right, current->mCrop.bottom); @@ -187,12 +186,12 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const ++current; } - outResult->appendFormat("%sSlots:\n", prefix.string()); + outResult->appendFormat("%sSlots:\n", prefix.c_str()); for (int s : mActiveBuffers) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); // A dequeued buffer might be null if it's still being allocated if (buffer.get()) { - outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(), + outResult->appendFormat("%s %s[%02d:%p] ", prefix.c_str(), (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), @@ -200,14 +199,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, buffer->stride, buffer->format); } else { - outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); + outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get()); outResult->appendFormat("state=%-8s frame=%" PRIu64 "\n", mSlots[s].mBufferState.string(), mSlots[s].mFrameNumber); } } for (int s : mFreeBuffers) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); + outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), buffer->handle, mSlots[s].mFrameNumber); outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, @@ -216,7 +215,7 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const for (int s : mFreeSlots) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(), + outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.c_str(), s, buffer.get(), mSlots[s].mBufferState.string()); } } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 43ff54f3c8..10f5899b68 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -47,23 +47,23 @@ namespace android { // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) @@ -573,9 +573,9 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou if (returnFlags & BUFFER_NEEDS_REALLOCATION) { BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - width, height, format, BQ_LAYER_COUNT, usage, - {mConsumerName.string(), mConsumerName.size()}); + sp<GraphicBuffer> graphicBuffer = + new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage, + {mConsumerName.c_str(), mConsumerName.size()}); status_t error = graphicBuffer->initCheck(); @@ -636,7 +636,8 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, - mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); + mSlots[*outSlot].mGraphicBuffer != nullptr ? + mSlots[*outSlot].mGraphicBuffer->handle : nullptr, returnFlags); if (outBufferAge) { *outBufferAge = mCore->mBufferAge; @@ -1045,8 +1046,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; - ATRACE_INT(mCore->mConsumerName.string(), - static_cast<int32_t>(mCore->mQueue.size())); + ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif @@ -1486,7 +1486,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; allocUsage = usage | mCore->mConsumerUsageBits; - allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size()); + allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size()); mCore->mIsAllocating = true; } // Autolock scope @@ -1588,7 +1588,7 @@ status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) { String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mCore->mMutex); - BQ_LOGV("getConsumerName: %s", mConsumerName.string()); + BQ_LOGV("getConsumerName: %s", mConsumerName.c_str()); return mConsumerName; } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 9f91d9d3aa..b625c3f75e 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -41,11 +41,11 @@ #include <utils/Trace.h> // Macros for including the ConsumerBase name in log messages -#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { @@ -86,8 +86,10 @@ ConsumerBase::~ConsumerBase() { // be done by ConsumerBase::onLastStrongRef(), but it's possible for a // derived class to override that method and not call // ConsumerBase::onLastStrongRef(). - LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the " - "consumer is not abandoned!", mName.string()); + LOG_ALWAYS_FATAL_IF(!mAbandoned, + "[%s] ~ConsumerBase was called, but the " + "consumer is not abandoned!", + mName.c_str()); } void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { @@ -451,7 +453,7 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, // them to get an accurate timestamp. if (currentStatus == incomingStatus) { char fenceName[32] = {}; - snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot); + snprintf(fenceName, 32, "%.28s:%d", mName.c_str(), slot); sp<Fence> mergedFence = Fence::merge( fenceName, mSlots[slot].mFence, fence); if (!mergedFence.get()) { diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index a62697064b..3031fa11fc 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -23,11 +23,11 @@ #include <gui/BufferItem.h> #include <utils/Log.h> -#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index b3647d6126..d49489c5a8 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -52,11 +52,11 @@ namespace android { // Macros for including the GLConsumer name in log messages -#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) static const struct { uint32_t width, height; diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 05b55337ab..070f6bf532 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,12 +1,9 @@ -adyabr@google.com -alecmouri@google.com -chaviw@google.com +# Bug component: 1075131 + chrisforbes@google.com jreck@google.com -lpy@google.com -pdwilliams@google.com -racarr@google.com -vishnun@google.com + +file:/services/surfaceflinger/OWNERS per-file EndToEndNativeInputTest.cpp = svv@google.com diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 180ed095d8..95ff96a8a7 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1269,7 +1269,7 @@ sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, boo sp<IBinder> display = nullptr; binder::Status status = ComposerServiceAIDL::getComposerService()->createDisplay(std::string( - displayName.string()), + displayName.c_str()), secure, requestedRefereshRate, &display); return status.isOk() ? display : nullptr; diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 8d0828d88e..22c2be7bc7 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -34,13 +34,13 @@ #include <mutex> #include <condition_variable> -#define ATRACE_BUFFER_INDEX(index) \ - do { \ - if (ATRACE_ENABLED()) { \ - char ___traceBuf[1024]; \ - snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \ - android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ - } \ +#define ATRACE_BUFFER_INDEX(index) \ + do { \ + if (ATRACE_ENABLED()) { \ + char ___traceBuf[1024]; \ + snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.c_str(), (index)); \ + android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ + } \ } while (false) namespace android { diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h index 004d87574a..32dc88ba84 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h @@ -298,7 +298,7 @@ struct TWGraphicBufferProducer : public BASE { } Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override { - _hidl_cb(mBase->getConsumerName().string()); + _hidl_cb(mBase->getConsumerName().c_str()); return Void(); } diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index afeea42546..ae79e5bace 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -177,7 +177,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, while ((err = glGetError()) != GL_NO_ERROR) { msg += String8::format(", %#x", err); } - return ::testing::AssertionFailure(::testing::Message(msg.string())); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { msg += String8::format("r(%d isn't %d)", pixel[0], r); @@ -201,7 +201,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, msg += String8::format("a(%d isn't %d)", pixel[3], a); } if (!msg.isEmpty()) { - return ::testing::AssertionFailure(::testing::Message(msg.string())); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } else { return ::testing::AssertionSuccess(); } @@ -236,8 +236,8 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom); - fprintf(stderr, "assertRectEq: %s\n", msg.string()); - return ::testing::AssertionFailure(::testing::Message(msg.string())); + fprintf(stderr, "assertRectEq: %s\n", msg.c_str()); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } else { return ::testing::AssertionSuccess(); } diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS new file mode 100644 index 0000000000..48cd30d39d --- /dev/null +++ b/libs/gui/tests/OWNERS @@ -0,0 +1,6 @@ +# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces +# Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp +# Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp + +# Android > Android OS & Apps > graphics > Core Graphics Stack (CoGS) +# Bug component: 1075130 diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index f34561f668..ccd0e59616 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -59,7 +59,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < 4; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); + SCOPED_TRACE(String8::format("frame %d", i).c_str()); ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index e2b4f3d035..f76c0be265 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -147,8 +147,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { for (int i = 0; i < 5; i++) { const android_native_rect_t& crop(crops[i]); - SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", - crop.left, crop.top, crop.right, crop.bottom).string()); + SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, crop.top, + crop.right, crop.bottom) + .c_str()); ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); @@ -308,7 +309,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { mFW->waitForFrame(); for (int i = 0; i < numFrames; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); + SCOPED_TRACE(String8::format("frame %d", i).c_str()); // We must wait for each frame to come in because if we ever do an // updateTexImage call that doesn't consume a newly available buffer diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index d7910d26aa..113563dd32 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -415,7 +415,7 @@ TEST_F(SurfaceTest, GetConsumerName) { sp<ANativeWindow> window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); - EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); + EXPECT_STREQ("TestConsumer", surface->getConsumerName().c_str()); } TEST_F(SurfaceTest, GetWideColorSupport) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 12c9e533c3..d571917ff9 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -140,7 +140,7 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) { #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); + tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (status != OK) { ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str()); @@ -297,7 +297,7 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t if (!findKey(ch, &keyCode, &metaState)) { #if DEBUG_MAPPING ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", - deviceId, toString(chars, numChars).string(), ch); + deviceId, toString(chars, numChars).c_str(), ch); #endif return false; } @@ -309,8 +309,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); } #if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", - deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); + ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId, + toString(chars, numChars).c_str(), int32_t(outEvents.size())); for (size_t i = 0; i < outEvents.size(); i++) { ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", outEvents[i].getKeyCode(), outEvents[i].getMetaState(), @@ -756,8 +756,8 @@ KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Form status_t KeyCharacterMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -779,8 +779,8 @@ status_t KeyCharacterMap::Parser::parse() { status_t status = parseKey(); if (status) return status; } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } break; @@ -795,10 +795,9 @@ status_t KeyCharacterMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", + mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str()); + return BAD_VALUE; } } @@ -807,27 +806,27 @@ status_t KeyCharacterMap::Parser::parse() { if (mState != STATE_TOP) { ALOGE("%s: Unterminated key description at end of file.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (mMap->mType == KeyboardType::UNKNOWN) { ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (mFormat == Format::BASE) { if (mMap->mType == KeyboardType::OVERLAY) { ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } else if (mFormat == Format::OVERLAY) { if (mMap->mType != KeyboardType::OVERLAY) { ALOGE("%s: Overlay keyboard layout missing required keyboard " - "'type OVERLAY' declaration.", - mTokenizer->getLocation().string()); + "'type OVERLAY' declaration.", + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -837,8 +836,7 @@ status_t KeyCharacterMap::Parser::parse() { status_t KeyCharacterMap::Parser::parseType() { if (mMap->mType != KeyboardType::UNKNOWN) { - ALOGE("%s: Duplicate keyboard 'type' declaration.", - mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -860,8 +858,8 @@ status_t KeyCharacterMap::Parser::parseType() { } else if (typeToken == "OVERLAY") { type = KeyboardType::OVERLAY; } else { - ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), - typeToken.string()); + ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().c_str(), + typeToken.c_str()); return BAD_VALUE; } @@ -878,8 +876,8 @@ status_t KeyCharacterMap::Parser::parseMap() { mTokenizer->skipDelimiters(WHITESPACE); return parseMapKey(); } - ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } @@ -893,26 +891,26 @@ status_t KeyCharacterMap::Parser::parseMapKey() { } char* end; - int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + int32_t code = int32_t(strtol(codeToken.c_str(), &end, 0)); if (*end) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; const auto it = map.find(code); if (it != map.end()) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } @@ -926,23 +924,23 @@ status_t KeyCharacterMap::Parser::parseMapKey() { status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) { - ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); if (openBraceToken != "{") { - ALOGE("%s: Expected '{' after key code label, got '%s'.", - mTokenizer->getLocation().string(), openBraceToken.string()); + ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().c_str(), + openBraceToken.c_str()); return BAD_VALUE; } @@ -971,10 +969,10 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { properties.emplace_back(PROPERTY_NUMBER); } else { int32_t metaState; - status_t status = parseModifier(token.string(), &metaState); + status_t status = parseModifier(token.c_str(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return status; } properties.emplace_back(PROPERTY_META, metaState); @@ -992,8 +990,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } } - ALOGE("%s: Expected ',' or ':' after property name.", - mTokenizer->getLocation().string()); + ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -1011,18 +1008,17 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { char16_t character; status_t status = parseCharacterLiteral(&character); if (status || !character) { - ALOGE("%s: Invalid character literal for key.", - mTokenizer->getLocation().string()); + ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine character literal with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.character = character; @@ -1032,28 +1028,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { if (token == "none") { if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine 'none' with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } haveCharacter = true; } else if (token == "fallback") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str()); if (!keyCode) { ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.fallbackKeyCode = *keyCode; @@ -1061,29 +1056,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "replace") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str()); if (!keyCode) { ALOGE("%s: Invalid key code label for replace, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine character literal with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.replacementKeyCode = *keyCode; haveReplacement = true; } else { - ALOGE("%s: Expected a key behavior after ':'.", - mTokenizer->getLocation().string()); + ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -1096,7 +1089,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { switch (property.property) { case PROPERTY_LABEL: if (key.label) { - ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } key.label = behavior.character; @@ -1106,7 +1099,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { break; case PROPERTY_NUMBER: if (key.number) { - ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } key.number = behavior.character; @@ -1118,7 +1111,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { for (const Behavior& b : key.behaviors) { if (b.metaState == property.metaState) { ALOGE("%s: Duplicate key behavior for modifier.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -1185,8 +1178,8 @@ status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_ return BAD_VALUE; } if (combinedMeta & metaState) { - ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.c_str()); + ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().c_str(), + token.c_str()); return BAD_VALUE; } @@ -1259,7 +1252,7 @@ status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) } Error: - ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index a194513953..ddc9ea457e 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -177,7 +177,7 @@ base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokeni #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), + tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (!status) { @@ -306,8 +306,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { - ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); mTokenizer->skipDelimiters(WHITESPACE); @@ -334,16 +334,15 @@ status_t KeyLayoutMap::Parser::parse() { status_t status = parseRequiredKernelConfig(); if (status) return status; } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } } @@ -362,26 +361,26 @@ status_t KeyLayoutMap::Parser::parseKey() { codeToken = mTokenizer->nextToken(WHITESPACE); } - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } 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()); + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } @@ -391,15 +390,15 @@ status_t KeyLayoutMap::Parser::parseKey() { if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string()); + std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str()); if (!flag) { - ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); + ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(), + flagToken.c_str()); return BAD_VALUE; } if (flags & *flag) { - ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); + ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(), + flagToken.c_str()); return BAD_VALUE; } flags |= *flag; @@ -417,15 +416,15 @@ status_t KeyLayoutMap::Parser::parseKey() { status_t KeyLayoutMap::Parser::parseAxis() { String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> scanCode = parseInt(scanCodeToken.string()); + std::optional<int> scanCode = parseInt(scanCodeToken.c_str()); if (!scanCode) { - ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(), + scanCodeToken.c_str()); return BAD_VALUE; } if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) { - ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(), + scanCodeToken.c_str()); return BAD_VALUE; } @@ -438,10 +437,10 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 axisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.c_str()); if (!axis) { ALOGE("%s: Expected inverted axis label, got '%s'.", - mTokenizer->getLocation().string(), axisToken.string()); + mTokenizer->getLocation().c_str(), axisToken.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; @@ -450,38 +449,38 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 splitToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> splitValue = parseInt(splitToken.string()); + std::optional<int> splitValue = parseInt(splitToken.c_str()); if (!splitValue) { ALOGE("%s: Expected split value, got '%s'.", - mTokenizer->getLocation().string(), splitToken.string()); + mTokenizer->getLocation().c_str(), splitToken.c_str()); return BAD_VALUE; } axisInfo.splitValue = *splitValue; mTokenizer->skipDelimiters(WHITESPACE); String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str()); if (!axis) { ALOGE("%s: Expected low axis label, got '%s'.", - mTokenizer->getLocation().string(), lowAxisToken.string()); + mTokenizer->getLocation().c_str(), lowAxisToken.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; mTokenizer->skipDelimiters(WHITESPACE); String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string()); + std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str()); if (!highAxis) { ALOGE("%s: Expected high axis label, got '%s'.", - mTokenizer->getLocation().string(), highAxisToken.string()); + mTokenizer->getLocation().c_str(), highAxisToken.c_str()); return BAD_VALUE; } axisInfo.highAxis = *highAxis; } else { - std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(token.c_str()); if (!axis) { ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; @@ -496,16 +495,16 @@ status_t KeyLayoutMap::Parser::parseAxis() { if (keywordToken == "flat") { mTokenizer->skipDelimiters(WHITESPACE); String8 flatToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> flatOverride = parseInt(flatToken.string()); + std::optional<int> flatOverride = parseInt(flatToken.c_str()); if (!flatOverride) { ALOGE("%s: Expected flat value, got '%s'.", - mTokenizer->getLocation().string(), flatToken.string()); + mTokenizer->getLocation().c_str(), flatToken.c_str()); return BAD_VALUE; } axisInfo.flatOverride = *flatOverride; } else { - ALOGE("%s: Expected keyword 'flat', got '%s'.", - mTokenizer->getLocation().string(), keywordToken.string()); + ALOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } } @@ -527,27 +526,27 @@ status_t KeyLayoutMap::Parser::parseLed() { mTokenizer->skipDelimiters(WHITESPACE); codeToken = mTokenizer->nextToken(WHITESPACE); } - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } 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()); + ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string()); + std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.c_str()); if (!ledCode) { - ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), - ledCodeToken.string()); + ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().c_str(), + ledCodeToken.c_str()); return BAD_VALUE; } @@ -569,7 +568,7 @@ static std::optional<InputDeviceSensorType> getSensorType(const char* token) { } static std::optional<int32_t> getSensorDataIndex(String8 token) { - std::string tokenStr(token.string()); + std::string tokenStr(token.c_str()); if (tokenStr == "X") { return 0; } else if (tokenStr == "Y") { @@ -594,26 +593,26 @@ static std::optional<int32_t> getSensorDataIndex(String8 token) { // sensor 0x05 GYROSCOPE Z status_t KeyLayoutMap::Parser::parseSensor() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(), - "abs code", codeToken.string()); + ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().c_str(), + "abs code", codeToken.c_str()); return BAD_VALUE; } std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode; if (map.find(*code) != map.end()) { - ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(), - "abs code", codeToken.string()); + ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().c_str(), + "abs code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string()); + std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.c_str()); if (!typeOpt) { - ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(), - sensorTypeToken.string()); + ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().c_str(), + sensorTypeToken.c_str()); return BAD_VALUE; } InputDeviceSensorType sensorType = typeOpt.value(); @@ -621,8 +620,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE); std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken); if (!indexOpt) { - ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(), - sensorDataIndexToken.string()); + ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().c_str(), + sensorDataIndexToken.c_str()); return BAD_VALUE; } int32_t sensorDataIndex = indexOpt.value(); @@ -643,12 +642,12 @@ status_t KeyLayoutMap::Parser::parseSensor() { // requires_kernel_config CONFIG_HID_PLAYSTATION status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::string configName = codeToken.string(); + std::string configName = codeToken.c_str(); 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()); + mTokenizer->getLocation().c_str(), configName.c_str()); return BAD_VALUE; } diff --git a/libs/input/OWNERS b/libs/input/OWNERS new file mode 100644 index 0000000000..c88bfe97ca --- /dev/null +++ b/libs/input/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/INPUT_OWNERS diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index 548f894d22..315f5a6d4f 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -163,8 +163,8 @@ PropertyMap::Parser::~Parser() {} status_t PropertyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -172,7 +172,7 @@ status_t PropertyMap::Parser::parse() { if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); if (keyToken.isEmpty()) { - ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -180,7 +180,7 @@ status_t PropertyMap::Parser::parse() { if (mTokenizer->nextChar() != '=') { ALOGE("%s: Expected '=' between property key and value.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -189,20 +189,20 @@ status_t PropertyMap::Parser::parse() { String8 valueToken = mTokenizer->nextToken(WHITESPACE); if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { ALOGE("%s: Found reserved character '\\' or '\"' in property value.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } if (mMap->hasProperty(keyToken.string())) { ALOGE("%s: Duplicate property value for key '%s'.", - mTokenizer->getLocation().string(), keyToken.string()); + mTokenizer->getLocation().c_str(), keyToken.c_str()); return BAD_VALUE; } diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 865366bcb2..de62c870ff 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -79,8 +79,8 @@ VirtualKeyMap::Parser::~Parser() { status_t VirtualKeyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -91,7 +91,7 @@ status_t VirtualKeyMap::Parser::parse() { String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); if (token != "0x01") { ALOGE("%s: Unknown virtual key type, expected 0x01.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -103,7 +103,7 @@ status_t VirtualKeyMap::Parser::parse() { && parseNextIntField(&defn.height); if (!success) { ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -116,9 +116,8 @@ status_t VirtualKeyMap::Parser::parse() { } while (consumeFieldDelimiterAndSkipWhitespace()); if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } } @@ -146,9 +145,9 @@ bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); char* end; - *outValue = strtol(token.string(), &end, 0); + *outValue = strtol(token.c_str(), &end, 0); if (token.isEmpty() || *end != '\0') { - ALOGE("Expected an integer, got '%s'.", token.string()); + ALOGE("Expected an integer, got '%s'.", token.c_str()); return false; } return true; diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 0128859ca6..275b7a4888 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -38,10 +38,10 @@ namespace android { // Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) static const struct { uint32_t width, height; diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index cf16739e89..32b229d77c 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -19,7 +19,7 @@ #include <surfacetexture/SurfaceTexture.h> // Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index d3d4cbafdf..9f610e1a50 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -26,10 +26,10 @@ namespace android { // Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) static const mat4 mtxIdentity; diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index 5d23a5ef8d..66e1aa1ca1 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1075131 + adyabr@google.com alecmouri@google.com djsollen@google.com diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp index 3dd534e602..b479400a06 100644 --- a/libs/renderengine/gl/GLExtensions.cpp +++ b/libs/renderengine/gl/GLExtensions.cpp @@ -68,19 +68,19 @@ void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* rende } char const* GLExtensions::getVendor() const { - return mVendor.string(); + return mVendor.c_str(); } char const* GLExtensions::getRenderer() const { - return mRenderer.string(); + return mRenderer.c_str(); } char const* GLExtensions::getVersion() const { - return mVersion.string(); + return mVersion.c_str(); } char const* GLExtensions::getExtensions() const { - return mExtensions.string(); + return mExtensions.c_str(); } void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { @@ -127,11 +127,11 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt } char const* GLExtensions::getEGLVersion() const { - return mEGLVersion.string(); + return mEGLVersion.c_str(); } char const* GLExtensions::getEGLExtensions() const { - return mEGLExtensions.string(); + return mEGLExtensions.c_str(); } } // namespace gl diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp index f7f2d54515..422b070b05 100644 --- a/libs/renderengine/gl/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -62,7 +62,7 @@ public: return out; } friend inline Formatter& operator<<(Formatter& out, const String8& in) { - return operator<<(out, in.string()); + return operator<<(out, in.c_str()); } friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { return (*func)(to); @@ -797,7 +797,7 @@ std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { // fragment shader String8 fs = generateFragmentShader(needs); - return std::make_unique<Program>(needs, vs.string(), fs.string()); + return std::make_unique<Program>(needs, vs.c_str(), fs.c_str()); } void ProgramCache::useProgram(EGLContext context, const Description& description) { diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index b6ea77deb5..9127b3772b 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -74,7 +74,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi if (hwSensor.maxDelay > INT_MAX) { // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should // always fit in a 32 bit integer, log error and cap it to INT_MAX. - ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(), + ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.c_str(), static_cast<int64_t>(hwSensor.maxDelay)); mMaxDelay = INT_MAX; } else { @@ -335,7 +335,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi if (actualReportingMode != expectedReportingMode) { ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " " "actual=%d expected=%d", - mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode); + mName.c_str(), mHandle, mType, actualReportingMode, expectedReportingMode); } } @@ -613,7 +613,7 @@ void Sensor::flattenString8(void*& buffer, size_t& size, const String8& string8) { uint32_t len = static_cast<uint32_t>(string8.length()); FlattenableUtils::write(buffer, size, len); - memcpy(static_cast<char*>(buffer), string8.string(), len); + memcpy(static_cast<char*>(buffer), string8.c_str(), len); FlattenableUtils::advance(buffer, size, len); size -= FlattenableUtils::align<4>(buffer); } diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 073da89758..8675f14d43 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -304,6 +304,12 @@ std::string decodePixelFormat(android::PixelFormat format) { return std::string("BGRA_8888"); case android::PIXEL_FORMAT_R_8: return std::string("R_8"); + case android::PIXEL_FORMAT_R_16_UINT: + return std::string("R_16_UINT"); + case android::PIXEL_FORMAT_RG_1616_UINT: + return std::string("RG_1616_UINT"); + case android::PIXEL_FORMAT_RGBA_10101010: + return std::string("RGBA_10101010"); default: return StringPrintf("Unknown %#08x", format); } diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index cc96f83578..4be0a3a9d9 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -115,7 +115,7 @@ sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1, sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, const sp<Fence>& f2) { - return merge(name.string(), f1, f2); + return merge(name.c_str(), f1, f2); } int Fence::dup() const { diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h index cb61e6a320..494272b1a8 100644 --- a/libs/ui/include/ui/FatVector.h +++ b/libs/ui/include/ui/FatVector.h @@ -65,6 +65,17 @@ public: free(p); } } + + // The STL checks that this member type is present so that + // std::allocator_traits<InlineStdAllocator<T, SIZE>>::rebind_alloc<Other> + // works. std::vector won't be able to construct an + // InlineStdAllocator<Other, SIZE>, because InlineStdAllocator has no + // default constructor, but vector presumably doesn't rebind the allocator + // because it doesn't allocate internal node types. + template <class Other> + struct rebind { + typedef InlineStdAllocator<Other, SIZE> other; + }; Allocation& mAllocation; }; diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp index f1122fd098..9a6bb7a61c 100644 --- a/opengl/libs/EGL/egl_angle_platform.cpp +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -152,6 +152,7 @@ bool initializeAnglePlatform(EGLDisplay dpy) { if (!angleGetDisplayPlatform) { ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!"); + dlclose(so); return false; } @@ -162,6 +163,7 @@ bool initializeAnglePlatform(EGLDisplay dpy) { if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr, &platformMethods))) { ALOGE("ANGLEGetDisplayPlatform call failed!"); + dlclose(so); return false; } if (platformMethods) { diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 88001b2b95..440eb17873 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -49,6 +49,7 @@ #include "egl_trace.h" using namespace android; +using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat; // ---------------------------------------------------------------------------- @@ -406,7 +407,7 @@ EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attri // ---------------------------------------------------------------------------- // Translates EGL color spaces to Android data spaces. -static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { +static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace, PixelFormat pixelFormat) { if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { return HAL_DATASPACE_UNKNOWN; } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { @@ -424,7 +425,13 @@ static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { } else if (colorspace == EGL_GL_COLORSPACE_BT2020_HLG_EXT) { return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG); } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { - return HAL_DATASPACE_BT2020_LINEAR; + if (pixelFormat == PixelFormat::RGBA_FP16) { + return static_cast<android_dataspace>(HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_LINEAR | + HAL_DATASPACE_RANGE_EXTENDED); + } else { + return HAL_DATASPACE_BT2020_LINEAR; + } } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { return HAL_DATASPACE_BT2020_PQ; } @@ -573,8 +580,6 @@ void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) { newList.push_back(EGL_NONE); } -using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat; - // Gets the native pixel format corrsponding to the passed EGLConfig. void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, PixelFormat* format) { @@ -714,7 +719,7 @@ EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); } - android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); + android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace, format); // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed // at this point. diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 4a08c11c14..48d793a4d4 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -143,7 +143,7 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri ALOGV("shellCommand"); for (size_t i = 0, n = args.size(); i < n; i++) - ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string()); + ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str()); if (args.size() >= 1) { if (args[0] == String16("vkjson")) return cmdVkjson(out, err); diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS index 0ff65bf4ae..07c681f1f4 100644 --- a/services/gpuservice/OWNERS +++ b/services/gpuservice/OWNERS @@ -4,3 +4,4 @@ alecmouri@google.com lfy@google.com paulthomson@google.com pbaiget@google.com +kocdemir@google.com diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp new file mode 100644 index 0000000000..6bcc5e8601 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/Android.bp @@ -0,0 +1,26 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "gpu_service_fuzzer", + defaults: [ + "service_fuzzer_defaults", + "fuzzer_disable_leaks", + ], + static_libs: [ + "liblog", + ], + fuzz_config: { + cc: [ + "paulthomson@google.com", + "pbaiget@google.com", + ], + triage_assignee: "waghpawan@google.com", + }, + include_dirs: ["frameworks/native/services/gpuservice/"], + srcs: ["GpuServiceFuzzer.cpp"], + shared_libs: [ + "libgpuservice", + ], +} diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp new file mode 100644 index 0000000000..241b8646e1 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h> + +#include "gpuservice/GpuService.h" + +using ::android::fuzzService; +using ::android::GpuService; +using ::android::sp; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + sp<GpuService> gpuService = new GpuService(); + fuzzService(gpuService, FuzzedDataProvider(data, size)); + return 0; +} diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS index c88bfe97ca..21d208f577 100644 --- a/services/inputflinger/OWNERS +++ b/services/inputflinger/OWNERS @@ -1 +1,2 @@ +# Bug component: 136048 include platform/frameworks/base:/INPUT_OWNERS diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index c906c3e0ce..862e12899e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -6264,9 +6264,9 @@ void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> applic StringPrintf("%s does not have a focused window", application->getName().c_str()); updateLastAnrStateLocked(*application, reason); - auto command = [this, application = std::move(application)]() REQUIRES(mLock) { + auto command = [this, app = std::move(application)]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy.notifyNoFocusedWindowAnr(application); + mPolicy.notifyNoFocusedWindowAnr(app); }; postCommandLocked(std::move(command)); } @@ -6326,9 +6326,9 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token, std::optional<gui::Pid> pid, std::string reason) { - auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) { + auto command = [this, token, pid, r = std::move(reason)]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy.notifyWindowUnresponsive(token, pid, reason); + mPolicy.notifyWindowUnresponsive(token, pid, r); }; postCommandLocked(std::move(command)); } diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index d50f74d5c5..729d01f363 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -26,11 +26,10 @@ namespace android { - /* * Input dispatcher policy interface. * - * The input reader policy is used by the input reader to interact with the Window Manager + * The input dispatcher policy is used by the input dispatcher to interact with the Window Manager * and other system components. * * The actual implementation is partially supported by callbacks into the DVM diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index ec0388d500..de99fc73ec 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -256,14 +256,14 @@ input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* InputDriver::inputGetPropertyKey(input_property_t* property) { if (property != nullptr) { - return property->key.string(); + return property->key.c_str(); } return nullptr; } const char* InputDriver::inputGetPropertyValue(input_property_t* property) { if (property != nullptr) { - return property->value.string(); + return property->value.c_str(); } return nullptr; } @@ -281,7 +281,7 @@ void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) { } void InputDriver::dump(String8& result) { - result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.string()); + result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.c_str()); } } // namespace android diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp index 2da2a70c03..d974c43d78 100644 --- a/services/inputflinger/host/InputFlinger.cpp +++ b/services/inputflinger/host/InputFlinger.cpp @@ -57,7 +57,7 @@ status_t InputFlinger::dump(int fd, const Vector<String16>& args) { } else { dumpInternal(result); } - write(fd, result.string(), result.size()); + write(fd, result.c_str(), result.size()); return OK; } diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp index d7ca6e1dbf..47fa8b39ac 100644 --- a/services/sensorservice/RecentEventLogger.cpp +++ b/services/sensorservice/RecentEventLogger.cpp @@ -83,7 +83,7 @@ std::string RecentEventLogger::dump() const { } buffer.append("\n"); } - return std::string(buffer.string()); + return std::string(buffer.c_str()); } /** diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 10ca990f87..3155b4ceaf 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -298,7 +298,7 @@ std::string SensorDevice::dump() const { result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f); } - return result.string(); + return result.c_str(); } /** diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 4fff8bb1b4..555b80aed3 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -63,7 +63,7 @@ void SensorService::SensorDirectConnection::onFirstRef() { void SensorService::SensorDirectConnection::dump(String8& result) const { Mutex::Autolock _l(mConnectionLock); result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n", - String8(mOpPackageName).string(), getHalChannelHandle(), mActivated.size()); + String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size()); for (auto &i : mActivated) { result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second); } @@ -79,7 +79,7 @@ void SensorService::SensorDirectConnection::dump(String8& result) const { void SensorService::SensorDirectConnection::dump(ProtoOutputStream* proto) const { using namespace service::SensorDirectConnectionProto; Mutex::Autolock _l(mConnectionLock); - proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).string())); + proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).c_str())); proto->write(HAL_CHANNEL_HANDLE, getHalChannelHandle()); proto->write(NUM_SENSOR_ACTIVATED, int(mActivated.size())); for (auto &i : mActivated) { diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index dc5070c315..d469ff4b3c 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -90,12 +90,12 @@ void SensorService::SensorEventConnection::dump(String8& result) { result.append("NORMAL\n"); } result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | " - "max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize, + "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize); for (auto& it : mSensorInfo) { const FlushInfo& flushInfo = it.second; result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n", - mService->getSensorName(it.first).string(), + mService->getSensorName(it.first).c_str(), it.first, flushInfo.mFirstFlushPending ? "First flush pending" : "active", @@ -131,7 +131,7 @@ void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) } else { proto->write(OPERATING_MODE, OP_MODE_NORMAL); } - proto->write(PACKAGE_NAME, std::string(mPackageName.string())); + proto->write(PACKAGE_NAME, std::string(mPackageName.c_str())); proto->write(WAKE_LOCK_REF_COUNT, int32_t(mWakeLockRefCount)); proto->write(UID, int32_t(mUid)); proto->write(CACHE_SIZE, int32_t(mCacheSize)); @@ -848,7 +848,7 @@ int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* if (numBytesRead == sizeof(sensors_event_t)) { if (!mDataInjectionMode) { ALOGE("Data injected in normal mode, dropping event" - "package=%s uid=%d", mPackageName.string(), mUid); + "package=%s uid=%d", mPackageName.c_str(), mUid); // Unregister call backs. return 0; } diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp index e27b52b23e..5c00260008 100644 --- a/services/sensorservice/SensorFusion.cpp +++ b/services/sensorservice/SensorFusion.cpp @@ -19,6 +19,7 @@ #include "SensorService.h" #include <android/util/ProtoOutputStream.h> +#include <cutils/properties.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> namespace android { @@ -60,10 +61,12 @@ SensorFusion::SensorFusion() mGyro = uncalibratedGyro; } - // 200 Hz for gyro events is a good compromise between precision - // and power/cpu usage. - mEstimatedGyroRate = 200; - mTargetDelayNs = 1000000000LL/mEstimatedGyroRate; + // Wearable devices will want to tune this parameter + // to 100 (Hz) in device.mk to save some power. + int32_t value = property_get_int32( + "sensors.aosp_low_power_sensor_fusion.maximum_rate", 200); + mEstimatedGyroRate = static_cast<float>(value); + mTargetDelayNs = 1000000000LL / mEstimatedGyroRate; for (int i = 0; i<NUM_FUSION_MODE; ++i) { mFusions[i].init(i); diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp index daff4d00dc..7e302060c5 100644 --- a/services/sensorservice/SensorList.cpp +++ b/services/sensorservice/SensorList.cpp @@ -150,12 +150,12 @@ std::string SensorList::dump() const { "%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32 ") | perm: %s | flags: 0x%08x\n", s.getHandle(), - s.getName().string(), - s.getVendor().string(), + s.getName().c_str(), + s.getVendor().c_str(), s.getVersion(), - s.getStringType().string(), + s.getStringType().c_str(), s.getType(), - s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a", + s.getRequiredPermission().size() ? s.getRequiredPermission().c_str() : "n/a", static_cast<int>(s.getFlags())); result.append("\t"); @@ -224,7 +224,7 @@ std::string SensorList::dump() const { } return true; }); - return std::string(result.string()); + return std::string(result.c_str()); } /** @@ -241,13 +241,13 @@ void SensorList::dump(util::ProtoOutputStream* proto) const { forEachSensor([&proto] (const Sensor& s) -> bool { const uint64_t token = proto->start(SENSORS); proto->write(HANDLE, s.getHandle()); - proto->write(NAME, std::string(s.getName().string())); - proto->write(VENDOR, std::string(s.getVendor().string())); + proto->write(NAME, std::string(s.getName().c_str())); + proto->write(VENDOR, std::string(s.getVendor().c_str())); proto->write(VERSION, s.getVersion()); - proto->write(STRING_TYPE, std::string(s.getStringType().string())); + proto->write(STRING_TYPE, std::string(s.getStringType().c_str())); proto->write(TYPE, s.getType()); proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ? - s.getRequiredPermission().string() : "")); + s.getRequiredPermission().c_str() : "")); proto->write(FLAGS, int(s.getFlags())); switch (s.getReportingMode()) { case AREPORTING_MODE_CONTINUOUS: diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h index a34a65bbdd..dc9e8215e7 100644 --- a/services/sensorservice/SensorRegistrationInfo.h +++ b/services/sensorservice/SensorRegistrationInfo.h @@ -93,7 +93,7 @@ public: using namespace service::SensorRegistrationInfoProto; proto->write(TIMESTAMP_SEC, int64_t(mRealtimeSec)); proto->write(SENSOR_HANDLE, mSensorHandle); - proto->write(PACKAGE_NAME, std::string(mPackageName.string())); + proto->write(PACKAGE_NAME, std::string(mPackageName.c_str())); proto->write(PID, int32_t(mPid)); proto->write(UID, int32_t(mUid)); proto->write(SAMPLING_RATE_US, mSamplingRateUs); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 398d60242b..049528a330 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -615,7 +615,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { i.second->setFormat("mask_data"); } // if there is events and sensor does not need special permission. - result.appendFormat("%s: ", s->getSensor().getName().string()); + result.appendFormat("%s: ", s->getSensor().getName().c_str()); result.append(i.second->dump().c_str()); } } @@ -626,7 +626,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { int handle = mActiveSensors.keyAt(i); if (dev.isSensorActive(handle)) { result.appendFormat("%s (handle=0x%08x, connections=%zu)\n", - getSensorName(handle).string(), + getSensorName(handle).c_str(), handle, mActiveSensors.valueAt(i)->getNumConnections()); } @@ -642,14 +642,14 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { result.appendFormat(" NORMAL\n"); break; case RESTRICTED: - result.appendFormat(" RESTRICTED : %s\n", mAllowListedPackage.string()); + result.appendFormat(" RESTRICTED : %s\n", mAllowListedPackage.c_str()); break; case DATA_INJECTION: - result.appendFormat(" DATA_INJECTION : %s\n", mAllowListedPackage.string()); + result.appendFormat(" DATA_INJECTION : %s\n", mAllowListedPackage.c_str()); break; case REPLAY_DATA_INJECTION: result.appendFormat(" REPLAY_DATA_INJECTION : %s\n", - mAllowListedPackage.string()); + mAllowListedPackage.c_str()); break; default: result.appendFormat(" UNKNOWN\n"); @@ -691,7 +691,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { } while(startIndex != currentIndex); } } - write(fd, result.string(), result.size()); + write(fd, result.c_str(), result.size()); return NO_ERROR; } @@ -739,7 +739,7 @@ status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock "normal" : "mask_data"); const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS); proto.write(service::SensorEventsProto::RecentEventsLog::NAME, - std::string(s->getSensor().getName().string())); + std::string(s->getSensor().getName().c_str())); i.second->dump(&proto); proto.end(mToken); } @@ -753,7 +753,7 @@ status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock if (dev.isSensorActive(handle)) { token = proto.start(ACTIVE_SENSORS); proto.write(service::ActiveSensorProto::NAME, - std::string(getSensorName(handle).string())); + std::string(getSensorName(handle).c_str())); proto.write(service::ActiveSensorProto::HANDLE, handle); proto.write(service::ActiveSensorProto::NUM_CONNECTIONS, int(mActiveSensors.valueAt(i)->getNumConnections())); @@ -771,11 +771,11 @@ status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock break; case RESTRICTED: proto.write(OPERATING_MODE, OP_MODE_RESTRICTED); - proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string())); + proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.c_str())); break; case DATA_INJECTION: proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION); - proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string())); + proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.c_str())); break; default: proto.write(OPERATING_MODE, OP_MODE_UNKNOWN); @@ -918,8 +918,8 @@ static status_t getUidForPackage(String16 packageName, int userId, /*inout*/uid_ PermissionController pc; uid = pc.getPackageUid(packageName, 0); if (uid <= 0) { - ALOGE("Unknown package: '%s'", String8(packageName).string()); - dprintf(err, "Unknown package: '%s'\n", String8(packageName).string()); + ALOGE("Unknown package: '%s'", String8(packageName).c_str()); + dprintf(err, "Unknown package: '%s'\n", String8(packageName).c_str()); return BAD_VALUE; } @@ -944,7 +944,7 @@ status_t SensorService::handleSetUidState(Vector<String16>& args, int err) { if (args[2] == String16("active")) { active = true; } else if ((args[2] != String16("idle"))) { - ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string()); + ALOGE("Expected active or idle but got: '%s'", String8(args[2]).c_str()); return BAD_VALUE; } @@ -2182,10 +2182,10 @@ bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation, !isAudioServerOrSystemServerUid(IPCThreadState::self()->getCallingUid())) { if (!mHtRestricted) { ALOGI("Permitting access to HT sensor type outside system (%s)", - String8(opPackageName).string()); + String8(opPackageName).c_str()); } else { - ALOGW("%s %s a sensor (%s) as a non-system client", String8(opPackageName).string(), - operation, sensor.getName().string()); + ALOGW("%s %s a sensor (%s) as a non-system client", String8(opPackageName).c_str(), + operation, sensor.getName().c_str()); return false; } } @@ -2218,8 +2218,8 @@ bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation, } if (!canAccess) { - ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(), - operation, sensor.getName().string(), sensor.getRequiredPermission().string()); + ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).c_str(), + operation, sensor.getName().c_str(), sensor.getRequiredPermission().c_str()); } return canAccess; @@ -2399,7 +2399,7 @@ void SensorService::sendEventsFromCache(const sp<SensorEventConnection>& connect } bool SensorService::isAllowListedPackage(const String8& packageName) { - return (packageName.contains(mAllowListedPackage.string())); + return (packageName.contains(mAllowListedPackage.c_str())); } bool SensorService::isOperationRestrictedLocked(const String16& opPackageName) { diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp index 5301fe9931..ed4829abe3 100644 --- a/services/sensorservice/aidl/fuzzer/Android.bp +++ b/services/sensorservice/aidl/fuzzer/Android.bp @@ -11,6 +11,7 @@ cc_fuzz { name: "libsensorserviceaidl_fuzzer", defaults: [ "service_fuzzer_defaults", + "fuzzer_disable_leaks", ], host_supported: true, static_libs: [ diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp index 5fa594d01d..d338d02675 100644 --- a/services/sensorservice/hidl/utils.cpp +++ b/services/sensorservice/hidl/utils.cpp @@ -32,8 +32,8 @@ SensorInfo convertSensor(const Sensor& src) { SensorInfo dst; const String8& name = src.getName(); const String8& vendor = src.getVendor(); - dst.name = hidl_string{name.string(), name.size()}; - dst.vendor = hidl_string{vendor.string(), vendor.size()}; + dst.name = hidl_string{name.c_str(), name.size()}; + dst.vendor = hidl_string{vendor.c_str(), vendor.size()}; dst.version = src.getVersion(); dst.sensorHandle = src.getHandle(); dst.type = static_cast<::android::hardware::sensors::V1_0::SensorType>( diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index b00d1a761b..88521f1185 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -116,7 +116,7 @@ int main() { Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER); printf("accelerometer=%p (%s)\n", - accelerometer, accelerometer->getName().string()); + accelerometer, accelerometer->getName().c_str()); sStartTime = systemTime(); @@ -141,7 +141,7 @@ int main() { printf("ALOOPER_POLL_TIMEOUT\n"); break; case ALOOPER_POLL_ERROR: - printf("ALOOPER_POLL_TIMEOUT\n"); + printf("ALOOPER_POLL_ERROR\n"); break; default: printf("ugh? poll returned %d\n", ret); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index f439caf9e1..8dab6ce61b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp @@ -34,7 +34,7 @@ LayerState::LayerState(compositionengine::OutputLayer* layer) [](const mat4& mat) { using namespace std::string_literals; std::vector<std::string> split = - base::Split(std::string(mat.asString().string()), "\n"s); + base::Split(std::string(mat.asString().c_str()), "\n"s); split.pop_back(); // Strip the last (empty) line return split; }}) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 54133d92b0..5e6cade56f 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -216,32 +216,32 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { base::StringAppendF(&result, "Expected two layer stack hashes, e.g. '--planner %s " "<left_hash> <right_hash>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 4) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <left_hash> " "<right_hash>'\n", - command.string()); + command.c_str()); return; } const String8 leftHashString(args[2]); size_t leftHash = 0; - int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash); + int fieldsRead = sscanf(leftHashString.c_str(), "%zx", &leftHash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - leftHashString.string()); + leftHashString.c_str()); return; } const String8 rightHashString(args[3]); size_t rightHash = 0; - fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash); + fieldsRead = sscanf(rightHashString.c_str(), "%zx", &rightHash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - rightHashString.string()); + rightHashString.c_str()); return; } @@ -252,22 +252,22 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { if (args.size() < 3) { base::StringAppendF(&result, "Expected a layer stack hash, e.g. '--planner %s <hash>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 3) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <hash>'\n", - command.string()); + command.c_str()); return; } const String8 hashString(args[2]); size_t hash = 0; - const int fieldsRead = sscanf(hashString.string(), "%zx", &hash); + const int fieldsRead = sscanf(hashString.c_str(), "%zx", &hash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - hashString.string()); + hashString.c_str()); return; } @@ -279,20 +279,20 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { } else if (command == "--similar" || command == "-s") { if (args.size() < 3) { base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 3) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <plan>'\n", - command.string()); + command.c_str()); return; } const String8 planString(args[2]); - std::optional<Plan> plan = Plan::fromString(std::string(planString.string())); + std::optional<Plan> plan = Plan::fromString(std::string(planString.c_str())); if (!plan) { - base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string()); + base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.c_str()); return; } @@ -302,7 +302,7 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { } else if (command == "--layers" || command == "-l") { mFlattener.dumpLayers(result); } else { - base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string()); + base::StringAppendF(&result, "Unknown command '%s'\n\n", command.c_str()); dumpUsage(result); } return; diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 4e7da829f2..3270e4c95c 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -1,6 +1,9 @@ +# Bug component: 1075131 + adyabr@google.com alecmouri@google.com chaviw@google.com +domlaskowski@google.com lpy@google.com pdwilliams@google.com racarr@google.com diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index f118d46e63..0492a45812 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -5710,7 +5710,7 @@ void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const auto name = String8(args[1]); mCurrentState.traverseInZOrder([&](Layer* layer) { - if (layer->getName() == name.string()) { + if (layer->getName() == name.c_str()) { layer->dumpFrameStats(result); } }); @@ -5721,7 +5721,7 @@ void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { const auto name = clearAll ? String8() : String8(args[1]); mCurrentState.traverse([&](Layer* layer) { - if (clearAll || layer->getName() == name.string()) { + if (clearAll || layer->getName() == name.c_str()) { layer->clearFrameStats(); } }); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index 1cc9ba40bd..dbf0cd8772 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -91,7 +91,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { const auto& display = getCurrentDisplayState(displayToken); EXPECT_TRUE(display.isVirtual()); EXPECT_FALSE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); + EXPECT_EQ(name.c_str(), display.displayName); // -------------------------------------------------------------------- // Cleanup conditions @@ -123,7 +123,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { const auto& display = getCurrentDisplayState(displayToken); EXPECT_TRUE(display.isVirtual()); EXPECT_TRUE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); + EXPECT_EQ(name.c_str(), display.displayName); // -------------------------------------------------------------------- // Cleanup conditions diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index f297da5511..1675584f5a 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -170,7 +170,7 @@ public: String8 err(String8::format("pixel @ (%3d, %3d): " "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", x, y, r, g, b, pixel[0], pixel[1], pixel[2])); - EXPECT_EQ(String8(), err) << err.string(); + EXPECT_EQ(String8(), err) << err.c_str(); } } diff --git a/services/vibratorservice/OWNERS b/services/vibratorservice/OWNERS index d073e2bd46..031b333fab 100644 --- a/services/vibratorservice/OWNERS +++ b/services/vibratorservice/OWNERS @@ -1 +1,3 @@ +# Bug component: 345036 + include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp index 523f890ce7..d0a9da1ff5 100644 --- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp +++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp @@ -113,7 +113,7 @@ status_t VirtualTouchpadService::dump( static_cast<long>(client_pid_)); touchpad_->dumpInternal(result); } - write(fd, result.string(), result.size()); + write(fd, result.c_str(), result.size()); return OK; } diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp index a14fed222a..d059f8fe4c 100644 --- a/vulkan/libvulkan/layers_extensions.cpp +++ b/vulkan/libvulkan/layers_extensions.cpp @@ -23,6 +23,7 @@ #include <dlfcn.h> #include <string.h> #include <sys/prctl.h> +#include <unistd.h> #include <mutex> #include <string> @@ -362,6 +363,7 @@ template <typename Functor> void ForEachFileInZip(const std::string& zipname, const std::string& dir_in_zip, Functor functor) { + static const size_t kPageSize = getpagesize(); int32_t err; ZipArchiveHandle zip = nullptr; if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) { @@ -389,7 +391,7 @@ void ForEachFileInZip(const std::string& zipname, // the APK. Loading still may fail for other reasons, but this at least // lets us avoid failed-to-load log messages in the typical case of // compressed and/or unaligned libraries. - if (entry.method != kCompressStored || entry.offset % PAGE_SIZE != 0) + if (entry.method != kCompressStored || entry.offset % kPageSize != 0) continue; functor(filename); } diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index c28390f26b..bffbe9d8d5 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -16,6 +16,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <aidl/android/hardware/graphics/common/PixelFormat.h> #include <android/hardware/graphics/common/1.0/types.h> #include <grallocusage/GrallocUsageConversion.h> #include <graphicsenv/GraphicsEnv.h> @@ -25,8 +26,6 @@ #include <sync/sync.h> #include <system/window.h> #include <ui/BufferQueueDefs.h> -#include <ui/DebugUtils.h> -#include <ui/PixelFormat.h> #include <utils/StrongPointer.h> #include <utils/Timers.h> #include <utils/Trace.h> @@ -37,6 +36,7 @@ #include "driver.h" +using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat; using android::hardware::graphics::common::V1_0::BufferUsage; namespace vulkan { @@ -503,27 +503,27 @@ void copy_ready_timings(Swapchain& swapchain, *count = num_copied; } -android::PixelFormat GetNativePixelFormat(VkFormat format) { - android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888; +PixelFormat GetNativePixelFormat(VkFormat format) { + PixelFormat native_format = PixelFormat::RGBA_8888; switch (format) { case VK_FORMAT_R8G8B8A8_UNORM: case VK_FORMAT_R8G8B8A8_SRGB: - native_format = android::PIXEL_FORMAT_RGBA_8888; + native_format = PixelFormat::RGBA_8888; break; case VK_FORMAT_R5G6B5_UNORM_PACK16: - native_format = android::PIXEL_FORMAT_RGB_565; + native_format = PixelFormat::RGB_565; break; case VK_FORMAT_R16G16B16A16_SFLOAT: - native_format = android::PIXEL_FORMAT_RGBA_FP16; + native_format = PixelFormat::RGBA_FP16; break; case VK_FORMAT_A2B10G10R10_UNORM_PACK32: - native_format = android::PIXEL_FORMAT_RGBA_1010102; + native_format = PixelFormat::RGBA_1010102; break; case VK_FORMAT_R8_UNORM: - native_format = android::PIXEL_FORMAT_R_8; + native_format = PixelFormat::R_8; break; case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: - native_format = android::PIXEL_FORMAT_RGBA_10101010; + native_format = PixelFormat::RGBA_10101010; break; default: ALOGV("unsupported swapchain format %d", format); @@ -532,7 +532,8 @@ android::PixelFormat GetNativePixelFormat(VkFormat format) { return native_format; } -android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) { +android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace, + PixelFormat pixelFormat) { switch (colorspace) { case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: return HAL_DATASPACE_V0_SRGB; @@ -551,7 +552,14 @@ android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) { case VK_COLOR_SPACE_BT709_NONLINEAR_EXT: return HAL_DATASPACE_V0_SRGB; case VK_COLOR_SPACE_BT2020_LINEAR_EXT: - return HAL_DATASPACE_BT2020_LINEAR; + if (pixelFormat == PixelFormat::RGBA_FP16) { + return static_cast<android_dataspace>( + HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_LINEAR | + HAL_DATASPACE_RANGE_EXTENDED); + } else { + return HAL_DATASPACE_BT2020_LINEAR; + } case VK_COLOR_SPACE_HDR10_ST2084_EXT: return static_cast<android_dataspace>( HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | @@ -561,9 +569,7 @@ android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) { HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL); case VK_COLOR_SPACE_HDR10_HLG_EXT: - return static_cast<android_dataspace>( - HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG | - HAL_DATASPACE_RANGE_FULL); + return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG); case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT: return static_cast<android_dataspace>( HAL_DATASPACE_STANDARD_ADOBE_RGB | @@ -1361,10 +1367,10 @@ VkResult CreateSwapchainKHR(VkDevice device, if (!allocator) allocator = &GetData(device).allocator; - android::PixelFormat native_pixel_format = + PixelFormat native_pixel_format = GetNativePixelFormat(create_info->imageFormat); android_dataspace native_dataspace = - GetNativeDataspace(create_info->imageColorSpace); + GetNativeDataspace(create_info->imageColorSpace, native_pixel_format); if (native_dataspace == HAL_DATASPACE_UNKNOWN) { ALOGE( "CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) " @@ -1462,10 +1468,11 @@ VkResult CreateSwapchainKHR(VkDevice device, const auto& dispatch = GetData(device).driver; - err = native_window_set_buffers_format(window, native_pixel_format); + err = native_window_set_buffers_format( + window, static_cast<int>(native_pixel_format)); if (err != android::OK) { ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)", - decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err); + toString(native_pixel_format).c_str(), strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } |