diff options
175 files changed, 3052 insertions, 1332 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/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..b302f52f3e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -236,6 +236,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()) { \ @@ -416,10 +426,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 +458,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 +493,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 +510,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 +656,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 +706,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 +734,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 +761,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 +779,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 +801,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 +841,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 +853,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 +869,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 +887,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 +911,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 +995,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 +1063,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 +1155,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 +1242,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 +1313,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 +1462,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 +1597,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 +1670,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 +1704,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 +1895,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 +1916,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 +2407,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 +2424,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 +2704,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 +2777,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 +2807,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 +2840,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 +2961,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 +3011,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 +3055,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 +3299,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 +3310,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 +3342,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 +3619,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 +3794,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 +3817,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) { 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/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/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..2143d93632 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -263,6 +263,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.threadnetwork.prebuilt.xml", + src: "android.hardware.threadnetwork.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.threadnetwork.xml b/data/etc/android.hardware.threadnetwork.xml new file mode 100644 index 0000000000..9cbdc905fb --- /dev/null +++ b/data/etc/android.hardware.threadnetwork.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 ThreadNetwork API --> +<permissions> + <feature name="android.hardware.threadnetwork" /> +</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..3f1fc33c90 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", @@ -285,14 +281,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 +519,6 @@ cc_library { "libbase", "libbinder", "libbinder_ndk", - "libcutils_sockets", "liblog", "libutils", ], @@ -548,6 +535,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/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/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..8d9ce0e731 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,30 @@ 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() } } } 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/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..ac968237a0 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -21,6 +21,7 @@ rust_fuzz { "waghpawan@google.com", "smoreland@google.com", ], + triage_assignee: "waghpawan@google.com", // hotlist "AIDL fuzzers bugs" on buganizer hotlists: ["4637097"], }, 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..2537ce0bcb 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 @@ -29,6 +29,7 @@ rust_fuzz { "waghpawan@google.com", "smoreland@google.com", ], + triage_assignee: "waghpawan@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..41856f9b79 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,39 @@ 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", + ], + 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 +698,7 @@ cc_benchmark { "liblog", "libutils", ], + test_suites: ["general-tests"], } cc_test_host { @@ -818,3 +801,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..17d5c8a219 --- /dev/null +++ b/libs/binder/tests/binderRecordReplayTest.cpp @@ -0,0 +1,291 @@ +/* + * 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/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 <gtest/gtest.h> + +#include <sys/prctl.h> + +#include "parcelables/SingleDataParcelable.h" + +using namespace android; +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>); +}; + +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) { + 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); + + // TODO: move logic to replay RecordedTransaction into RecordedTransaction + Parcel data; + data.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize()); + auto result = + mBpBinder->transact(transaction->getCode(), data, nullptr, transaction->getFlags()); + + // make sure recording does the thing we expect it to do + EXPECT_EQ(OK, result); + + 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/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/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 8bef33f2ca..b268c5dcd4 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -21,19 +21,36 @@ #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 = {}, }; + // 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) { @@ -43,7 +60,8 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { 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 +79,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 +96,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/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp new file mode 100644 index 0000000000..96092b10d3 --- /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: true, + fuzz_on_haiku_device: true, + }, +} + +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..46205d7689 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp @@ -0,0 +1,138 @@ +/* + * 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, +}; + +// 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(); + } + +private: + CrashType mCrash; +}; + +CrashType gCrashType = CrashType::NONE; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + if (*argc < 2) { + printf("You must specify at least one argument\n"); + exit(0); // success because this is a crash test + } + + 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 { + 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) { + 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..25906d8aeb --- /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; 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/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp index 3272bbc1aa..80661c1cbd 100644 --- a/libs/fakeservicemanager/FakeServiceManager.cpp +++ b/libs/fakeservicemanager/FakeServiceManager.cpp @@ -26,6 +26,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 +38,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 +48,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 +67,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 +118,8 @@ std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDeb } void FakeServiceManager::clear() { + std::lock_guard<std::mutex> l(mMutex); + mNameToService.clear(); } } // namespace android 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/gui/Android.bp b/libs/gui/Android.bp index bf34987b9e..342f132f0c 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -129,13 +129,24 @@ 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/IWindowInfosReportedListener.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 { @@ -147,9 +158,6 @@ filegroup { cc_library_static { name: "libgui_aidl_static", vendor_available: true, - srcs: [ - ":libgui_aidl", - ], shared_libs: [ "libbinder", @@ -175,9 +183,7 @@ cc_library_static { aidl: { export_aidl_headers: true, - include_dirs: [ - "frameworks/native/libs/gui", - ], + libs: ["libgui_aidl"], }, } @@ -247,6 +253,7 @@ cc_library_shared { shared_libs: [ "libbinder", + "libGLESv2", ], export_shared_lib_headers: [ @@ -372,7 +379,6 @@ cc_defaults { "libbase", "libcutils", "libEGL", - "libGLESv2", "libhidlbase", "liblog", "libnativewindow", diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 43ff54f3c8..b872541fec 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -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; diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 05b55337ab..826a418aab 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,12 +1,7 @@ -adyabr@google.com -alecmouri@google.com -chaviw@google.com 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/tests/OWNERS b/libs/gui/tests/OWNERS new file mode 100644 index 0000000000..156efdb883 --- /dev/null +++ b/libs/gui/tests/OWNERS @@ -0,0 +1,3 @@ +# 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 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/nativewindow/include/android/hardware_buffer_aidl.h b/libs/nativewindow/include/android/hardware_buffer_aidl.h index 1659d54539..e269f0dddf 100644 --- a/libs/nativewindow/include/android/hardware_buffer_aidl.h +++ b/libs/nativewindow/include/android/hardware_buffer_aidl.h @@ -34,6 +34,10 @@ #include <android/hardware_buffer.h> #include <sys/cdefs.h> +#ifdef __cplusplus +#include <string> +#endif + __BEGIN_DECLS /** @@ -142,6 +146,15 @@ public: return ret; } + inline std::string toString() const { + if (!mBuffer) { + return "<HardwareBuffer: Invalid>"; + } + uint64_t id = 0; + AHardwareBuffer_getId(mBuffer, &id); + return "<HardwareBuffer " + std::to_string(id) + ">"; + } + private: HardwareBuffer(const HardwareBuffer& other) = delete; HardwareBuffer& operator=(const HardwareBuffer& other) = delete; 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/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/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 fbbb38835a..0fb79d59fc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -6190,9 +6190,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)); } @@ -6252,9 +6252,9 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token, std::optional<int32_t> 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 5539915694..baea6f8eda 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -25,11 +25,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/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/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/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index b00d1a761b..e939d51f5b 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -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/OWNERS b/services/surfaceflinger/OWNERS index 4e7da829f2..473409715f 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -1,6 +1,7 @@ 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/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/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index af873065ff..8e3971debe 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); @@ -1356,7 +1356,7 @@ 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); @@ -1457,10 +1457,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; } |