diff options
251 files changed, 9627 insertions, 2710 deletions
diff --git a/Android.bp b/Android.bp index dec6716290..615a7a8727 100644 --- a/Android.bp +++ b/Android.bp @@ -64,14 +64,20 @@ filegroup { name: "framework_native_aidl_binder", srcs: ["aidl/binder/**/*.aidl"], path: "aidl/binder", - visibility: ["//frameworks/native"], + visibility: [ + "//frameworks/native", + "//frameworks/native/libs/gui", + ], } filegroup { name: "framework_native_aidl_gui", srcs: ["aidl/gui/**/*.aidl"], path: "aidl/gui", - visibility: ["//frameworks/native"], + visibility: [ + "//frameworks/native", + "//frameworks/native/libs/gui", + ], } filegroup { @@ -82,7 +88,7 @@ filegroup { ], } -cc_library_headers{ +cc_library_headers { name: "libandroid_headers_private", export_include_dirs: ["include/private"], } diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 6d837c2cea..3480d63f9a 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,8 +1,10 @@ [Builtin Hooks] +rustfmt = true bpfmt = true clang_format = true [Builtin Hooks Options] +rustfmt = --config-path=rustfmt.toml # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc index 9186514d0a..fa7be1816a 100644 --- a/cmds/atrace/atrace_userdebug.rc +++ b/cmds/atrace/atrace_userdebug.rc @@ -18,3 +18,9 @@ on post-fs chmod 0666 /sys/kernel/tracing/events/filemap/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable + # Allow traced_probes to use the raw_syscall filters to trace only a subset + # of syscalls. + chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_enter/filter + chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/filter + chmod 0666 /sys/kernel/tracing/events/raw_syscalls/sys_exit/filter + chmod 0666 /sys/kernel/debug/tracing/events/raw_syscalls/sys_exit/filter diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a2491e503f..a60972b722 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -101,6 +101,7 @@ cc_defaults { "libhidlbase", "liblog", "libutils", + "libvintf", "libbinderdebug", "packagemanager_aidl-cpp", ], diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING index 839a2c3023..649a13ee1e 100644 --- a/cmds/dumpstate/TEST_MAPPING +++ b/cmds/dumpstate/TEST_MAPPING @@ -9,15 +9,15 @@ ] }, { - "name": "dumpstate_smoke_test" - }, - { "name": "dumpstate_test" } ], "postsubmit": [ { "name": "BugreportManagerTestCases" + }, + { + "name": "dumpstate_smoke_test" } ], "imports": [ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 2b94b71a7f..6dea91bc2b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "dumpstate" +#define ATRACE_TAG ATRACE_TAG_ALWAYS #include <dirent.h> #include <errno.h> @@ -76,6 +77,7 @@ #include <cutils/native_handle.h> #include <cutils/properties.h> #include <cutils/sockets.h> +#include <cutils/trace.h> #include <debuggerd/client.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> @@ -88,6 +90,7 @@ #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> #include <utils/StrongPointer.h> +#include <vintf/VintfObject.h> #include "DumpstateInternal.h" #include "DumpstateService.h" #include "dumpstate.h" @@ -829,7 +832,8 @@ status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, // Logging statement below is useful to time how long each entry takes, but it's too verbose. // MYLOGD("Adding zip entry %s\n", entry_name.c_str()); - int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress, + size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression; + int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), flags, get_mtime(fd, ds.now_)); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(), @@ -921,7 +925,8 @@ void Dumpstate::AddDir(const std::string& dir, bool recursive) { bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) { MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); - int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_); + size_t flags = ZipWriter::kCompress | ZipWriter::kDefaultCompression; + int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), flags, ds.now_); if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err)); @@ -1394,6 +1399,23 @@ static void DumpHals(int out_fd = STDOUT_FILENO) { } } +// Dump all of the files that make up the vendor interface. +// See the files listed in dumpFileList() for the latest list of files. +static void DumpVintf() { + const auto vintfFiles = android::vintf::details::dumpFileList(); + for (const auto vintfFile : vintfFiles) { + struct stat st; + if (stat(vintfFile.c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ds.AddDir(vintfFile, true /* recursive */); + } else { + ds.EnqueueAddZipEntryAndCleanupIfNeeded(ZIP_ROOT_DIR + vintfFile, + vintfFile); + } + } + } +} + static void DumpExternalFragmentationInfo() { struct stat st; if (stat("/proc/buddyinfo", &st) != 0) { @@ -1486,7 +1508,6 @@ static void DumpCheckins(int out_fd = STDOUT_FILENO) { dprintf(out_fd, "========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd); RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd); @@ -1619,6 +1640,8 @@ static Dumpstate::RunStatus dumpstate() { do_dmesg(); } + DumpVintf(); + RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); @@ -3076,7 +3099,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); // Zip the (now complete) .tmp file within the internal directory. + ATRACE_BEGIN("FinalizeFile"); FinalizeFile(); + ATRACE_END(); // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; @@ -3387,6 +3412,9 @@ DurationReporter::DurationReporter(const std::string& title, bool logcat_only, b duration_fd_(duration_fd) { if (!title_.empty()) { started_ = Nanotime(); + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_BEGIN(title_.c_str(), 0); + } } } @@ -3401,6 +3429,9 @@ DurationReporter::~DurationReporter() { dprintf(duration_fd_, "------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } + if (title_.find("SHOW MAP") == std::string::npos) { + ATRACE_ASYNC_END(title_.c_str(), 0); + } } } diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index c9f680b266..ac101ecb29 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -25,6 +25,7 @@ cc_defaults { "CrateManager.cpp", "InstalldNativeService.cpp", "QuotaUtils.cpp", + "SysTrace.cpp", "dexopt.cpp", "execv_helper.cpp", "globals.cpp", @@ -72,8 +73,6 @@ cc_defaults { }, }, - clang: true, - tidy: true, tidy_checks: [ "-*", @@ -81,8 +80,9 @@ cc_defaults { "cert-*", "-cert-err58-cpp", ], - tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*", + tidy_checks_as_errors: [ + "clang-analyzer-security*", + "cert-*", ], } @@ -127,7 +127,6 @@ cc_library_headers { cc_test_host { name: "run_dex2oat_test", test_suites: ["general-tests"], - clang: true, srcs: [ "run_dex2oat_test.cpp", "run_dex2oat.cpp", @@ -175,7 +174,7 @@ cc_binary { // Needs to be wherever installd is as it's execed by // installd. - required: ["migrate_legacy_obb_data.sh"], + required: ["migrate_legacy_obb_data"], } // OTA chroot tool @@ -187,7 +186,6 @@ cc_binary { "-Wall", "-Werror", ], - clang: true, srcs: [ "otapreopt_chroot.cpp", @@ -302,6 +300,6 @@ sh_binary { // Script to migrate legacy obb data. sh_binary { - name: "migrate_legacy_obb_data.sh", + name: "migrate_legacy_obb_data", src: "migrate_legacy_obb_data.sh", } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index a49f563060..faf67fd92f 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -16,8 +16,6 @@ #include "InstalldNativeService.h" -#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER - #include <errno.h> #include <fts.h> #include <inttypes.h> @@ -75,6 +73,7 @@ #include "CrateManager.h" #include "MatchExtensionGen.h" #include "QuotaUtils.h" +#include "SysTrace.h" #ifndef LOG_TAG #define LOG_TAG "installd" @@ -126,8 +125,6 @@ static std::once_flag flag; namespace { -constexpr const char* kDump = "android.permission.DUMP"; - static binder::Status ok() { return binder::Status::ok(); } @@ -151,19 +148,6 @@ static binder::Status error(uint32_t code, const std::string& msg) { return binder::Status::fromServiceSpecificError(code, String8(msg.c_str())); } -binder::Status checkPermission(const char* permission) { - pid_t pid; - uid_t uid; - - if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid), - reinterpret_cast<int32_t*>(&uid))) { - return ok(); - } else { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); - } -} - binder::Status checkUid(uid_t expectedUid) { uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid == expectedUid || uid == AID_ROOT) { @@ -230,6 +214,19 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } } +binder::Status checkArgumentFileName(const std::string& path) { + if (path.empty()) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing name"); + } + for (const char& c : path) { + if (c == '\0' || c == '\n' || c == '/') { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Name %s is malformed", path.c_str())); + } + } + return ok(); +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -266,6 +263,14 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } \ } +#define CHECK_ARGUMENT_FILE_NAME(path) \ + { \ + binder::Status status = checkArgumentFileName((path)); \ + if (!status.isOk()) { \ + return status; \ + } \ + } + #ifdef GRANULAR_LOCKS /** @@ -380,13 +385,7 @@ status_t InstalldNativeService::start() { return android::OK; } -status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) { - const binder::Status dump_permission = checkPermission(kDump); - if (!dump_permission.isOk()) { - dprintf(fd, "%s\n", dump_permission.toString8().c_str()); - return PERMISSION_DENIED; - } - +status_t InstalldNativeService::dump(int fd, const Vector<String16>& /* args */) { { std::lock_guard<std::recursive_mutex> lock(mMountsLock); dprintf(fd, "Storage mounts:\n"); @@ -1006,6 +1005,12 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_FILE_NAME(profileName); + if (!base::EndsWith(profileName, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Profile name %s does not end with .prof", + profileName.c_str())); + } LOCK_PACKAGE(); binder::Status res = ok(); @@ -1300,7 +1305,7 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri const char* uuid_ = uuid ? uuid->c_str() : nullptr; for (auto userId : get_known_users(uuid_)) { LOCK_USER(); - ATRACE_BEGIN("fixup user"); + atrace_pm_begin("fixup user"); FTS* fts; FTSENT* p; auto ce_path = create_data_user_ce_path(uuid_, userId); @@ -1390,7 +1395,7 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri } } fts_close(fts); - ATRACE_END(); + atrace_pm_end(); } return ok(); } @@ -1869,8 +1874,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s binder::Status res = ok(); if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_de dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } auto sdk_sandbox_de_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId); @@ -1890,8 +1896,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the user_ce dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } auto sdk_sandbox_ce_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId); @@ -1899,8 +1906,9 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s res = error("Failed to delete " + sdk_sandbox_ce_path); } path = findDataMediaPath(uuid, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { - res = error("Failed to delete " + path); + // Contents only, as vold is responsible for the media dir itself. + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); } } return res; @@ -1925,7 +1933,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> return error("Failed to determine free space for " + data_path); } - int64_t cleared = 0; int64_t needed = targetFreeBytes - free; if (!defy_target) { LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested " @@ -1941,7 +1948,7 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> // files from the UIDs which are most over their allocated quota // 1. Create trackers for every known UID - ATRACE_BEGIN("create"); + atrace_pm_begin("create"); const auto users = get_known_users(uuid_); #ifdef GRANULAR_LOCKS std::vector<UserLock> userLocks; @@ -2022,11 +2029,10 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } fts_close(fts); } - ATRACE_END(); + atrace_pm_end(); // 2. Populate tracker stats and insert into priority queue - ATRACE_BEGIN("populate"); - int64_t cacheTotal = 0; + atrace_pm_begin("populate"); auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) { return (left->getCacheRatio() < right->getCacheRatio()); }; @@ -2035,13 +2041,12 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> for (const auto& it : trackers) { it.second->loadStats(); queue.push(it.second); - cacheTotal += it.second->cacheUsed; } - ATRACE_END(); + atrace_pm_end(); // 3. Bounce across the queue, freeing items from whichever tracker is // the most over their assigned quota - ATRACE_BEGIN("bounce"); + atrace_pm_begin("bounce"); std::shared_ptr<CacheTracker> active; while (active || !queue.empty()) { // Only look at apps under quota when explicitly requested @@ -2081,7 +2086,6 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } active->cacheUsed -= item->size; needed -= item->size; - cleared += item->size; } if (!defy_target) { @@ -2098,7 +2102,7 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> } } } - ATRACE_END(); + atrace_pm_end(); } else { return error("Legacy cache logic no longer supported"); @@ -2443,84 +2447,84 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string flags &= ~FLAG_USE_QUOTA; } - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); for (const auto& packageName : packageNames) { auto obbCodePath = create_data_media_package_path(uuid_, userId, "obb", packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } - ATRACE_END(); + atrace_pm_end(); // Calculating the app size of the external storage owning app in a manual way, since // calculating it through quota apis also includes external media storage in the app storage // numbers if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, multiuser_get_shared_gid(0, appId)); } - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); collectQuotaStats(uuidString, userId, appId, &stats, &extStats); - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize); } - ATRACE_END(); + atrace_pm_end(); for (size_t i = 0; i < packageNames.size(); i++) { const char* pkgname = packageNames[i].c_str(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]); collectManualStats(cePath, &stats); auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname); collectManualStats(dePath, &stats); - ATRACE_END(); + atrace_pm_end(); // In case of sdk sandbox storage (e.g. /data/misc_ce/0/sdksandbox/<package-name>), // collect individual stats of each subdirectory (shared, storage of each sdk etc.) if (appId >= AID_APP_START && appId <= AID_APP_END) { - ATRACE_BEGIN("sdksandbox"); + atrace_pm_begin("sdksandbox"); auto sdkSandboxCePath = create_data_misc_sdk_sandbox_package_path(uuid_, true, userId, pkgname); collectManualStatsForSubDirectories(sdkSandboxCePath, &stats); auto sdkSandboxDePath = create_data_misc_sdk_sandbox_package_path(uuid_, false, userId, pkgname); collectManualStatsForSubDirectories(sdkSandboxDePath, &stats); - ATRACE_END(); + atrace_pm_end(); } if (!uuid) { - ATRACE_BEGIN("profiles"); + atrace_pm_begin("profiles"); calculate_tree_size( create_primary_current_profile_package_dir_path(userId, pkgname), &stats.dataSize); calculate_tree_size( create_primary_reference_profile_package_dir_path(pkgname), &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + atrace_pm_begin("external"); auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname); collectManualStats(extPath, &extStats); auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname); calculate_tree_size(mediaPath, &extStats.dataSize); - ATRACE_END(); + atrace_pm_end(); } if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); int32_t sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, sharedGid, -1); } - ATRACE_END(); + atrace_pm_end(); } } @@ -2666,41 +2670,41 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin } if (flags & FLAG_USE_QUOTA) { - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats, true); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats, true); - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("profile"); + atrace_pm_begin("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + atrace_pm_begin("external"); auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); extStats.dataSize += sizes.totalSize; extStats.codeSize += sizes.obbSize; - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, -1, -1, true); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize, -1, -1, true); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); int64_t dataSize = extStats.dataSize; for (auto appId : appIds) { if (appId >= AID_APP_START) { @@ -2712,54 +2716,54 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin } } extStats.dataSize = dataSize; - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); auto obbPath = create_data_path(uuid_) + "/media/obb"; calculate_tree_size(obbPath, &extStats.codeSize); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("code"); + atrace_pm_begin("code"); calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("data"); + atrace_pm_begin("data"); auto cePath = create_data_user_ce_path(uuid_, userId); collectManualStatsForUser(cePath, &stats); auto dePath = create_data_user_de_path(uuid_, userId); collectManualStatsForUser(dePath, &stats); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("sdksandbox"); + atrace_pm_begin("sdksandbox"); auto sdkSandboxCePath = create_data_misc_sdk_sandbox_path(uuid_, true, userId); collectManualStatsForUser(sdkSandboxCePath, &stats, false, true); auto sdkSandboxDePath = create_data_misc_sdk_sandbox_path(uuid_, false, userId); collectManualStatsForUser(sdkSandboxDePath, &stats, false, true); - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("profile"); + atrace_pm_begin("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); calculate_tree_size(userProfilePath, &stats.dataSize); auto refProfilePath = create_primary_ref_profile_dir_path(); calculate_tree_size(refProfilePath, &stats.codeSize); - ATRACE_END(); + atrace_pm_end(); } - ATRACE_BEGIN("external"); + 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; #endif - ATRACE_END(); + atrace_pm_end(); if (!uuid) { - ATRACE_BEGIN("dalvik"); + atrace_pm_begin("dalvik"); calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize); calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize); - ATRACE_END(); + atrace_pm_end(); } } @@ -2807,16 +2811,16 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } if (flags & FLAG_USE_QUOTA) { - ATRACE_BEGIN("quota"); + atrace_pm_begin("quota"); auto sizes = getExternalSizesForUserWithQuota(uuidString, userId, appIds); totalSize = sizes.totalSize; audioSize = sizes.audioSize; videoSize = sizes.videoSize; imageSize = sizes.imageSize; obbSize = sizes.obbSize; - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("apps"); + atrace_pm_begin("apps"); struct stats extStats; memset(&extStats, 0, sizeof(extStats)); for (auto appId : appIds) { @@ -2825,9 +2829,9 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } } appSize = extStats.dataSize; - ATRACE_END(); + atrace_pm_end(); } else { - ATRACE_BEGIN("manual"); + atrace_pm_begin("manual"); FTS *fts; FTSENT *p; auto path = create_data_media_path(uuid_, userId); @@ -2870,13 +2874,16 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s } } fts_close(fts); - ATRACE_END(); + atrace_pm_end(); - ATRACE_BEGIN("obb"); + atrace_pm_begin("obb"); auto obbPath = StringPrintf("%s/Android/obb", create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); - ATRACE_END(); + if (!(flags & FLAG_USE_QUOTA)) { + totalSize -= obbSize; + } + atrace_pm_end(); } std::vector<int64_t> ret; @@ -3019,7 +3026,19 @@ binder::Status InstalldNativeService::copySystemProfile(const std::string& syste int32_t packageUid, const std::string& packageName, const std::string& profileName, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(systemProfile); + if (!base::EndsWith(systemProfile, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("System profile path %s does not end with .prof", + systemProfile.c_str())); + } CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_FILE_NAME(profileName); + if (!base::EndsWith(profileName, ".prof")) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Profile name %s does not end with .prof", + profileName.c_str())); + } LOCK_PACKAGE(); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); @@ -3565,10 +3584,10 @@ binder::Status InstalldNativeService::tryMountDataMirror( return error("Failed to stat " + mirrorVolCePath); } - if (mirrorCeStat.st_ino == ceStat.st_ino) { + if (mirrorCeStat.st_ino == ceStat.st_ino && mirrorCeStat.st_dev == ceStat.st_dev) { // As it's being called by prepareUserStorage, it can be called multiple times. // Hence, we if we mount it already, we should skip it. - LOG(WARNING) << "CE dir is mounted already: " + cePath; + LOG(INFO) << "CE dir is mounted already: " + cePath; return ok(); } @@ -3668,7 +3687,7 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { ENFORCE_UID(AID_SYSTEM); // NOTE: The lint warning doesn't apply to the use of system(3) with // absolute parse and no command line arguments. - if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c) + if (system("/system/bin/migrate_legacy_obb_data") != 0) { // NOLINT(cert-env33-c) LOG(ERROR) << "Unable to migrate legacy obb data"; } diff --git a/cmds/installd/SysTrace.cpp b/cmds/installd/SysTrace.cpp new file mode 100644 index 0000000000..fa65c77a2b --- /dev/null +++ b/cmds/installd/SysTrace.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER + +#include "SysTrace.h" +#include <utils/Trace.h> + +namespace android::installd { +void atrace_pm_begin(const char* name) { + ATRACE_BEGIN(name); +} + +void atrace_pm_end() { + ATRACE_END(); +} +} /* namespace android::installd */ diff --git a/cmds/installd/SysTrace.h b/cmds/installd/SysTrace.h new file mode 100644 index 0000000000..18506a9258 --- /dev/null +++ b/cmds/installd/SysTrace.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android::installd { +void atrace_pm_begin(const char*); +void atrace_pm_end(); +} /* namespace android::installd */ diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index b3baca5c41..07f73b9029 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -11,7 +11,6 @@ package { cc_test { name: "installd_utils_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_utils_test.cpp"], cflags: [ "-Wall", @@ -36,7 +35,6 @@ cc_test { cc_test { name: "installd_cache_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_cache_test.cpp"], cflags: [ "-Wall", @@ -82,7 +80,6 @@ cc_test { cc_test { name: "installd_service_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_service_test.cpp"], cflags: [ "-Wall", @@ -130,7 +127,6 @@ cc_test { cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_dexopt_test.cpp"], cflags: [ "-Wall", @@ -177,7 +173,6 @@ cc_test { cc_test { name: "installd_otapreopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_otapreopt_test.cpp"], cflags: [ "-Wall", @@ -198,7 +193,6 @@ cc_test { cc_test { name: "installd_file_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_file_test.cpp"], cflags: [ "-Wall", diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 4eb30e2542..6ef41e3258 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -920,6 +920,11 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { TEST_F(DexoptTest, DexoptDex2oat64Enabled) { LOG(INFO) << "DexoptDex2oat64Enabled"; + std::string zygote_prop = android::base::GetProperty("ro.zygote", ""); + ASSERT_GT(zygote_prop.size(), 0); + if (zygote_prop != "zygote32_64" && zygote_prop != "zygote64_32") { + GTEST_SKIP() << "DexoptDex2oat64Enabled skipped for single-bitness Zygote."; + } const std::string property = "dalvik.vm.dex2oat64.enabled"; const std::string previous_value = android::base::GetProperty(property, ""); auto restore_property = android::base::make_scope_guard([=]() { @@ -1366,6 +1371,58 @@ TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { /*has_user_id*/ true, /*expected_result*/ false); } +TEST_F(ProfileTest, ClearAppProfilesOk) { + LOG(INFO) << "ClearAppProfilesOk"; + + ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "primary.prof")); + ASSERT_BINDER_SUCCESS(service_->clearAppProfiles(package_name_, "image_editor.split.prof")); +} + +TEST_F(ProfileTest, ClearAppProfilesFailWrongProfileName) { + LOG(INFO) << "ClearAppProfilesFailWrongProfileName"; + + ASSERT_BINDER_FAIL( + service_->clearAppProfiles(package_name_, + "../../../../dalvik-cache/arm64/" + "system@app@SecureElement@SecureElement.apk@classes.vdex")); + ASSERT_BINDER_FAIL(service_->clearAppProfiles(package_name_, "image_editor.split.apk")); +} + +TEST_F(ProfileTest, CopySystemProfileOk) { + LOG(INFO) << "CopySystemProfileOk"; + + bool result; + ASSERT_BINDER_SUCCESS( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "primary.prof", &result)); +} + +TEST_F(ProfileTest, CopySystemProfileFailWrongSystemProfilePath) { + LOG(INFO) << "CopySystemProfileFailWrongSystemProfilePath"; + + bool result; + ASSERT_BINDER_FAIL(service_->copySystemProfile("../../secret.dat", kTestAppUid, package_name_, + "primary.prof", &result)); + ASSERT_BINDER_FAIL(service_->copySystemProfile("/data/user/package.name/secret.data", + kTestAppUid, package_name_, "primary.prof", + &result)); +} + +TEST_F(ProfileTest, CopySystemProfileFailWrongProfileName) { + LOG(INFO) << "CopySystemProfileFailWrongProfileName"; + + bool result; + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, + "../../../../dalvik-cache/arm64/test.vdex", &result)); + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "/test.prof", &result)); + ASSERT_BINDER_FAIL( + service_->copySystemProfile("/data/app/random.string/package.name.random/base.apk.prof", + kTestAppUid, package_name_, "base.apk", &result)); +} class BootProfileTest : public ProfileTest { public: diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 4d9b71016e..ffc082d5b2 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -523,7 +523,6 @@ int calculate_tree_size(const std::string& path, int64_t* size, */ bool is_valid_package_name(const std::string& packageName) { // This logic is borrowed from PackageParser.java - bool hasSep = false; bool front = true; auto it = packageName.begin(); @@ -539,7 +538,6 @@ bool is_valid_package_name(const std::string& packageName) { } } if (c == '.') { - hasSep = true; front = true; continue; } diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index cbfbdc9223..af85666276 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -35,5 +35,6 @@ cc_library_static { ], export_include_dirs: [ "include", - ] + ], + min_sdk_version: "30", } diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp index b7e520f2f1..711038ce63 100644 --- a/cmds/servicemanager/Access.cpp +++ b/cmds/servicemanager/Access.cpp @@ -30,6 +30,7 @@ constexpr bool kIsVendor = true; constexpr bool kIsVendor = false; #endif +#ifdef __ANDROID__ static std::string getPidcon(pid_t pid) { android_errorWriteLog(0x534e4554, "121035042"); @@ -45,7 +46,6 @@ static std::string getPidcon(pid_t pid) { static struct selabel_handle* getSehandle() { static struct selabel_handle* gSehandle = nullptr; - if (gSehandle != nullptr && selinux_status_updated()) { selabel_close(gSehandle); gSehandle = nullptr; @@ -78,8 +78,10 @@ static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t ad->tname->c_str()); return 0; } +#endif Access::Access() { +#ifdef __ANDROID__ union selinux_callback cb; cb.func_audit = auditCallback; @@ -91,6 +93,7 @@ Access::Access() { CHECK(selinux_status_open(true /*fallback*/) >= 0); CHECK(getcon(&mThisProcessContext) == 0); +#endif } Access::~Access() { @@ -98,6 +101,7 @@ Access::~Access() { } Access::CallingContext Access::getCallingContext() { +#ifdef __ANDROID__ IPCThreadState* ipc = IPCThreadState::self(); const char* callingSid = ipc->getCallingSid(); @@ -108,6 +112,9 @@ Access::CallingContext Access::getCallingContext() { .uid = ipc->getCallingUid(), .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid), }; +#else + return CallingContext(); +#endif } bool Access::canFind(const CallingContext& ctx,const std::string& name) { @@ -124,6 +131,7 @@ bool Access::canList(const CallingContext& ctx) { bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, const std::string& tname) { +#ifdef __ANDROID__ const char* tclass = "service_manager"; AuditCallbackData data = { @@ -133,9 +141,18 @@ bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const c return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, reinterpret_cast<void*>(&data)); +#else + (void)sctx; + (void)tctx; + (void)perm; + (void)tname; + + return true; +#endif } bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) { +#ifdef __ANDROID__ char *tctx = nullptr; if (selabel_lookup(getSehandle(), &tctx, name.c_str(), SELABEL_CTX_ANDROID_SERVICE) != 0) { LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n"; @@ -145,6 +162,14 @@ bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::stri bool allowed = actionAllowed(sctx, tctx, perm, name); freecon(tctx); return allowed; +#else + (void)sctx; + (void)name; + (void)perm; + (void)kIsVendor; + + return true; +#endif } } // android diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 32922ca24c..fd879c6c34 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -44,6 +44,7 @@ cc_binary { defaults: ["servicemanager_defaults"], init_rc: ["servicemanager.rc"], srcs: ["main.cpp"], + bootstrap: true, } cc_binary { @@ -86,3 +87,22 @@ cc_test { ], static_libs: ["libgmock"], } + +cc_fuzz { + name: "servicemanager_fuzzer", + defaults: [ + "servicemanager_defaults", + "service_fuzzer_defaults", + ], + host_supported: true, + srcs: ["ServiceManagerFuzzer.cpp"], + fuzz_config: { + libfuzzer_options: [ + "max_len=50000", + ], + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + }, +} diff --git a/cmds/servicemanager/ServiceManagerFuzzer.cpp b/cmds/servicemanager/ServiceManagerFuzzer.cpp new file mode 100644 index 0000000000..39f8522f84 --- /dev/null +++ b/cmds/servicemanager/ServiceManagerFuzzer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fuzzbinder/libbinder_driver.h> +#include <utils/StrongPointer.h> + +#include "Access.h" +#include "ServiceManager.h" + +using ::android::Access; +using ::android::fuzzService; +using ::android::ServiceManager; +using ::android::sp; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto accessPtr = std::make_unique<Access>(); + auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr)); + fuzzService(serviceManager, FuzzedDataProvider(data, size)); + + return 0; +} diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 2fb9c2bc9a..a831d1b74d 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -15,6 +15,7 @@ */ #include <android-base/logging.h> +#include <android-base/properties.h> #include <binder/IPCThreadState.h> #include <binder/ProcessState.h> #include <binder/Status.h> @@ -26,15 +27,14 @@ #include "ServiceManager.h" using ::android::Access; -using ::android::sp; +using ::android::IPCThreadState; using ::android::Looper; using ::android::LooperCallback; using ::android::ProcessState; -using ::android::IPCThreadState; -using ::android::ProcessState; using ::android::ServiceManager; -using ::android::os::IServiceManager; using ::android::sp; +using ::android::base::SetProperty; +using ::android::os::IServiceManager; class BinderCallback : public LooperCallback { public: @@ -121,6 +121,8 @@ int main(int argc, char** argv) { const char* driver = argc == 2 ? argv[1] : "/dev/binder"; + LOG(INFO) << "Starting sm instance on " << driver; + sp<ProcessState> ps = ProcessState::initWithDriver(driver); ps->setThreadPoolMaxThreadCount(0); ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY); @@ -138,6 +140,12 @@ int main(int argc, char** argv) { BinderCallback::setupTo(looper); ClientCallbackCallback::setupTo(looper, manager); +#ifndef VENDORSERVICEMANAGER + if (!SetProperty("servicemanager.ready", "true")) { + LOG(ERROR) << "Failed to set servicemanager ready property"; + } +#endif + while(true) { looper->pollAll(-1); } diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc index e01f132c64..c516043bb7 100644 --- a/cmds/servicemanager/servicemanager.microdroid.rc +++ b/cmds/servicemanager/servicemanager.microdroid.rc @@ -3,6 +3,7 @@ service servicemanager /system/bin/servicemanager.microdroid user system group system readproc critical + onrestart setprop servicemanager.ready false onrestart restart apexd task_profiles ServiceCapacityLow shutdown critical diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index e5d689ff91..6b35265fc8 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -3,6 +3,7 @@ service servicemanager /system/bin/servicemanager user system group system readproc critical + onrestart setprop servicemanager.ready false onrestart restart apexd onrestart restart audioserver onrestart restart gatekeeperd diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc index 067faf9c8f..b927c018df 100644 --- a/cmds/servicemanager/servicemanager.recovery.rc +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -1,4 +1,5 @@ service servicemanager /system/bin/servicemanager disabled group system readproc + onrestart setprop servicemanager.ready false seclabel u:r:servicemanager:s0 diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index e65b224a20..4746bc3c9d 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -318,6 +318,9 @@ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { OMX_VIDEO_DolbyVisionLevelUhd30 = 0x40, OMX_VIDEO_DolbyVisionLevelUhd48 = 0x80, OMX_VIDEO_DolbyVisionLevelUhd60 = 0x100, + OMX_VIDEO_DolbyVisionLevelUhd120 = 0x200, + OMX_VIDEO_DolbyVisionLevel8k30 = 0x400, + OMX_VIDEO_DolbyVisionLevel8k60 = 0x800, OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONLEVELTYPE; diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 50849506a4..1da78aa0c1 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -20,7 +20,6 @@ #include <android-base/result.h> #include <stdint.h> #include <utils/Errors.h> -#include <utils/KeyedVector.h> #include <utils/Tokenizer.h> #include <set> @@ -72,11 +71,11 @@ public: status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const; - status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; - status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; + std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const; + std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const; + std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const; - status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + std::optional<AxisInfo> mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode); @@ -100,11 +99,11 @@ private: int32_t sensorDataIndex; }; - KeyedVector<int32_t, Key> mKeysByScanCode; - KeyedVector<int32_t, Key> mKeysByUsageCode; - KeyedVector<int32_t, AxisInfo> mAxes; - KeyedVector<int32_t, Led> mLedsByScanCode; - KeyedVector<int32_t, Led> mLedsByUsageCode; + std::unordered_map<int32_t, Key> mKeysByScanCode; + std::unordered_map<int32_t, Key> mKeysByUsageCode; + std::unordered_map<int32_t, AxisInfo> mAxes; + std::unordered_map<int32_t, Led> mLedsByScanCode; + std::unordered_map<int32_t, Led> mLedsByUsageCode; std::unordered_map<int32_t, Sensor> mSensorsByAbsCode; std::set<std::string> mRequiredKernelConfigs; std::string mLoadFileName; diff --git a/include/input/OWNERS b/include/input/OWNERS new file mode 100644 index 0000000000..c88bfe97ca --- /dev/null +++ b/include/input/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/INPUT_OWNERS diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt index 7584ca3f53..f9f042ef5d 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -1,17 +1,17 @@ LIBADBD_AUTH { global: - adbd_auth_new; # apex introduced=30 - adbd_auth_delete; # apex introduced=30 - adbd_auth_run; # apex introduced=30 - adbd_auth_get_public_keys; #apex introduced=30 - adbd_auth_notify_auth; # apex introduced=30 - adbd_auth_notify_disconnect; # apex introduced=30 - adbd_auth_prompt_user; # apex introduced=30 - adbd_auth_prompt_user_with_id; # apex introduced=30 - adbd_auth_tls_device_connected; # apex introduced=30 - adbd_auth_tls_device_disconnected; # apex introduced=30 - adbd_auth_get_max_version; # apex introduced=30 - adbd_auth_supports_feature; # apex introduced=30 + adbd_auth_new; # systemapi introduced=30 + adbd_auth_delete; # systemapi introduced=30 + adbd_auth_run; # systemapi introduced=30 + adbd_auth_get_public_keys; # systemapi introduced=30 + adbd_auth_notify_auth; # systemapi introduced=30 + adbd_auth_notify_disconnect; # systemapi introduced=30 + adbd_auth_prompt_user; # systemapi introduced=30 + adbd_auth_prompt_user_with_id; # systemapi introduced=30 + adbd_auth_tls_device_connected; # systemapi introduced=30 + adbd_auth_tls_device_disconnected; # systemapi introduced=30 + adbd_auth_get_max_version; # systemapi introduced=30 + adbd_auth_supports_feature; # systemapi introduced=30 local: *; }; diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp index ea3c341fe4..2bf15d45eb 100644 --- a/libs/attestation/Android.bp +++ b/libs/attestation/Android.bp @@ -28,11 +28,9 @@ cc_library_static { "-Werror", ], srcs: [ - "HmacKeyManager.cpp" + "HmacKeyManager.cpp", ], - clang: true, - shared_libs: [ "liblog", "libcrypto", diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index d8d2cf2652..df965ab65f 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -60,100 +60,74 @@ cc_library_headers { // // Currently, these are only on system android (not vendor, not host) // TODO(b/183654927) - move these into separate libraries -libbinder_device_interface_sources = [ - "IPermissionController.cpp", - "PermissionCache.cpp", - "PermissionController.cpp", -] -cc_library { - name: "libbinder", - - version_script: "libbinder.map", - - // for vndbinder - vendor_available: true, - vndk: { - enabled: true, - }, - recovery_available: true, - double_loadable: true, - host_supported: true, - // TODO(b/153609531): remove when no longer needed. - native_bridge_supported: true, - - // TODO(b/31559095): get headers from bionic on host - include_dirs: [ - "bionic/libc/kernel/android/uapi/", - "bionic/libc/kernel/uapi/", +filegroup { + name: "libbinder_device_interface_sources", + srcs: [ + "IPermissionController.cpp", + "PermissionCache.cpp", + "PermissionController.cpp", ], +} - // libbinder does not offer a stable wire protocol. - // if a second copy of it is installed, then it may break after security - // or dessert updates. Instead, apex users should use libbinder_ndk. - apex_available: [ - "//apex_available:platform", - ], +cc_defaults { + name: "libbinder_common_defaults", + host_supported: true, srcs: [ "Binder.cpp", + "BinderRecordReplay.cpp", "BpBinder.cpp", - "BufferedTextOutput.cpp", "Debug.cpp", "FdTrigger.cpp", "IInterface.cpp", - "IMemory.cpp", - "IPCThreadState.cpp", "IResultReceiver.cpp", - "IServiceManager.cpp", - "IShellCallback.cpp", - "LazyServiceRegistrar.cpp", - "MemoryBase.cpp", - "MemoryDealer.cpp", - "MemoryHeapBase.cpp", "Parcel.cpp", - "ParcelableHolder.cpp", "ParcelFileDescriptor.cpp", - "PersistableBundle.cpp", - "ProcessState.cpp", "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", - "RpcTransportRaw.cpp", - "Static.cpp", "Stability.cpp", "Status.cpp", "TextOutput.cpp", + "Trace.cpp", "Utils.cpp", - ":libbinder_aidl", ], - target: { - android: { - srcs: libbinder_device_interface_sources, + shared_libs: [ + "libcutils", + "libutils", + ], - // NOT static to keep the wire protocol unfrozen - static: { - enabled: false, - }, - }, - vendor: { - exclude_srcs: libbinder_device_interface_sources, - }, - darwin: { - enabled: false, - }, + static_libs: [ + "libbase", + ], + + header_libs: [ + "libbinder_headers", + ], +} + +cc_defaults { + name: "libbinder_android_defaults", + + // TODO(b/31559095): get headers from bionic on host + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi/", + ], + + srcs: [ + "OS.cpp", + "RpcTransportRaw.cpp", + ], + + target: { host: { srcs: [ - "ServiceManagerHost.cpp", "UtilsHost.cpp", ], }, - recovery: { - exclude_header_libs: [ - "libandroid_runtime_vm_headers", - ], - }, }, aidl: { @@ -161,7 +135,6 @@ cc_library { }, cflags: [ - "-Wall", "-Wextra", "-Wextra-semi", "-Werror", @@ -176,22 +149,18 @@ cc_library { }, debuggable: { - cflags: ["-DBINDER_RPC_DEV_SERVERS"], + cflags: [ + "-DBINDER_RPC_DEV_SERVERS", + "-DBINDER_ENABLE_RECORDING", + ], }, }, shared_libs: [ "liblog", - "libcutils", - "libutils", - ], - - static_libs: [ - "libbase", ], header_libs: [ - "libbinder_headers", "libandroid_runtime_vm_headers", ], @@ -199,7 +168,6 @@ cc_library { "libbinder_headers", ], - clang: true, sanitize: { misc_undefined: ["integer"], }, @@ -217,6 +185,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "google-*", @@ -224,6 +193,132 @@ cc_library { "performance*", "portability*", ], +} + +cc_library_shared { + name: "libbinder_on_trusty_mock", + defaults: ["libbinder_common_defaults"], + + srcs: [ + // Trusty-specific files + "trusty/logging.cpp", + "trusty/OS.cpp", + "trusty/RpcServerTrusty.cpp", + "trusty/RpcTransportTipcTrusty.cpp", + "trusty/TrustyStatus.cpp", + "trusty/socket.cpp", + ], + + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + // Trusty libbinder uses vendor stability for its binders + "-D__ANDROID_VNDK__", + "-U__ANDROID__", + "-D__TRUSTY__", + "-DTRUSTY_USERSPACE", + // Flags from the Trusty build system + "-Werror", + "-Wsign-compare", + "-Wno-unused-function", + "-Wno-unused-label", + "-fno-common", + "-fno-omit-frame-pointer", + "-fno-threadsafe-statics", + ], + rtti: false, + + local_include_dirs: [ + "trusty/include", + "trusty/include_mock", + ], + + visibility: [ + ":__subpackages__", + ], +} + +cc_defaults { + name: "libbinder_kernel_defaults", + srcs: [ + "BufferedTextOutput.cpp", + "IPCThreadState.cpp", + "IServiceManager.cpp", + "ProcessState.cpp", + "Static.cpp", + ":libbinder_aidl", + ":libbinder_device_interface_sources", + ], + target: { + vendor: { + exclude_srcs: [ + ":libbinder_device_interface_sources", + ], + }, + host: { + srcs: [ + "ServiceManagerHost.cpp", + ], + }, + }, + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], +} + +cc_library { + name: "libbinder", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + + version_script: "libbinder.map", + + // for vndbinder + vendor_available: true, + vndk: { + enabled: true, + }, + recovery_available: true, + double_loadable: true, + // TODO(b/153609531): remove when no longer needed. + native_bridge_supported: true, + + // libbinder does not offer a stable wire protocol. + // if a second copy of it is installed, then it may break after security + // or dessert updates. Instead, apex users should use libbinder_ndk. + apex_available: [ + "//apex_available:platform", + ], + + srcs: [ + "IMemory.cpp", + "IShellCallback.cpp", + "LazyServiceRegistrar.cpp", + "MemoryBase.cpp", + "MemoryDealer.cpp", + "MemoryHeapBase.cpp", + "ParcelableHolder.cpp", + "PersistableBundle.cpp", + ], + + target: { + android: { + // NOT static to keep the wire protocol unfrozen + static: { + enabled: false, + }, + }, + darwin: { + enabled: false, + }, + recovery: { + exclude_header_libs: [ + "libandroid_runtime_vm_headers", + ], + }, + }, afdo: true, @@ -232,6 +327,46 @@ cc_library { }, } +cc_library_static { + name: "libbinder_rpc_no_kernel", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + "libbinder_kernel_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + +cc_library_static { + name: "libbinder_rpc_single_threaded_no_kernel", + defaults: [ + "libbinder_common_defaults", + "libbinder_android_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + visibility: [ + ":__subpackages__", + ], +} + cc_defaults { name: "libbinder_tls_shared_deps", shared_libs: [ @@ -272,6 +407,34 @@ cc_library_shared { defaults: ["libbinder_tls_defaults"], } +cc_library_shared { + name: "libbinder_trusty", + vendor: true, + srcs: [ + "RpcTransportTipcAndroid.cpp", + "RpcTrusty.cpp", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libtrusty", + "libutils", + ], + static_libs: [ + "libbase", + ], + export_include_dirs: ["include_trusty"], + + // Most of Android doesn't need this library and shouldn't use it, + // so we restrict its visibility to the Trusty-specific packages. + visibility: [ + ":__subpackages__", + "//system/core/trusty:__subpackages__", + "//vendor:__subpackages__", + ], +} + // For testing cc_library_static { name: "libbinder_tls_static", @@ -327,6 +490,9 @@ aidl_interface { java: { enabled: false, }, + cpp: { + gen_trace: false, + }, }, } @@ -351,6 +517,7 @@ cc_library { // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ + ":__subpackages__", "//packages/modules/Virtualization:__subpackages__", ], } @@ -370,6 +537,7 @@ filegroup { cc_library { name: "libbatterystats_aidl", + host_supported: true, srcs: [ "IBatteryStats.cpp", ], @@ -382,6 +550,7 @@ cc_library { cc_library { name: "libprocessinfoservice_aidl", + host_supported: true, srcs: [ "IProcessInfoService.cpp", "ProcessInfoService.cpp", diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 39befbe8e0..481d704f1c 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <android-base/unique_fd.h> +#include <binder/BinderRecordReplay.h> #include <binder/BpBinder.h> #include <binder/IInterface.h> #include <binder/IPCThreadState.h> @@ -28,13 +29,19 @@ #include <binder/IShellCallback.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> +#include <cutils/compiler.h> #include <private/android_filesystem_config.h> +#include <pthread.h> #include <utils/misc.h> #include <inttypes.h> -#include <linux/sched.h> #include <stdio.h> +#ifdef __linux__ +#include <linux/sched.h> +#endif + +#include "BuildFlags.h" #include "RpcState.h" namespace android { @@ -49,10 +56,17 @@ static_assert(sizeof(IBinder) == 12); static_assert(sizeof(BBinder) == 20); #endif +// global b/c b/230079120 - consistent symbol table #ifdef BINDER_RPC_DEV_SERVERS -constexpr const bool kEnableRpcDevServers = true; +bool kEnableRpcDevServers = true; #else -constexpr const bool kEnableRpcDevServers = false; +bool kEnableRpcDevServers = false; +#endif + +#ifdef BINDER_ENABLE_RECORDING +bool kEnableRecording = true; +#else +bool kEnableRecording = false; #endif // Log any reply transactions for which the data exceeds this size @@ -156,10 +170,14 @@ status_t IBinder::getDebugPid(pid_t* out) { status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd, const sp<IBinder>& keepAliveBinder) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("setRpcClientDebug disallowed because RPC is not enabled"); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } BBinder* local = this->localBinder(); if (local != nullptr) { @@ -193,6 +211,17 @@ void IBinder::withLock(const std::function<void()>& doWithLock) { proxy->withLock(doWithLock); } +sp<IBinder> IBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + BBinder* local = localBinder(); + if (local) { + return local->lookupOrCreateWeak(objectID, make, makeArgs); + } + BpBinder* proxy = this->remoteBinder(); + LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote"); + return proxy->lookupOrCreateWeak(objectID, make, makeArgs); +} + // --------------------------------------------------------------------------- class BBinder::RpcServerLink : public IBinder::DeathRecipient { @@ -201,6 +230,7 @@ public: RpcServerLink(const sp<RpcServer>& rpcServer, const sp<IBinder>& keepAliveBinder, const wp<BBinder>& binder) : mRpcServer(rpcServer), mKeepAliveBinder(keepAliveBinder), mBinder(binder) {} + virtual ~RpcServerLink(); void binderDied(const wp<IBinder>&) override { LOG_RPC_DETAIL("RpcServerLink: binder died, shutting down RpcServer"); if (mRpcServer == nullptr) { @@ -226,26 +256,31 @@ private: sp<IBinder> mKeepAliveBinder; // hold to avoid automatically unlinking wp<BBinder> mBinder; }; +BBinder::RpcServerLink::~RpcServerLink() {} class BBinder::Extras { public: // unlocked objects - bool mRequestingSid = false; - bool mInheritRt = false; sp<IBinder> mExtension; +#ifdef __linux__ int mPolicy = SCHED_NORMAL; int mPriority = 0; +#endif + bool mRequestingSid = false; + bool mInheritRt = false; // for below objects Mutex mLock; std::set<sp<RpcServerLink>> mRpcServerLinks; BpBinder::ObjectManager mObjects; + + android::base::unique_fd mRecordingFd; }; // --------------------------------------------------------------------------- -BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false) {} +BBinder::BBinder() : mExtras(nullptr), mStability(0), mParceled(false), mRecordingOn(false) {} bool BBinder::isBinderAlive() const { @@ -257,13 +292,68 @@ status_t BBinder::pingBinder() return NO_ERROR; } +status_t BBinder::startRecordingTransactions(const Parcel& data) { + if (!kEnableRecording) { + ALOGW("Binder recording disallowed because recording is not enabled"); + return INVALID_OPERATION; + } + if (!kEnableKernelIpc) { + ALOGW("Binder recording disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_ROOT) { + ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); + return PERMISSION_DENIED; + } + Extras* e = getOrCreateExtras(); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + LOG(INFO) << "Could not start Binder recording. Another is already in progress."; + return INVALID_OPERATION; + } else { + status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd)); + if (readStatus != OK) { + return readStatus; + } + mRecordingOn = true; + LOG(INFO) << "Started Binder recording."; + return NO_ERROR; + } +} + +status_t BBinder::stopRecordingTransactions() { + if (!kEnableRecording) { + ALOGW("Binder recording disallowed because recording is not enabled"); + return INVALID_OPERATION; + } + if (!kEnableKernelIpc) { + ALOGW("Binder recording disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } + uid_t uid = IPCThreadState::self()->getCallingUid(); + if (uid != AID_ROOT) { + ALOGE("Binder recording not allowed because client %" PRIu32 " is not root", uid); + return PERMISSION_DENIED; + } + Extras* e = getOrCreateExtras(); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + e->mRecordingFd.reset(); + mRecordingOn = false; + LOG(INFO) << "Stopped Binder recording."; + return NO_ERROR; + } else { + LOG(INFO) << "Could not stop Binder recording. One is not in progress."; + return INVALID_OPERATION; + } +} + const String16& BBinder::getInterfaceDescriptor() const { - // This is a local static rather than a global static, - // to avoid static initializer ordering issues. - static String16 sEmptyDescriptor; - ALOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return sEmptyDescriptor; + static StaticString16 sBBinder(u"BBinder"); + ALOGW("Reached BBinder::getInterfaceDescriptor (this=%p). Override?", this); + return sBBinder; } // NOLINTNEXTLINE(google-default-arguments) @@ -281,6 +371,12 @@ status_t BBinder::transact( case PING_TRANSACTION: err = pingBinder(); break; + case START_RECORDING_TRANSACTION: + err = startRecordingTransactions(data); + break; + case STOP_RECORDING_TRANSACTION: + err = stopRecordingTransactions(); + break; case EXTENSION_TRANSACTION: CHECK(reply != nullptr); err = reply->writeStrongBinder(getExtension()); @@ -307,6 +403,26 @@ status_t BBinder::transact( } } + if (CC_UNLIKELY(kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION)) { + Extras* e = mExtras.load(std::memory_order_acquire); + AutoMutex lock(e->mLock); + if (mRecordingOn) { + Parcel emptyReply; + auto transaction = + android::binder::debug::RecordedTransaction::fromDetails(code, flags, data, + reply ? *reply + : emptyReply, + err); + if (transaction) { + if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) { + LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err; + } + } else { + LOG(INFO) << "Failed to create RecordedTransaction object."; + } + } + } + return err; } @@ -365,6 +481,14 @@ void BBinder::withLock(const std::function<void()>& doWithLock) { doWithLock(); } +sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + Extras* e = getOrCreateExtras(); + LOG_ALWAYS_FATAL_IF(!e, "no memory"); + AutoMutex _l(e->mLock); + return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); +} + BBinder* BBinder::localBinder() { return this; @@ -404,6 +528,7 @@ sp<IBinder> BBinder::getExtension() { return e->mExtension; } +#ifdef __linux__ void BBinder::setMinSchedulerPolicy(int policy, int priority) { LOG_ALWAYS_FATAL_IF(mParceled, "setMinSchedulerPolicy() should not be called after a binder object " @@ -448,6 +573,7 @@ int BBinder::getMinSchedulerPriority() { if (e == nullptr) return 0; return e->mPriority; } +#endif // __linux__ bool BBinder::isInheritRt() { Extras* e = mExtras.load(std::memory_order_acquire); @@ -475,7 +601,12 @@ void BBinder::setInheritRt(bool inheritRt) { } pid_t BBinder::getDebugPid() { +#ifdef __linux__ return getpid(); +#else + // TODO: handle other OSes + return 0; +#endif // __linux__ } void BBinder::setExtension(const sp<IBinder>& extension) { @@ -496,10 +627,14 @@ void BBinder::setParceled() { } status_t BBinder::setRpcClientDebug(const Parcel& data) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid != AID_ROOT) { ALOGE("%s: not allowed because client %" PRIu32 " is not root", __PRETTY_FUNCTION__, uid); @@ -521,10 +656,14 @@ status_t BBinder::setRpcClientDebug(const Parcel& data) { status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, const sp<IBinder>& keepAliveBinder) { - if constexpr (!kEnableRpcDevServers) { + if (!kEnableRpcDevServers) { ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__); return INVALID_OPERATION; } + if (!kEnableKernelIpc) { + ALOGW("setRpcClientDebug disallowed because kernel binder is not enabled"); + return INVALID_OPERATION; + } const int socketFdForPrint = socketFd.get(); LOG_RPC_DETAIL("%s(fd=%d)", __PRETTY_FUNCTION__, socketFdForPrint); @@ -539,7 +678,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, return UNEXPECTED_NULL; } - size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxThreadCount(); + size_t binderThreadPoolMaxCount = ProcessState::self()->getThreadPoolMaxTotalThreadCount(); if (binderThreadPoolMaxCount <= 1) { ALOGE("%s: ProcessState thread pool max count is %zu. RPC is disabled for this service " "because RPC requires the service to support multithreading.", @@ -582,8 +721,24 @@ void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) { BBinder::~BBinder() { - if (!wasParceled() && getExtension()) { - ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + if (!wasParceled()) { + if (getExtension()) { + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + } + if (isRequestingSid()) { + ALOGW("Binder %p destroyed when requesting SID before being parceled.", this); + } + if (isInheritRt()) { + ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this); + } +#ifdef __linux__ + if (getMinSchedulerPolicy() != SCHED_NORMAL) { + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + } + if (getMinSchedulerPriority() != 0) { + ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this); + } +#endif // __linux__ } Extras* e = mExtras.load(std::memory_order_relaxed); @@ -620,13 +775,14 @@ status_t BBinder::onTransact( for (int i = 0; i < argc && data.dataAvail() > 0; i++) { args.add(data.readString16()); } - sp<IShellCallback> shellCallback = IShellCallback::asInterface( - data.readStrongBinder()); + sp<IBinder> shellCallbackBinder = data.readStrongBinder(); sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface( data.readStrongBinder()); // XXX can't add virtuals until binaries are updated. - //return shellCommand(in, out, err, args, resultReceiver); + // sp<IShellCallback> shellCallback = IShellCallback::asInterface( + // shellCallbackBinder); + // return shellCommand(in, out, err, args, resultReceiver); (void)in; (void)out; (void)err; diff --git a/libs/binder/BinderRecordReplay.cpp b/libs/binder/BinderRecordReplay.cpp new file mode 100644 index 0000000000..90c02a80b9 --- /dev/null +++ b/libs/binder/BinderRecordReplay.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <binder/BinderRecordReplay.h> +#include <algorithm> + +using android::Parcel; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +#define PADDING8(s) ((8 - (s) % 8) % 8) + +static_assert(PADDING8(0) == 0); +static_assert(PADDING8(1) == 7); +static_assert(PADDING8(7) == 1); +static_assert(PADDING8(8) == 0); + +// Transactions are sequentially recorded to the file descriptor in the following format: +// +// RecordedTransaction.TransactionHeader (32 bytes) +// Sent Parcel data (getDataSize() bytes) +// padding (enough bytes to align the reply Parcel data to 8 bytes) +// Reply Parcel data (getReplySize() bytes) +// padding (enough bytes to align the next header to 8 bytes) +// [repeats with next transaction] +// +// Warning: This format is non-stable + +RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { + mHeader = {t.getCode(), t.getFlags(), t.getDataSize(), + t.getReplySize(), t.getReturnedStatus(), t.getVersion()}; + mSent.setData(t.getDataParcel().data(), t.getDataSize()); + mReply.setData(t.getReplyParcel().data(), t.getReplySize()); +} + +std::optional<RecordedTransaction> RecordedTransaction::fromDetails(uint32_t code, uint32_t flags, + const Parcel& dataParcel, + const Parcel& replyParcel, + status_t err) { + RecordedTransaction t; + t.mHeader = {code, + flags, + static_cast<uint64_t>(dataParcel.dataSize()), + static_cast<uint64_t>(replyParcel.dataSize()), + static_cast<int32_t>(err), + dataParcel.isForRpc() ? static_cast<uint32_t>(1) : static_cast<uint32_t>(0)}; + + if (t.mSent.setData(dataParcel.data(), t.getDataSize()) != android::NO_ERROR) { + LOG(INFO) << "Failed to set sent parcel data."; + return std::nullopt; + } + + if (t.mReply.setData(replyParcel.data(), t.getReplySize()) != android::NO_ERROR) { + LOG(INFO) << "Failed to set reply parcel data."; + return std::nullopt; + } + + return std::optional<RecordedTransaction>(std::move(t)); +} + +std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd& fd) { + RecordedTransaction t; + if (!android::base::ReadFully(fd, &t.mHeader, sizeof(mHeader))) { + LOG(INFO) << "Failed to read transactionHeader from fd " << fd.get(); + return std::nullopt; + } + if (t.getVersion() != 0) { + LOG(INFO) << "File corrupted: transaction version is not 0."; + return std::nullopt; + } + + std::vector<uint8_t> bytes; + bytes.resize(t.getDataSize()); + if (!android::base::ReadFully(fd, bytes.data(), t.getDataSize())) { + LOG(INFO) << "Failed to read sent parcel data from fd " << fd.get(); + return std::nullopt; + } + if (t.mSent.setData(bytes.data(), t.getDataSize()) != android::NO_ERROR) { + LOG(INFO) << "Failed to set sent parcel data."; + return std::nullopt; + } + + uint8_t padding[7]; + if (!android::base::ReadFully(fd, padding, PADDING8(t.getDataSize()))) { + LOG(INFO) << "Failed to read sent parcel padding from fd " << fd.get(); + return std::nullopt; + } + if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { + LOG(INFO) << "File corrupted: padding isn't 0."; + return std::nullopt; + } + + bytes.resize(t.getReplySize()); + if (!android::base::ReadFully(fd, bytes.data(), t.getReplySize())) { + LOG(INFO) << "Failed to read reply parcel data from fd " << fd.get(); + return std::nullopt; + } + if (t.mReply.setData(bytes.data(), t.getReplySize()) != android::NO_ERROR) { + LOG(INFO) << "Failed to set reply parcel data."; + return std::nullopt; + } + + if (!android::base::ReadFully(fd, padding, PADDING8(t.getReplySize()))) { + LOG(INFO) << "Failed to read parcel padding from fd " << fd.get(); + return std::nullopt; + } + if (std::any_of(padding, padding + 7, [](uint8_t i) { return i != 0; })) { + LOG(INFO) << "File corrupted: padding isn't 0."; + return std::nullopt; + } + + return std::optional<RecordedTransaction>(std::move(t)); +} + +android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { + if (!android::base::WriteFully(fd, &mHeader, sizeof(mHeader))) { + LOG(INFO) << "Failed to write transactionHeader to fd " << fd.get(); + return UNKNOWN_ERROR; + } + if (!android::base::WriteFully(fd, mSent.data(), getDataSize())) { + LOG(INFO) << "Failed to write sent parcel data to fd " << fd.get(); + return UNKNOWN_ERROR; + } + const uint8_t zeros[7] = {0}; + if (!android::base::WriteFully(fd, zeros, PADDING8(getDataSize()))) { + LOG(INFO) << "Failed to write sent parcel padding to fd " << fd.get(); + return UNKNOWN_ERROR; + } + if (!android::base::WriteFully(fd, mReply.data(), getReplySize())) { + LOG(INFO) << "Failed to write reply parcel data to fd " << fd.get(); + return UNKNOWN_ERROR; + } + if (!android::base::WriteFully(fd, zeros, PADDING8(getReplySize()))) { + LOG(INFO) << "Failed to write reply parcel padding to fd " << fd.get(); + return UNKNOWN_ERROR; + } + return NO_ERROR; +} + +uint32_t RecordedTransaction::getCode() const { + return mHeader.code; +} + +uint32_t RecordedTransaction::getFlags() const { + return mHeader.flags; +} + +uint64_t RecordedTransaction::getDataSize() const { + return mHeader.dataSize; +} + +uint64_t RecordedTransaction::getReplySize() const { + return mHeader.replySize; +} + +int32_t RecordedTransaction::getReturnedStatus() const { + return mHeader.statusReturned; +} + +uint32_t RecordedTransaction::getVersion() const { + return mHeader.version; +} + +const Parcel& RecordedTransaction::getDataParcel() const { + return mSent; +} + +const Parcel& RecordedTransaction::getReplyParcel() const { + return mReply; +} diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 921e57c7bf..54d24457d4 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -28,6 +28,10 @@ #include <stdio.h> +#include "BuildFlags.h" + +#include <android-base/file.h> + //#undef ALOGV //#define ALOGV(...) fprintf(stderr, __VA_ARGS__) @@ -98,6 +102,36 @@ void* BpBinder::ObjectManager::detach(const void* objectID) { return value; } +namespace { +struct Tag { + wp<IBinder> binder; +}; +} // namespace + +static void cleanWeak(const void* /* id */, void* obj, void* /* cookie */) { + delete static_cast<Tag*>(obj); +} + +sp<IBinder> BpBinder::ObjectManager::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + entry_t& e = mObjects[objectID]; + if (e.object != nullptr) { + if (auto attached = static_cast<Tag*>(e.object)->binder.promote()) { + return attached; + } + } else { + e.object = new Tag; + LOG_ALWAYS_FATAL_IF(!e.object, "no more memory"); + } + sp<IBinder> newObj = make(makeArgs); + + static_cast<Tag*>(e.object)->binder = newObj; + e.cleanupCookie = nullptr; + e.func = cleanWeak; + + return newObj; +} + void BpBinder::ObjectManager::kill() { const size_t N = mObjects.size(); @@ -115,6 +149,11 @@ void BpBinder::ObjectManager::kill() // --------------------------------------------------------------------------- sp<BpBinder> BpBinder::create(int32_t handle) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return nullptr; + } + int32_t trackedUid = -1; if (sCountByUidEnabled) { trackedUid = IPCThreadState::self()->getCallingUid(); @@ -177,6 +216,11 @@ BpBinder::BpBinder(Handle&& handle) } BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle(handle)) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + mTrackedUid = trackedUid; ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle()); @@ -257,6 +301,18 @@ status_t BpBinder::pingBinder() return transact(PING_TRANSACTION, data, &reply); } +status_t BpBinder::startRecordingBinder(const android::base::unique_fd& fd) { + Parcel send, reply; + send.writeUniqueFileDescriptor(fd); + return transact(START_RECORDING_TRANSACTION, send, &reply); +} + +status_t BpBinder::stopRecordingBinder() { + Parcel data, reply; + data.markForBinder(sp<BpBinder>::fromExisting(this)); + return transact(STOP_RECORDING_TRANSACTION, data, &reply); +} + status_t BpBinder::dump(int fd, const Vector<String16>& args) { Parcel send; @@ -279,7 +335,7 @@ status_t BpBinder::transact( if (mAlive) { bool privateVendor = flags & FLAG_PRIVATE_VENDOR; // don't send userspace flags to the kernel - flags = flags & ~FLAG_PRIVATE_VENDOR; + flags = flags & ~static_cast<uint32_t>(FLAG_PRIVATE_VENDOR); // user transactions require a given stability level if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) { @@ -303,6 +359,11 @@ status_t BpBinder::transact( status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply, flags); } else { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } + status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { @@ -326,7 +387,24 @@ status_t BpBinder::transact( status_t BpBinder::linkToDeath( const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (isRpcBinder()) { + if (rpcSession()->getMaxIncomingThreads() < 1) { + LOG_ALWAYS_FATAL("Cannot register a DeathRecipient without any incoming connections."); + return INVALID_OPERATION; + } + } else if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } else { + if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) { + ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming " + "transactions. See ProcessState::startThreadPool and " + "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the " + "binder " + "threadpool before other initialization steps.", + String8(getInterfaceDescriptor()).c_str()); + } + } Obituary ob; ob.recipient = recipient; @@ -346,10 +424,14 @@ status_t BpBinder::linkToDeath( return NO_MEMORY; } ALOGV("Requesting death notification: %p handle %d\n", this, binderHandle()); - getWeakRefs()->incWeak(this); - IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } } ssize_t res = mObituaries->add(ob); return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; @@ -364,7 +446,10 @@ status_t BpBinder::unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, wp<DeathRecipient>* outRecipient) { - if (isRpcBinder()) return UNKNOWN_TRANSACTION; + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; + } AutoMutex _l(mLock); @@ -384,9 +469,13 @@ status_t BpBinder::unlinkToDeath( mObituaries->removeAt(i); if (mObituaries->size() == 0) { ALOGV("Clearing death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } delete mObituaries; mObituaries = nullptr; } @@ -399,7 +488,10 @@ status_t BpBinder::unlinkToDeath( void BpBinder::sendObituary() { - LOG_ALWAYS_FATAL_IF(isRpcBinder(), "Cannot send obituary for remote binder."); + if (!kEnableKernelIpc && !isRpcBinder()) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", this, binderHandle(), mObitsSent ? "true" : "false"); @@ -411,9 +503,13 @@ void BpBinder::sendObituary() Vector<Obituary>* obits = mObituaries; if(obits != nullptr) { ALOGV("Clearing sent death notification: %p handle %d\n", this, binderHandle()); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(binderHandle(), this); - self->flushCommands(); + if (!isRpcBinder()) { + if constexpr (kEnableKernelIpc) { + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(binderHandle(), this); + self->flushCommands(); + } + } mObituaries = nullptr; } mObitsSent = 1; @@ -464,17 +560,27 @@ void BpBinder::withLock(const std::function<void()>& doWithLock) { doWithLock(); } +sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs) { + AutoMutex _l(mLock); + return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); +} + BpBinder* BpBinder::remoteBinder() { return this; } -BpBinder::~BpBinder() -{ - ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); - +BpBinder::~BpBinder() { if (CC_UNLIKELY(isRpcBinder())) return; + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("Destroying BpBinder %p handle %d\n", this, binderHandle()); + IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { @@ -505,21 +611,31 @@ BpBinder::~BpBinder() } } -void BpBinder::onFirstRef() -{ - ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onFirstRef() { if (CC_UNLIKELY(isRpcBinder())) return; + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onFirstRef BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->incStrongHandle(binderHandle(), this); } -void BpBinder::onLastStrongRef(const void* /*id*/) -{ - ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); +void BpBinder::onLastStrongRef(const void* /*id*/) { if (CC_UNLIKELY(isRpcBinder())) { (void)rpcSession()->sendDecStrong(this); return; } + + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return; + } + + ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); IF_ALOGV() { printRefs(); } @@ -552,6 +668,11 @@ bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) // RPC binder doesn't currently support inc from weak binders if (CC_UNLIKELY(isRpcBinder())) return false; + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return false; + } + ALOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, binderHandle()); IPCThreadState* ipc = IPCThreadState::self(); return ipc ? ipc->attemptIncStrongHandle(binderHandle()) == NO_ERROR : false; diff --git a/libs/binder/BuildFlags.h b/libs/binder/BuildFlags.h new file mode 100644 index 0000000000..3e9d1c2b4a --- /dev/null +++ b/libs/binder/BuildFlags.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +constexpr bool kEnableRpcThreads = false; +#else +constexpr bool kEnableRpcThreads = true; +#endif + +#ifdef BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = true; +#else // BINDER_WITH_KERNEL_IPC +constexpr bool kEnableKernelIpc = false; +#endif // BINDER_WITH_KERNEL_IPC + +} // namespace android diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index e4ac4b49a4..c6e4fb378d 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -15,6 +15,7 @@ */ #include "Debug.h" +#include "BuildFlags.h" #include <binder/ProcessState.h> @@ -301,6 +302,11 @@ void printHexData(int32_t indent, const void *buf, size_t length, } ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { + if constexpr (!kEnableKernelIpc) { + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return 0; + } + sp<ProcessState> proc = ProcessState::selfOrNull(); if (proc.get() == nullptr) { return 0; diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 5e22593f69..8ee6cb0615 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -22,39 +22,70 @@ #include <poll.h> #include <android-base/macros.h> +#include <android-base/scopeguard.h> #include "RpcState.h" namespace android { std::unique_ptr<FdTrigger> FdTrigger::make() { auto ret = std::make_unique<FdTrigger>(); +#ifndef BINDER_RPC_SINGLE_THREADED if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) { ALOGE("Could not create pipe %s", strerror(errno)); return nullptr; } +#endif return ret; } void FdTrigger::trigger() { +#ifdef BINDER_RPC_SINGLE_THREADED + mTriggered = true; +#else mWrite.reset(); +#endif } bool FdTrigger::isTriggered() { +#ifdef BINDER_RPC_SINGLE_THREADED + return mTriggered; +#else return mWrite == -1; +#endif } -status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { - LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", fd.get()); - pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, - {.fd = mRead.get(), .events = 0, .revents = 0}}; +status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, int16_t event) { +#ifdef BINDER_RPC_SINGLE_THREADED + if (mTriggered) { + return DEAD_OBJECT; + } +#endif + + LOG_ALWAYS_FATAL_IF(event == 0, "triggerablePoll %d with event 0 is not allowed", + transportFd.fd.get()); + pollfd pfd[]{ + {.fd = transportFd.fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, +#ifndef BINDER_RPC_SINGLE_THREADED + {.fd = mRead.get(), .events = 0, .revents = 0}, +#endif + }; + + LOG_ALWAYS_FATAL_IF(transportFd.isInPollingState() == true, + "Only one thread should be polling on Fd!"); + + transportFd.setPollingState(true); + auto pollingStateGuard = + android::base::make_scope_guard([&]() { transportFd.setPollingState(false); }); + int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); if (ret < 0) { return -errno; } - LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", fd.get()); + LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", transportFd.fd.get()); // At least one FD has events. Check them. +#ifndef BINDER_RPC_SINGLE_THREADED // Detect explicit trigger(): DEAD_OBJECT if (pfd[1].revents & POLLHUP) { return DEAD_OBJECT; @@ -68,6 +99,7 @@ status_t FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { // pfd[1].revents is 0, hence pfd[0].revents must be set, and only possible values are // a subset of event | POLLHUP | POLLERR | POLLNVAL. +#endif // POLLNVAL: invalid FD number, e.g. not opened. if (pfd[0].revents & POLLNVAL) { diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index a545d6cbea..5fbf2908ad 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once #include <memory> @@ -20,6 +21,8 @@ #include <android-base/unique_fd.h> #include <utils/Errors.h> +#include <binder/RpcTransport.h> + namespace android { /** This is not a pipe. */ @@ -52,10 +55,15 @@ public: * true - time to read! * false - trigger happened */ - [[nodiscard]] status_t triggerablePoll(base::borrowed_fd fd, int16_t event); + [[nodiscard]] status_t triggerablePoll(const android::RpcTransportFd& transportFd, + int16_t event); private: +#ifdef BINDER_RPC_SINGLE_THREADED + bool mTriggered = false; +#else base::unique_fd mWrite; base::unique_fd mRead; +#endif }; } // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 3c97dcab93..bfcf39ad30 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -638,7 +638,9 @@ void IPCThreadState::processPostWriteDerefs() void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); - + pthread_mutex_lock(&mProcess->mThreadCountLock); + mProcess->mCurrentThreads++; + pthread_mutex_unlock(&mProcess->mThreadCountLock); mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); mIsLooper = true; @@ -666,6 +668,13 @@ void IPCThreadState::joinThreadPool(bool isMain) mOut.writeInt32(BC_EXIT_LOOPER); mIsLooper = false; talkWithDriver(false); + pthread_mutex_lock(&mProcess->mThreadCountLock); + LOG_ALWAYS_FATAL_IF(mProcess->mCurrentThreads == 0, + "Threadpool thread count = 0. Thread cannot exist and exit in empty " + "threadpool\n" + "Misconfiguration. Increase threadpool max threads configuration\n"); + mProcess->mCurrentThreads--; + pthread_mutex_unlock(&mProcess->mThreadCountLock); } status_t IPCThreadState::setupPolling(int* fd) @@ -677,6 +686,9 @@ status_t IPCThreadState::setupPolling(int* fd) mOut.writeInt32(BC_ENTER_LOOPER); flushCommands(); *fd = mProcess->mDriverFD; + pthread_mutex_lock(&mProcess->mThreadCountLock); + mProcess->mCurrentThreads++; + pthread_mutex_unlock(&mProcess->mThreadCountLock); return 0; } @@ -960,18 +972,15 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) freeBuffer); } else { err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer); - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); } } else { - freeBuffer(nullptr, - reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), - tr.offsets_size/sizeof(binder_size_t)); + freeBuffer(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, + reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), + tr.offsets_size / sizeof(binder_size_t)); continue; } } @@ -989,6 +998,7 @@ finish: if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; + logExtendedError(); } return err; @@ -1308,6 +1318,13 @@ status_t IPCThreadState::executeCommand(int32_t cmd) LOG_ONEWAY("Sending reply to %d!", mCallingPid); if (error < NO_ERROR) reply.setError(error); + // b/238777741: clear buffer before we send the reply. + // Otherwise, there is a race where the client may + // receive the reply and send another transaction + // here and the space used by this transaction won't + // be freed for the client. + buffer.setDataSize(0); + constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF; sendReply(reply, (tr.flags & kForwardReplyFlags)); } else { @@ -1443,17 +1460,30 @@ status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { return ret; } -void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, - size_t /*dataSize*/, - const binder_size_t* /*objects*/, - size_t /*objectsSize*/) -{ +void IPCThreadState::logExtendedError() { + struct binder_extended_error ee = {.command = BR_OK}; + + if (!ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::EXTENDED_ERROR)) + return; + +#if defined(__ANDROID__) + if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0) { + ALOGE("Failed to get extended error: %s", strerror(errno)); + return; + } +#endif + + ALOGE_IF(ee.command != BR_OK, "Binder transaction failure: %d/%d/%d", + ee.id, ee.command, ee.param); +} + +void IPCThreadState::freeBuffer(const uint8_t* data, size_t /*dataSize*/, + const binder_size_t* /*objects*/, size_t /*objectsSize*/) { //ALOGI("Freeing parcel %p", &parcel); IF_LOG_COMMANDS() { alog << "Writing BC_FREE_BUFFER for " << data << endl; } ALOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != nullptr) parcel->closeFileDescriptors(); IPCThreadState* state = self(); state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writePointer((uintptr_t)data); diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index fd2d86857e..5db3eef392 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -14,13 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "ServiceManager" +#define LOG_TAG "ServiceManagerCppClient" #include <binder/IServiceManager.h> #include <inttypes.h> #include <unistd.h> +#include <android-base/properties.h> #include <android/os/BnServiceCallback.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> @@ -140,6 +141,16 @@ protected: sp<IServiceManager> defaultServiceManager() { std::call_once(gSmOnce, []() { +#if defined(__BIONIC__) && !defined(__ANDROID_VNDK__) + /* wait for service manager */ { + using std::literals::chrono_literals::operator""s; + using android::base::WaitForProperty; + while (!WaitForProperty("servicemanager.ready", "true", 1s)) { + ALOGE("Waited for servicemanager.ready for a second, waiting another..."); + } + } +#endif + sp<AidlServiceManager> sm = nullptr; while (sm == nullptr) { sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr)); @@ -167,7 +178,7 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) { } } -#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) +#if !defined(__ANDROID_VNDK__) // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 8132d46940..8fe1d2bb3d 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -74,7 +74,7 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) fd = memfd_create_region(name ? name : "MemoryHeapBase", size); if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | - ((mFlags & MEMFD_ALLOW_SEALING) ? 0 : F_SEAL_SEAL); + ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL); 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)); @@ -85,12 +85,9 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) } return; #else - mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING); + mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG); #endif } - if (mFlags & MEMFD_ALLOW_SEALING) { - LOG_ALWAYS_FATAL("Invalid Flags. MEMFD_ALLOW_SEALING only valid with FORCE_MEMFD."); - } fd = ashmem_create_region(name ? name : "MemoryHeapBase", size); ALOGE_IF(fd < 0, "MemoryHeapBase: error creating ashmem region: %s", strerror(errno)); if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; @@ -103,7 +100,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { - if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) { LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); } int open_flags = O_RDWR; @@ -125,7 +122,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { - if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING)) { + if (flags & (FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG)) { LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); } const size_t pagesize = getpagesize(); diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp new file mode 100644 index 0000000000..24ce2bb465 --- /dev/null +++ b/libs/binder/OS.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OS.h" + +#include <android-base/file.h> +#include <binder/RpcTransportRaw.h> +#include <string.h> + +using android::base::ErrnoError; +using android::base::Result; + +namespace android { + +Result<void> setNonBlocking(android::base::borrowed_fd fd) { + int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); + if (flags == -1) { + return ErrnoError() << "Could not get flags for fd"; + } + if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { + return ErrnoError() << "Could not set non-blocking flag for fd"; + } + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { + int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ret == -1) { + return -errno; + } + + base::unique_fd fd(ret); + if (!base::ReadFully(fd, data, size)) { + return -errno; + } + return OK; +} + +status_t dupFileDescriptor(int oldFd, int* newFd) { + int ret = fcntl(oldFd, F_DUPFD_CLOEXEC, 0); + if (ret < 0) { + return -errno; + } + + *newFd = ret; + return OK; +} + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() { + return RpcTransportCtxFactoryRaw::make(); +} + +} // namespace android diff --git a/libs/binder/OS.h b/libs/binder/OS.h new file mode 100644 index 0000000000..5ab8bab0e7 --- /dev/null +++ b/libs/binder/OS.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stddef.h> +#include <cstdint> + +#include <android-base/result.h> +#include <android-base/unique_fd.h> +#include <binder/RpcTransport.h> +#include <utils/Errors.h> + +namespace android { + +android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); + +status_t getRandomBytes(uint8_t* data, size_t size); + +status_t dupFileDescriptor(int oldFd, int* newFd); + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory(); + +} // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 58b0b35323..888757214d 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -20,15 +20,14 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> -#include <linux/sched.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/types.h> -#include <sys/resource.h> #include <unistd.h> #include <binder/Binder.h> @@ -40,6 +39,7 @@ #include <binder/Status.h> #include <binder/TextOutput.h> +#include <android-base/scopeguard.h> #include <cutils/ashmem.h> #include <cutils/compiler.h> #include <utils/Flattenable.h> @@ -48,15 +48,31 @@ #include <utils/String8.h> #include <utils/misc.h> +#include "OS.h" #include "RpcState.h" #include "Static.h" #include "Utils.h" + +// A lot of code in this file uses definitions from the +// Linux kernel header for Binder <linux/android/binder.h> +// which is included indirectly via "binder_module.h". +// Non-Linux OSes do not have that header, so libbinder should be +// built for those targets without kernel binder support, i.e., +// without BINDER_WITH_KERNEL_IPC. For this reason, all code in this +// file that depends on kernel binder, including the header itself, +// is conditional on BINDER_WITH_KERNEL_IPC. +#ifdef BINDER_WITH_KERNEL_IPC +#include <linux/sched.h> #include "binder_module.h" +#else // BINDER_WITH_KERNEL_IPC +// Needed by {read,write}Pointer +typedef uintptr_t binder_uintptr_t; +#endif // BINDER_WITH_KERNEL_IPC #define LOG_REFS(...) -//#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOG_ALLOC(...) -//#define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) +// #define LOG_ALLOC(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) // --------------------------------------------------------------------------- @@ -87,7 +103,8 @@ static_assert(sizeof(Parcel) == 60); static std::atomic<size_t> gParcelGlobalAllocCount; static std::atomic<size_t> gParcelGlobalAllocSize; -static size_t gMaxFds = 0; +// Maximum number of file descriptors per Parcel. +constexpr size_t kMaxFds = 1024; // Maximum size of a blob to transfer in-place. static const size_t BLOB_INPLACE_LIMIT = 16 * 1024; @@ -98,6 +115,7 @@ enum { BLOB_ASHMEM_MUTABLE = 2, }; +#ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { switch (obj.hdr.type) { @@ -150,6 +168,15 @@ static void release_object(const sp<ProcessState>& proc, const flat_binder_objec ALOGE("Invalid object type 0x%08x", obj.hdr.type); } +#endif // BINDER_WITH_KERNEL_IPC + +static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) { + return std::visit([](const auto& fd) { return fd.get(); }, v); +} + +Parcel::RpcFields::RpcFields(const sp<RpcSession>& session) : mSession(session) { + LOG_ALWAYS_FATAL_IF(mSession == nullptr); +} status_t Parcel::finishFlattenBinder(const sp<IBinder>& binder) { @@ -173,22 +200,25 @@ status_t Parcel::finishUnflattenBinder( return OK; } +#ifdef BINDER_WITH_KERNEL_IPC static constexpr inline int schedPolicyMask(int policy, int priority) { return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT); } +#endif // BINDER_WITH_KERNEL_IPC status_t Parcel::flattenBinder(const sp<IBinder>& binder) { BBinder* local = nullptr; if (binder) local = binder->localBinder(); if (local) local->setParceled(); - if (isForRpc()) { + if (const auto* rpcFields = maybeRpcFields()) { if (binder) { status_t status = writeInt32(1); // non-null if (status != OK) return status; uint64_t address; // TODO(b/167966510): need to undo this if the Parcel is not sent - status = mSession->state()->onBinderLeaving(mSession, binder, &address); + status = rpcFields->mSession->state()->onBinderLeaving(rpcFields->mSession, binder, + &address); if (status != OK) return status; status = writeUint64(address); if (status != OK) return status; @@ -199,6 +229,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { return finishFlattenBinder(binder); } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; int schedBits = 0; @@ -255,13 +286,15 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { if (status != OK) return status; return finishFlattenBinder(binder); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::unflattenBinder(sp<IBinder>* out) const { - if (isForRpc()) { - LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel"); - + if (const auto* rpcFields = maybeRpcFields()) { int32_t isPresent; status_t status = readInt32(&isPresent); if (status != OK) return status; @@ -271,10 +304,14 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const if (isPresent & 1) { uint64_t addr; if (status_t status = readUint64(&addr); status != OK) return status; - if (status_t status = mSession->state()->onBinderEntering(mSession, addr, &binder); + if (status_t status = + rpcFields->mSession->state()->onBinderEntering(rpcFields->mSession, addr, + &binder); status != OK) return status; - if (status_t status = mSession->state()->flushExcessBinderRefs(mSession, addr, binder); + if (status_t status = + rpcFields->mSession->state()->flushExcessBinderRefs(rpcFields->mSession, + addr, binder); status != OK) return status; } @@ -282,6 +319,7 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const return finishUnflattenBinder(binder, out); } +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(false); if (flat) { @@ -299,6 +337,10 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const } } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // --------------------------------------------------------------------------- @@ -378,8 +420,10 @@ void Parcel::setDataPosition(size_t pos) const } mDataPos = pos; - mNextObjectHint = 0; - mObjectsSorted = false; + if (const auto* kernelFields = maybeKernelFields()) { + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + } } status_t Parcel::setDataCapacity(size_t size) @@ -406,25 +450,27 @@ status_t Parcel::setData(const uint8_t* buffer, size_t len) if (err == NO_ERROR) { memcpy(const_cast<uint8_t*>(data()), buffer, len); mDataSize = len; - mFdsKnown = false; + if (auto* kernelFields = maybeKernelFields()) { + kernelFields->mFdsKnown = false; + } } return err; } -status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) -{ - if (mSession != parcel->mSession) { +status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { + if (isForRpc() != parcel->isForRpc()) { ALOGE("Cannot append Parcel from one context to another. They may be different formats, " "and objects are specific to a context."); return BAD_TYPE; } + if (isForRpc() && maybeRpcFields()->mSession != parcel->maybeRpcFields()->mSession) { + ALOGE("Cannot append Parcels from different sessions"); + return BAD_TYPE; + } status_t err; - const uint8_t *data = parcel->mData; - const binder_size_t *objects = parcel->mObjects; - size_t size = parcel->mObjectsSize; + const uint8_t* data = parcel->mData; int startPos = mDataPos; - int firstIndex = -1, lastIndex = -2; if (len == 0) { return NO_ERROR; @@ -443,18 +489,6 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) return BAD_VALUE; } - // Count objects in range - for (int i = 0; i < (int) size; i++) { - size_t off = objects[i]; - if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { - if (firstIndex == -1) { - firstIndex = i; - } - lastIndex = i; - } - } - int numObjects = lastIndex - firstIndex + 1; - if ((mDataSize+len) > mDataCapacity) { // grow data err = growData(len); @@ -470,43 +504,125 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) err = NO_ERROR; - if (numObjects > 0) { - const sp<ProcessState> proc(ProcessState::self()); - // grow objects - if (mObjectsCapacity < mObjectsSize + numObjects) { - if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow - if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow - size_t newSize = ((mObjectsSize + numObjects)*3)/2; - if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow - binder_size_t *objects = - (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); - if (objects == (binder_size_t*)nullptr) { - return NO_MEMORY; + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + auto* otherKernelFields = parcel->maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(otherKernelFields == nullptr); + + const binder_size_t* objects = otherKernelFields->mObjects; + size_t size = otherKernelFields->mObjectsSize; + // Count objects in range + int firstIndex = -1, lastIndex = -2; + for (int i = 0; i < (int)size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; } - mObjects = objects; - mObjectsCapacity = newSize; } + int numObjects = lastIndex - firstIndex + 1; + if (numObjects > 0) { + const sp<ProcessState> proc(ProcessState::self()); + // grow objects + if (kernelFields->mObjectsCapacity < kernelFields->mObjectsSize + numObjects) { + if ((size_t)numObjects > SIZE_MAX - kernelFields->mObjectsSize) + return NO_MEMORY; // overflow + if (kernelFields->mObjectsSize + numObjects > SIZE_MAX / 3) + return NO_MEMORY; // overflow + size_t newSize = ((kernelFields->mObjectsSize + numObjects) * 3) / 2; + if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow + binder_size_t* objects = (binder_size_t*)realloc(kernelFields->mObjects, + newSize * sizeof(binder_size_t)); + if (objects == (binder_size_t*)nullptr) { + return NO_MEMORY; + } + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = newSize; + } - // append and acquire objects - int idx = mObjectsSize; - for (int i = firstIndex; i <= lastIndex; i++) { - size_t off = objects[i] - offset + startPos; - mObjects[idx++] = off; - mObjectsSize++; + // append and acquire objects + int idx = kernelFields->mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + kernelFields->mObjects[idx++] = off; + kernelFields->mObjectsSize++; - flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData + off); - acquire_object(proc, *flat, this); + flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData + off); + acquire_object(proc, *flat, this); + + if (flat->hdr.type == BINDER_TYPE_FD) { + // If this is a file descriptor, we need to dup it so the + // new Parcel now owns its own fd, and can declare that we + // officially know we have fds. + flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); + flat->cookie = 1; + kernelFields->mHasFds = kernelFields->mFdsKnown = true; + if (!mAllowFds) { + err = FDS_NOT_ALLOWED; + } + } + } + } +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else { + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + auto* otherRpcFields = parcel->maybeRpcFields(); + if (otherRpcFields == nullptr) { + return BAD_TYPE; + } + if (rpcFields->mSession != otherRpcFields->mSession) { + return BAD_TYPE; + } + + const size_t savedDataPos = mDataPos; + base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + + rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); + if (otherRpcFields->mFds != nullptr) { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + rpcFields->mFds->reserve(otherRpcFields->mFds->size()); + } + for (size_t i = 0; i < otherRpcFields->mObjectPositions.size(); i++) { + const binder_size_t objPos = otherRpcFields->mObjectPositions[i]; + if (offset <= objPos && objPos < offset + len) { + size_t newDataPos = objPos - offset + startPos; + rpcFields->mObjectPositions.push_back(newDataPos); + + mDataPos = newDataPos; + int32_t objectType; + if (status_t status = readInt32(&objectType); status != OK) { + return status; + } + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + continue; + } - if (flat->hdr.type == BINDER_TYPE_FD) { - // If this is a file descriptor, we need to dup it so the - // new Parcel now owns its own fd, and can declare that we - // officially know we have fds. - flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); - flat->cookie = 1; - mHasFds = mFdsKnown = true; if (!mAllowFds) { - err = FDS_NOT_ALLOWED; + return FDS_NOT_ALLOWED; + } + + // Read FD, duplicate, and add to list. + int32_t fdIndex; + if (status_t status = readInt32(&fdIndex); status != OK) { + return status; + } + const auto& oldFd = otherRpcFields->mFds->at(fdIndex); + // To match kernel binder behavior, we always dup, even if the + // FD was unowned in the source parcel. + rpcFields->mFds->emplace_back( + base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0))); + // Fixup the index in the data. + mDataPos = newDataPos + 4; + if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) { + return status; } } } @@ -563,18 +679,28 @@ void Parcel::restoreAllowFds(bool lastValue) bool Parcel::hasFileDescriptors() const { - if (!mFdsKnown) { + if (const auto* rpcFields = maybeRpcFields()) { + return rpcFields->mFds != nullptr && !rpcFields->mFds->empty(); + } + auto* kernelFields = maybeKernelFields(); + if (!kernelFields->mFdsKnown) { scanForFds(); } - return mHasFds; + return kernelFields->mHasFds; } std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { std::vector<sp<IBinder>> ret; +#ifdef BINDER_WITH_KERNEL_IPC + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return ret; + } + size_t initPosition = dataPosition(); - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + offset); if (flat->hdr.type != BINDER_TYPE_BINDER) continue; @@ -586,27 +712,39 @@ std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { } setDataPosition(initPosition); +#endif // BINDER_WITH_KERNEL_IPC + return ret; } std::vector<int> Parcel::debugReadAllFileDescriptors() const { std::vector<int> ret; - size_t initPosition = dataPosition(); - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; - const flat_binder_object* flat = - reinterpret_cast<const flat_binder_object*>(mData + offset); - if (flat->hdr.type != BINDER_TYPE_FD) continue; + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t initPosition = dataPosition(); + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; - setDataPosition(offset); + setDataPosition(offset); - int fd = readFileDescriptor(); - LOG_ALWAYS_FATAL_IF(fd == -1); - ret.push_back(fd); + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + setDataPosition(initPosition); +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif + } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { + for (const auto& fd : *rpcFields->mFds) { + ret.push_back(toRawFd(fd)); + } } - setDataPosition(initPosition); return ret; } @@ -621,17 +759,38 @@ status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* resu return BAD_VALUE; } *result = false; - for (size_t i = 0; i < mObjectsSize; i++) { - size_t pos = mObjects[i]; - if (pos < offset) continue; - if (pos + sizeof(flat_binder_object) > offset + len) { - if (mObjectsSorted) break; - else continue; - } - const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos); - if (flat->hdr.type == BINDER_TYPE_FD) { - *result = true; - break; + if (const auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + size_t pos = kernelFields->mObjects[i]; + if (pos < offset) continue; + if (pos + sizeof(flat_binder_object) > offset + len) { + if (kernelFields->mObjectsSorted) { + break; + } else { + continue; + } + } + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + pos); + if (flat->hdr.type == BINDER_TYPE_FD) { + *result = true; + break; + } + } +#else + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC + } else if (const auto* rpcFields = maybeRpcFields()) { + for (uint32_t pos : rpcFields->mObjectPositions) { + if (offset <= pos && pos < limit) { + const auto* type = reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (*type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + *result = true; + break; + } + } } } return NO_ERROR; @@ -654,23 +813,28 @@ void Parcel::markForRpc(const sp<RpcSession>& session) { LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr, "format must be set before data is written OR on IPC data"); - LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session"); - mSession = session; + mVariantFields.emplace<RpcFields>(session); } bool Parcel::isForRpc() const { - return mSession != nullptr; + return std::holds_alternative<RpcFields>(mVariantFields); } void Parcel::updateWorkSourceRequestHeaderPosition() const { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + // Only update the request headers once. We only want to point // to the first headers read/written. - if (!mRequestHeaderPresent) { - mWorkSourceRequestHeaderPosition = dataPosition(); - mRequestHeaderPresent = true; + if (!kernelFields->mRequestHeaderPresent) { + kernelFields->mWorkSourceRequestHeaderPosition = dataPosition(); + kernelFields->mRequestHeaderPresent = true; } } +#ifdef BINDER_WITH_KERNEL_IPC #if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #elif defined(__ANDROID_RECOVERY__) @@ -678,6 +842,7 @@ constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); #endif +#endif // BINDER_WITH_KERNEL_IPC // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) @@ -686,13 +851,18 @@ status_t Parcel::writeInterfaceToken(const String16& interface) } status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { - if (CC_LIKELY(!isForRpc())) { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC const IPCThreadState* threadState = IPCThreadState::self(); writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); updateWorkSourceRequestHeaderPosition(); writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); writeInt32(kHeader); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } // currently the interface identification token is just its name as a string @@ -701,12 +871,16 @@ status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) { bool Parcel::replaceCallingWorkSourceUid(uid_t uid) { - if (!mRequestHeaderPresent) { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return false; + } + if (!kernelFields->mRequestHeaderPresent) { return false; } const size_t initialPosition = dataPosition(); - setDataPosition(mWorkSourceRequestHeaderPosition); + setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition); status_t err = writeInt32(uid); setDataPosition(initialPosition); return err == NO_ERROR; @@ -714,12 +888,16 @@ bool Parcel::replaceCallingWorkSourceUid(uid_t uid) uid_t Parcel::readCallingWorkSourceUid() const { - if (!mRequestHeaderPresent) { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return false; + } + if (!kernelFields->mRequestHeaderPresent) { return IPCThreadState::kUnsetWorkSource; } const size_t initialPosition = dataPosition(); - setDataPosition(mWorkSourceRequestHeaderPosition); + setDataPosition(kernelFields->mWorkSourceRequestHeaderPosition); uid_t uid = readInt32(); setDataPosition(initialPosition); return uid; @@ -740,7 +918,8 @@ bool Parcel::enforceInterface(const char16_t* interface, size_t len, IPCThreadState* threadState) const { - if (CC_LIKELY(!isForRpc())) { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { @@ -766,6 +945,11 @@ bool Parcel::enforceInterface(const char16_t* interface, header); return false; } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)threadState; + return false; +#endif // BINDER_WITH_KERNEL_IPC } // Interface descriptor. @@ -795,7 +979,10 @@ binder::Status Parcel::enforceNoDataAvail() const { size_t Parcel::objectsCount() const { - return mObjectsSize; + if (const auto* kernelFields = maybeKernelFields()) { + return kernelFields->mObjectsSize; + } + return 0; } status_t Parcel::errorCheck() const @@ -1237,13 +1424,43 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) return err; } -status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) -{ - if (isForRpc()) { - ALOGE("Cannot write file descriptor to remote binder."); - return BAD_TYPE; +status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { + if (auto* rpcFields = maybeRpcFields()) { + std::variant<base::unique_fd, base::borrowed_fd> fdVariant; + if (takeOwnership) { + fdVariant = base::unique_fd(fd); + } else { + fdVariant = base::borrowed_fd(fd); + } + if (!mAllowFds) { + return FDS_NOT_ALLOWED; + } + switch (rpcFields->mSession->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: { + return FDS_NOT_ALLOWED; + } + case RpcSession::FileDescriptorTransportMode::UNIX: { + if (rpcFields->mFds == nullptr) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + } + size_t dataPos = mDataPos; + if (dataPos > UINT32_MAX) { + return NO_MEMORY; + } + if (status_t err = writeInt32(RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR); err != OK) { + return err; + } + if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) { + return err; + } + rpcFields->mObjectPositions.push_back(dataPos); + rpcFields->mFds->push_back(std::move(fdVariant)); + return OK; + } + } } +#ifdef BINDER_WITH_KERNEL_IPC flat_binder_object obj; obj.hdr.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; @@ -1251,13 +1468,19 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) obj.handle = fd; obj.cookie = takeOwnership ? 1 : 0; return writeObject(obj, true); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)fd; + (void)takeOwnership; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeDupFileDescriptor(int fd) { - int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (dupFd < 0) { - return -errno; + int dupFd; + if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + return err; } status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { @@ -1274,9 +1497,9 @@ status_t Parcel::writeParcelFileDescriptor(int fd, bool takeOwnership) status_t Parcel::writeDupParcelFileDescriptor(int fd) { - int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (dupFd < 0) { - return -errno; + int dupFd; + if (status_t err = dupFileDescriptor(fd, &dupFd); err != OK) { + return err; } status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/); if (err != OK) { @@ -1361,7 +1584,7 @@ status_t Parcel::write(const FlattenableHelperInterface& val) const size_t len = val.getFlattenedSize(); const size_t fd_count = val.getFdCount(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -1401,8 +1624,12 @@ status_t Parcel::write(const FlattenableHelperInterface& val) status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) { + auto* kernelFields = maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kernelFields == nullptr, "Can't write flat_binder_object to RPC Parcel"); + +#ifdef BINDER_WITH_KERNEL_IPC const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; - const bool enoughObjects = mObjectsSize < mObjectsCapacity; + const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity; if (enoughData && enoughObjects) { restart_write: *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; @@ -1413,14 +1640,14 @@ restart_write: // fail before modifying our object index return FDS_NOT_ALLOWED; } - mHasFds = mFdsKnown = true; + kernelFields->mHasFds = kernelFields->mFdsKnown = true; } // Need to write meta-data? if (nullMetaData || val.binder != 0) { - mObjects[mObjectsSize] = mDataPos; + kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this); - mObjectsSize++; + kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); @@ -1431,17 +1658,24 @@ restart_write: if (err != NO_ERROR) return err; } if (!enoughObjects) { - if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow - if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow - size_t newSize = ((mObjectsSize+2)*3)/2; + if (kernelFields->mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow + if ((kernelFields->mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow + size_t newSize = ((kernelFields->mObjectsSize + 2) * 3) / 2; if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow - binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); + binder_size_t* objects = + (binder_size_t*)realloc(kernelFields->mObjects, newSize * sizeof(binder_size_t)); if (objects == nullptr) return NO_MEMORY; - mObjects = objects; - mObjectsCapacity = newSize; + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = newSize; } goto restart_write; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + (void)val; + (void)nullMetaData; + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::writeNoException() @@ -1452,55 +1686,70 @@ status_t Parcel::writeNoException() status_t Parcel::validateReadData(size_t upperBound) const { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + // Can't validate RPC Parcel reads because the location of binder + // objects is unknown. + return OK; + } + +#ifdef BINDER_WITH_KERNEL_IPC // Don't allow non-object reads on object data - if (mObjectsSorted || mObjectsSize <= 1) { -data_sorted: + if (kernelFields->mObjectsSorted || kernelFields->mObjectsSize <= 1) { + data_sorted: // Expect to check only against the next object - if (mNextObjectHint < mObjectsSize && upperBound > mObjects[mNextObjectHint]) { + if (kernelFields->mNextObjectHint < kernelFields->mObjectsSize && + upperBound > kernelFields->mObjects[kernelFields->mNextObjectHint]) { // For some reason the current read position is greater than the next object // hint. Iterate until we find the right object - size_t nextObject = mNextObjectHint; + size_t nextObject = kernelFields->mNextObjectHint; do { - if (mDataPos < mObjects[nextObject] + sizeof(flat_binder_object)) { + 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); return PERMISSION_DENIED; } nextObject++; - } while (nextObject < mObjectsSize && upperBound > mObjects[nextObject]); - mNextObjectHint = nextObject; + } while (nextObject < kernelFields->mObjectsSize && + upperBound > kernelFields->mObjects[nextObject]); + kernelFields->mNextObjectHint = nextObject; } return NO_ERROR; } // Quickly determine if mObjects is sorted. - binder_size_t* currObj = mObjects + mObjectsSize - 1; + binder_size_t* currObj = kernelFields->mObjects + kernelFields->mObjectsSize - 1; binder_size_t* prevObj = currObj; - while (currObj > mObjects) { + while (currObj > kernelFields->mObjects) { prevObj--; if(*prevObj > *currObj) { goto data_unsorted; } currObj--; } - mObjectsSorted = true; + kernelFields->mObjectsSorted = true; goto data_sorted; data_unsorted: // Insertion Sort mObjects // Great for mostly sorted lists. If randomly sorted or reverse ordered mObjects become common, // switch to std::sort(mObjects, mObjects + mObjectsSize); - for (binder_size_t* iter0 = mObjects + 1; iter0 < mObjects + mObjectsSize; iter0++) { + for (binder_size_t* iter0 = kernelFields->mObjects + 1; + iter0 < kernelFields->mObjects + kernelFields->mObjectsSize; iter0++) { binder_size_t temp = *iter0; binder_size_t* iter1 = iter0 - 1; - while (iter1 >= mObjects && *iter1 > temp) { + while (iter1 >= kernelFields->mObjects && *iter1 > temp) { *(iter1 + 1) = *iter1; iter1--; } *(iter1 + 1) = temp; } - mNextObjectHint = 0; - mObjectsSorted = true; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = true; goto data_sorted; +#else // BINDER_WITH_KERNEL_IPC + (void)upperBound; + return NO_ERROR; +#endif // BINDER_WITH_KERNEL_IPC } status_t Parcel::read(void* outData, size_t len) const @@ -1513,7 +1762,8 @@ status_t Parcel::read(void* outData, size_t len) const if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1540,7 +1790,8 @@ const void* Parcel::readInplace(size_t len) const if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize && len <= pad_size(len)) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + pad_size(len)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1587,7 +1838,8 @@ status_t Parcel::readAligned(T *pArg) const { static_assert(std::is_trivially_copyable_v<T>); if ((mDataPos+sizeof(T)) <= mDataSize) { - if (mObjectsSize > 0) { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields != nullptr && kernelFields->mObjectsSize > 0) { status_t err = validateReadData(mDataPos + sizeof(T)); if(err != NO_ERROR) { // Still increment the data position by the expected length @@ -1965,8 +2217,32 @@ native_handle* Parcel::readNativeHandle() const return h; } -int Parcel::readFileDescriptor() const -{ +int Parcel::readFileDescriptor() const { + if (const auto* rpcFields = maybeRpcFields()) { + if (!std::binary_search(rpcFields->mObjectPositions.begin(), + rpcFields->mObjectPositions.end(), mDataPos)) { + ALOGW("Attempt to read file descriptor from Parcel %p at offset %zu that is not in the " + "object list", + this, mDataPos); + return BAD_TYPE; + } + + int32_t objectType = readInt32(); + if (objectType != RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + return BAD_TYPE; + } + + int32_t fdIndex = readInt32(); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + return toRawFd(rpcFields->mFds->at(fdIndex)); + } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* flat = readObject(true); if (flat && flat->hdr.type == BINDER_TYPE_FD) { @@ -1974,10 +2250,13 @@ int Parcel::readFileDescriptor() const } return BAD_TYPE; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); + return INVALID_OPERATION; +#endif // BINDER_WITH_KERNEL_IPC } -int Parcel::readParcelFileDescriptor() const -{ +int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { @@ -2017,7 +2296,12 @@ status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const return BAD_TYPE; } - val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); + int dupFd; + if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + return BAD_VALUE; + } + + val->reset(dupFd); if (val->get() < 0) { return BAD_VALUE; @@ -2034,7 +2318,12 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const return BAD_TYPE; } - val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); + int dupFd; + if (status_t err = dupFileDescriptor(got, &dupFd); err != OK) { + return BAD_VALUE; + } + + val->reset(dupFd); if (val->get() < 0) { return BAD_VALUE; @@ -2086,7 +2375,7 @@ status_t Parcel::read(FlattenableHelperInterface& val) const const size_t len = this->readInt32(); const size_t fd_count = this->readInt32(); - if ((len > INT32_MAX) || (fd_count >= gMaxFds)) { + if ((len > INT32_MAX) || (fd_count > kMaxFds)) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; @@ -2130,8 +2419,15 @@ status_t Parcel::read(FlattenableHelperInterface& val) const return err; } + +#ifdef BINDER_WITH_KERNEL_IPC const flat_binder_object* Parcel::readObject(bool nullMetaData) const { + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return nullptr; + } + const size_t DPOS = mDataPos; if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { const flat_binder_object* obj @@ -2146,9 +2442,9 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const } // Ensure that this object is valid... - binder_size_t* const OBJS = mObjects; - const size_t N = mObjectsSize; - size_t opos = mNextObjectHint; + binder_size_t* const OBJS = kernelFields->mObjects; + const size_t N = kernelFields->mObjectsSize; + size_t opos = kernelFields->mNextObjectHint; if (N > 0) { ALOGV("Parcel %p looking for obj at %zu, hint=%zu", @@ -2167,7 +2463,7 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const // Found it! ALOGV("Parcel %p found obj %zu at index %zu with forward search", this, DPOS, opos); - mNextObjectHint = opos+1; + kernelFields->mNextObjectHint = opos + 1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } @@ -2180,7 +2476,7 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const // Found it! ALOGV("Parcel %p found obj %zu at index %zu with backward search", this, DPOS, opos); - mNextObjectHint = opos+1; + kernelFields->mNextObjectHint = opos + 1; ALOGV("readObject Setting data pos of %p to %zu", this, mDataPos); return obj; } @@ -2190,21 +2486,29 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const } return nullptr; } +#endif // BINDER_WITH_KERNEL_IPC -void Parcel::closeFileDescriptors() -{ - size_t i = mObjectsSize; - if (i > 0) { - //ALOGI("Closing file descriptors for %zu objects...", i); - } - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); - if (flat->hdr.type == BINDER_TYPE_FD) { - //ALOGI("Closing fd: %ld", flat->handle); - close(flat->handle); +void Parcel::closeFileDescriptors() { + if (auto* kernelFields = maybeKernelFields()) { +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; + if (i > 0) { + // ALOGI("Closing file descriptors for %zu objects...", i); + } + while (i > 0) { + i--; + const flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); + if (flat->hdr.type == BINDER_TYPE_FD) { + // ALOGI("Closing fd: %ld", flat->handle); + close(flat->handle); + } } +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); +#endif // BINDER_WITH_KERNEL_IPC + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mFds.reset(); } } @@ -2220,35 +2524,44 @@ size_t Parcel::ipcDataSize() const uintptr_t Parcel::ipcObjects() const { - return reinterpret_cast<uintptr_t>(mObjects); + if (const auto* kernelFields = maybeKernelFields()) { + return reinterpret_cast<uintptr_t>(kernelFields->mObjects); + } + return 0; } size_t Parcel::ipcObjectsCount() const { - return mObjectsSize; + if (const auto* kernelFields = maybeKernelFields()) { + return kernelFields->mObjectsSize; + } + return 0; } -void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount, release_func relFunc) -{ +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function"); freeData(); + auto* kernelFields = maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. + mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; - mObjects = const_cast<binder_size_t*>(objects); - mObjectsSize = mObjectsCapacity = objectsCount; + kernelFields->mObjects = const_cast<binder_size_t*>(objects); + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsCount; mOwner = relFunc; +#ifdef BINDER_WITH_KERNEL_IPC binder_size_t minOffset = 0; - for (size_t i = 0; i < mObjectsSize; i++) { - binder_size_t offset = mObjects[i]; + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; if (offset < minOffset) { ALOGE("%s: bad object offset %" PRIu64 " < %" PRIu64 "\n", __func__, (uint64_t)offset, (uint64_t)minOffset); - mObjectsSize = 0; + kernelFields->mObjectsSize = 0; break; } const flat_binder_object* flat @@ -2266,12 +2579,64 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, // WARNING: callers of ipcSetDataReference need to make sure they // don't rely on mObjectsSize in their release_func. - mObjectsSize = 0; + kernelFields->mObjectsSize = 0; break; } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL_IF(objectsCount != 0, + "Non-zero objects count passed to Parcel with kernel driver disabled"); +#endif // BINDER_WITH_KERNEL_IPC +} + +status_t Parcel::rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc) { + // this code uses 'mOwner == nullptr' to understand whether it owns memory + LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function"); + + LOG_ALWAYS_FATAL_IF(session == nullptr); + + if (objectTableSize != ancillaryFds.size()) { + ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size()); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + for (size_t i = 0; i < objectTableSize; i++) { + uint32_t minObjectEnd; + if (__builtin_add_overflow(objectTable[i], sizeof(RpcFields::ObjectType), &minObjectEnd) || + minObjectEnd >= dataSize) { + ALOGE("received out of range object position: %" PRIu32 " (parcel size is %zu)", + objectTable[i], dataSize); + relFunc(data, dataSize, nullptr, 0); + return BAD_VALUE; + } + } + + freeData(); + markForRpc(session); + + auto* rpcFields = maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. + + mData = const_cast<uint8_t*>(data); + mDataSize = mDataCapacity = dataSize; + mOwner = relFunc; + + rpcFields->mObjectPositions.reserve(objectTableSize); + for (size_t i = 0; i < objectTableSize; i++) { + rpcFields->mObjectPositions.push_back(objectTable[i]); + } + if (!ancillaryFds.empty()) { + rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>(); + *rpcFields->mFds = std::move(ancillaryFds); + } + + return OK; } void Parcel::print(TextOutput& to, uint32_t /*flags*/) const @@ -2284,15 +2649,19 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const } else if (dataSize() > 0) { const uint8_t* DATA = data(); to << indent << HexDump(DATA, dataSize()) << dedent; - const binder_size_t* OBJS = mObjects; - const size_t N = objectsCount(); - for (size_t i=0; i<N; i++) { - const flat_binder_object* flat - = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]); - to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " - << TypeCode(flat->hdr.type & 0x7f7f7f00) - << " = " << flat->binder; +#ifdef BINDER_WITH_KERNEL_IPC + if (const auto* kernelFields = maybeKernelFields()) { + const binder_size_t* OBJS = kernelFields->mObjects; + const size_t N = objectsCount(); + for (size_t i = 0; i < N; i++) { + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(DATA + OBJS[i]); + to << endl + << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder; + } } +#endif // BINDER_WITH_KERNEL_IPC } else { to << "NULL"; } @@ -2302,36 +2671,48 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const void Parcel::releaseObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; if (i == 0) { return; } sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; - binder_size_t* const objects = mObjects; + binder_size_t* const objects = kernelFields->mObjects; while (i > 0) { i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(data+objects[i]); + const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); release_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::acquireObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + +#ifdef BINDER_WITH_KERNEL_IPC + size_t i = kernelFields->mObjectsSize; if (i == 0) { return; } const sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; - binder_size_t* const objects = mObjects; + binder_size_t* const objects = kernelFields->mObjects; while (i > 0) { i--; - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(data+objects[i]); + const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); acquire_object(proc, *flat, this); } +#endif // BINDER_WITH_KERNEL_IPC } void Parcel::freeData() @@ -2345,7 +2726,11 @@ void Parcel::freeDataNoInit() if (mOwner) { LOG_ALLOC("Parcel %p: freeing other owner data", this); //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize); + auto* kernelFields = maybeKernelFields(); + // Close FDs before freeing, otherwise they will leak for kernel binder. + closeFileDescriptors(); + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); } else { LOG_ALLOC("Parcel %p: freeing allocated data", this); releaseObjects(); @@ -2358,7 +2743,8 @@ void Parcel::freeDataNoInit() } free(mData); } - if (mObjects) free(mObjects); + auto* kernelFields = maybeKernelFields(); + if (kernelFields && kernelFields->mObjects) free(kernelFields->mObjects); } } @@ -2433,13 +2819,18 @@ status_t Parcel::restartWrite(size_t desired) ALOGV("restartWrite Setting data size of %p to %zu", this, mDataSize); ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos); - free(mObjects); - mObjects = nullptr; - mObjectsSize = mObjectsCapacity = 0; - mNextObjectHint = 0; - mObjectsSorted = false; - mHasFds = false; - mFdsKnown = true; + if (auto* kernelFields = maybeKernelFields()) { + free(kernelFields->mObjects); + kernelFields->mObjects = nullptr; + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = 0; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + kernelFields->mHasFds = false; + kernelFields->mFdsKnown = true; + } else if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mObjectPositions.clear(); + rpcFields->mFds.reset(); + } mAllowFds = true; return NO_ERROR; @@ -2453,17 +2844,27 @@ status_t Parcel::continueWrite(size_t desired) return BAD_VALUE; } + auto* kernelFields = maybeKernelFields(); + auto* rpcFields = maybeRpcFields(); + // If shrinking, first adjust for any objects that appear // after the new data size. - size_t objectsSize = mObjectsSize; + size_t objectsSize = + kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size(); if (desired < mDataSize) { if (desired == 0) { objectsSize = 0; } else { - while (objectsSize > 0) { - if (mObjects[objectsSize-1] < desired) - break; - objectsSize--; + if (kernelFields) { + while (objectsSize > 0) { + if (kernelFields->mObjects[objectsSize - 1] < desired) break; + objectsSize--; + } + } else { + while (objectsSize > 0) { + if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break; + objectsSize--; + } } } } @@ -2484,7 +2885,7 @@ status_t Parcel::continueWrite(size_t desired) } binder_size_t* objects = nullptr; - if (objectsSize) { + if (kernelFields && objectsSize) { objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)); if (!objects) { free(data); @@ -2495,20 +2896,32 @@ status_t Parcel::continueWrite(size_t desired) // Little hack to only acquire references on objects // we will be keeping. - size_t oldObjectsSize = mObjectsSize; - mObjectsSize = objectsSize; + size_t oldObjectsSize = kernelFields->mObjectsSize; + kernelFields->mObjectsSize = objectsSize; acquireObjects(); - mObjectsSize = oldObjectsSize; + kernelFields->mObjectsSize = oldObjectsSize; + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + free(data); + return status; + } } if (mData) { memcpy(data, mData, mDataSize < desired ? mDataSize : desired); } - if (objects && mObjects) { - memcpy(objects, mObjects, objectsSize*sizeof(binder_size_t)); + if (objects && kernelFields && kernelFields->mObjects) { + memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t)); } - //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize); + // ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); + if (kernelFields) { + // TODO(b/239222407): This seems wrong. We should only free FDs when + // they are in a truncated section of the parcel. + closeFileDescriptors(); + } + mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); mOwner = nullptr; LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired); @@ -2516,43 +2929,55 @@ status_t Parcel::continueWrite(size_t desired) gParcelGlobalAllocCount++; mData = data; - mObjects = objects; mDataSize = (mDataSize < desired) ? mDataSize : desired; ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize); mDataCapacity = desired; - mObjectsSize = mObjectsCapacity = objectsSize; - mNextObjectHint = 0; - mObjectsSorted = false; + if (kernelFields) { + kernelFields->mObjects = objects; + kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsSize; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; + } } else if (mData) { - if (objectsSize < mObjectsSize) { + if (kernelFields && objectsSize < kernelFields->mObjectsSize) { +#ifdef BINDER_WITH_KERNEL_IPC // Need to release refs on any objects we are dropping. const sp<ProcessState> proc(ProcessState::self()); - for (size_t i=objectsSize; i<mObjectsSize; i++) { - const flat_binder_object* flat - = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); + for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) { + const flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]); if (flat->hdr.type == BINDER_TYPE_FD) { // will need to rescan because we may have lopped off the only FDs - mFdsKnown = false; + kernelFields->mFdsKnown = false; } release_object(proc, *flat, this); } if (objectsSize == 0) { - free(mObjects); - mObjects = nullptr; - mObjectsCapacity = 0; + free(kernelFields->mObjects); + kernelFields->mObjects = nullptr; + kernelFields->mObjectsCapacity = 0; } else { binder_size_t* objects = - (binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t)); + (binder_size_t*)realloc(kernelFields->mObjects, + objectsSize * sizeof(binder_size_t)); if (objects) { - mObjects = objects; - mObjectsCapacity = objectsSize; + kernelFields->mObjects = objects; + kernelFields->mObjectsCapacity = objectsSize; } } - mObjectsSize = objectsSize; - mNextObjectHint = 0; - mObjectsSorted = false; + kernelFields->mObjectsSize = objectsSize; + kernelFields->mNextObjectHint = 0; + kernelFields->mObjectsSorted = false; +#else // BINDER_WITH_KERNEL_IPC + LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel"); +#endif // BINDER_WITH_KERNEL_IPC + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + return status; + } } // We own the data, so we can just do a realloc(). @@ -2588,9 +3013,12 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } - if(!(mDataCapacity == 0 && mObjects == nullptr - && mObjectsCapacity == 0)) { - ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired); + if (!(mDataCapacity == 0 && + (kernelFields == nullptr || + (kernelFields->mObjects == nullptr && kernelFields->mObjectsCapacity == 0)))) { + ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, + kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsCapacity : 0, desired); } LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired); @@ -2607,6 +3035,35 @@ status_t Parcel::continueWrite(size_t desired) return NO_ERROR; } +status_t Parcel::truncateRpcObjects(size_t newObjectsSize) { + auto* rpcFields = maybeRpcFields(); + if (newObjectsSize == 0) { + rpcFields->mObjectPositions.clear(); + if (rpcFields->mFds) { + rpcFields->mFds->clear(); + } + return OK; + } + while (rpcFields->mObjectPositions.size() > newObjectsSize) { + uint32_t pos = rpcFields->mObjectPositions.back(); + rpcFields->mObjectPositions.pop_back(); + const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos); + if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) { + const auto fdIndex = + *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType)); + if (rpcFields->mFds == nullptr || fdIndex < 0 || + static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) { + ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu", + fdIndex, rpcFields->mFds ? rpcFields->mFds->size() : 0); + return BAD_VALUE; + } + // In practice, this always removes the last element. + rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex); + } + } + return OK; +} + void Parcel::initState() { LOG_ALLOC("Parcel %p: initState", this); @@ -2617,39 +3074,23 @@ void Parcel::initState() mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); - mSession = nullptr; - mObjects = nullptr; - mObjectsSize = 0; - mObjectsCapacity = 0; - mNextObjectHint = 0; - mObjectsSorted = false; - mHasFds = false; - mFdsKnown = true; + mVariantFields.emplace<KernelFields>(); mAllowFds = true; mDeallocZero = false; mOwner = nullptr; - mWorkSourceRequestHeaderPosition = 0; - mRequestHeaderPresent = false; - - // racing multiple init leads only to multiple identical write - if (gMaxFds == 0) { - struct rlimit result; - if (!getrlimit(RLIMIT_NOFILE, &result)) { - gMaxFds = (size_t)result.rlim_cur; - //ALOGI("parcel fd limit set to %zu", gMaxFds); - } else { - ALOGW("Unable to getrlimit: %s", strerror(errno)); - gMaxFds = 1024; - } - } } void Parcel::scanForFds() const { - status_t status = hasFileDescriptorsInRange(0, dataSize(), &mHasFds); + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + status_t status = hasFileDescriptorsInRange(0, dataSize(), &kernelFields->mHasFds); ALOGE_IF(status != NO_ERROR, "Error %d calling hasFileDescriptorsInRange()", status); - mFdsKnown = true; + kernelFields->mFdsKnown = true; } +#ifdef BINDER_WITH_KERNEL_IPC size_t Parcel::getBlobAshmemSize() const { // This used to return the size of all blobs that were written to ashmem, now we're returning @@ -2660,10 +3101,15 @@ size_t Parcel::getBlobAshmemSize() const size_t Parcel::getOpenAshmemSize() const { + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return 0; + } + size_t openAshmemSize = 0; - for (size_t i = 0; i < mObjectsSize; i++) { + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { const flat_binder_object* flat = - reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]); + reinterpret_cast<const flat_binder_object*>(mData + kernelFields->mObjects[i]); // cookie is compared against zero for historical reasons // > obj.cookie = takeOwnership ? 1 : 0; @@ -2677,6 +3123,7 @@ size_t Parcel::getOpenAshmemSize() const } return openAshmemSize; } +#endif // BINDER_WITH_KERNEL_IPC // --- Parcel::Blob --- diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 4a01d8176d..1f311acb20 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -19,6 +19,7 @@ #include <binder/ProcessState.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> @@ -35,14 +36,15 @@ #include <errno.h> #include <fcntl.h> -#include <mutex> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> +#include <unistd.h> +#include <mutex> #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) #define DEFAULT_MAX_BINDER_THREADS 15 @@ -100,6 +102,11 @@ static void verifyNotForked(bool forked) { 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 if (driver == nullptr) { std::lock_guard<std::mutex> l(gProcessMutex); @@ -170,6 +177,10 @@ void ProcessState::childPostFork() { // the thread handler is installed if (gProcess) { gProcess->mForked = true; + + // "O_CLOFORK" + close(gProcess->mDriverFD); + gProcess->mDriverFD = -1; } gProcessMutex.unlock(); } @@ -182,7 +193,6 @@ void ProcessState::startThreadPool() ALOGW("Extra binder thread started, but 0 threads requested. Do not use " "*startThreadPool when zero threads are requested."); } - mThreadPoolStarted = true; spawnPooledThread(true); } @@ -290,12 +300,17 @@ ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) return &mHandleToObject.editItemAt(handle); } +// see b/166779391: cannot change the VNDK interface, so access like this +extern sp<BBinder> the_context_object; + sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; AutoMutex _l(mLock); + if (handle == 0 && the_context_object != nullptr) return the_context_object; + handle_entry* e = lookupHandleLocked(handle); if (e != nullptr) { @@ -386,6 +401,9 @@ void ProcessState::spawnPooledThread(bool isMain) ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = sp<PoolThread>::make(isMain); t->run(name.string()); + pthread_mutex_lock(&mThreadCountLock); + mKernelStartedThreads++; + pthread_mutex_unlock(&mThreadCountLock); } } @@ -402,12 +420,23 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { return result; } -size_t ProcessState::getThreadPoolMaxThreadCount() const { +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 mMaxThreads; + if (mThreadPoolStarted) { + return mCurrentThreads < mKernelStartedThreads + ? mMaxThreads + : mMaxThreads + mCurrentThreads - mKernelStartedThreads; + } // must not be initialized or maybe has poll thread setup, we // currently don't track this in libbinder - return 0; + LOG_ALWAYS_FATAL_IF(mKernelStartedThreads != 0, + "Expecting 0 kernel started threads but have" + " %zu", + mKernelStartedThreads); + return mCurrentThreads; } #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" @@ -415,6 +444,8 @@ bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { static const char* const names[] = { [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] = DRIVER_FEATURES_PATH "oneway_spam_detection", + [static_cast<int>(DriverFeature::EXTENDED_ERROR)] = + DRIVER_FEATURES_PATH "extended_error", }; int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC); char on; @@ -491,6 +522,8 @@ ProcessState::ProcessState(const char* driver) mExecutingThreadsCount(0), mWaitingForThreads(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), + mCurrentThreads(0), + mKernelStartedThreads(0), mStarvationStartTimeMs(0), mForked(false), mThreadPoolStarted(false), diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index ace5cd5052..e581d0b16c 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -24,18 +24,21 @@ #include <thread> #include <vector> -#include <android-base/file.h> #include <android-base/hex.h> #include <android-base/scopeguard.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> +#include <utils/Compat.h> +#include "BuildFlags.h" #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" #include "RpcWireFormat.h" +#include "Utils.h" namespace android { @@ -52,7 +55,7 @@ RpcServer::~RpcServer() { sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { // Default is without TLS. if (rpcTransportCtxFactory == nullptr) - rpcTransportCtxFactory = RpcTransportCtxFactoryRaw::make(); + rpcTransportCtxFactory = makeDefaultRpcTransportCtxFactory(); auto ctx = rpcTransportCtxFactory->newServerCtx(); if (ctx == nullptr) return nullptr; return sp<RpcServer>::make(std::move(ctx)); @@ -83,7 +86,7 @@ status_t RpcServer::setupInetServer(const char* address, unsigned int port, LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet"); sockaddr_in addr{}; socklen_t len = sizeof(addr); - if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) { + if (0 != getsockname(mServer.fd.get(), reinterpret_cast<sockaddr*>(&addr), &len)) { int savedErrno = errno; ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(), strerror(savedErrno)); @@ -121,28 +124,36 @@ void RpcServer::setProtocolVersion(uint32_t version) { mProtocolVersion = version; } +void RpcServer::setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes) { + mSupportedFileDescriptorTransportModes.reset(); + for (RpcSession::FileDescriptorTransportMode mode : modes) { + mSupportedFileDescriptorTransportModes.set(static_cast<size_t>(mode)); + } +} + void RpcServer::setRootObject(const sp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObjectFactory = nullptr; mRootObjectWeak = mRootObject = binder; } void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectFactory = nullptr; mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) { - std::lock_guard<std::mutex> _l(mLock); + std::function<sp<IBinder>(const void*, size_t)>&& makeObject) { + RpcMutexLockGuard _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); mRootObjectFactory = std::move(makeObject); } sp<IBinder> RpcServer::getRootObject() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); bool hasWeak = mRootObjectWeak.unsafe_get(); sp<IBinder> ret = mRootObjectWeak.promote(); ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr"); @@ -150,7 +161,7 @@ sp<IBinder> RpcServer::getRootObject() { } std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mCtx->getCertificate(format); } @@ -159,16 +170,18 @@ static void joinRpcServer(sp<RpcServer>&& thiz) { } void RpcServer::start() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); LOG_ALWAYS_FATAL_IF(mJoinThread.get(), "Already started!"); - mJoinThread = std::make_unique<std::thread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + mJoinThread = + std::make_unique<RpcMaybeThread>(&joinRpcServer, sp<RpcServer>::fromExisting(this)); + rpcJoinIfSingleThreaded(*mJoinThread); } void RpcServer::join() { { - std::lock_guard<std::mutex> _l(mLock); - LOG_ALWAYS_FATAL_IF(!mServer.ok(), "RpcServer must be setup to join."); + RpcMutexLockGuard _l(mLock); + LOG_ALWAYS_FATAL_IF(!mServer.fd.ok(), "RpcServer must be setup to join."); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined"); mJoinThreadRunning = true; mShutdownTrigger = FdTrigger::make(); @@ -177,40 +190,50 @@ void RpcServer::join() { status_t status; while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) { - sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); + std::array<uint8_t, kRpcAddressSize> addr; + static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small"); - unique_fd clientFd( - TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr), - &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK))); + socklen_t addrLen = addr.size(); + RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY( + accept4(mServer.fd.get(), reinterpret_cast<sockaddr*>(addr.data()), &addrLen, + SOCK_CLOEXEC | SOCK_NONBLOCK)))); - LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address"); + LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)), + "Truncated address"); - if (clientFd < 0) { + if (clientSocket.fd < 0) { ALOGE("Could not accept4 socket: %s", strerror(errno)); continue; } - LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); + LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get()); { - std::lock_guard<std::mutex> _l(mLock); - std::thread thread = - std::thread(&RpcServer::establishConnection, sp<RpcServer>::fromExisting(this), - std::move(clientFd), addr, addrLen); - mConnectingThreads[thread.get_id()] = std::move(thread); + RpcMutexLockGuard _l(mLock); + RpcMaybeThread thread = + RpcMaybeThread(&RpcServer::establishConnection, + sp<RpcServer>::fromExisting(this), std::move(clientSocket), addr, + addrLen, RpcSession::join); + + auto& threadRef = mConnectingThreads[thread.get_id()]; + threadRef = std::move(thread); + rpcJoinIfSingleThreaded(threadRef); } } LOG_RPC_DETAIL("RpcServer::join exiting with %s", statusToString(status).c_str()); - { - std::lock_guard<std::mutex> _l(mLock); + if constexpr (kEnableRpcThreads) { + RpcMutexLockGuard _l(mLock); mJoinThreadRunning = false; + } else { + // Multi-threaded builds clear this in shutdown(), but we need it valid + // so the loop above exits cleanly + mShutdownTrigger = nullptr; } mShutdownCv.notify_all(); } bool RpcServer::shutdown() { - std::unique_lock<std::mutex> _l(mLock); + RpcMutexUniqueLock _l(mLock); if (mShutdownTrigger == nullptr) { LOG_RPC_DETAIL("Cannot shutdown. No shutdown trigger installed (already shutdown?)"); return false; @@ -221,10 +244,16 @@ bool RpcServer::shutdown() { for (auto& [id, session] : mSessions) { (void)id; // server lock is a more general lock - std::lock_guard<std::mutex> _lSession(session->mMutex); + RpcMutexLockGuard _lSession(session->mMutex); session->mShutdownTrigger->trigger(); } + if constexpr (!kEnableRpcThreads) { + // In single-threaded mode we're done here, everything else that + // needs to happen should be at the end of RpcServer::join() + return true; + } + while (mJoinThreadRunning || !mConnectingThreads.empty() || !mSessions.empty()) { if (std::cv_status::timeout == mShutdownCv.wait_for(_l, std::chrono::seconds(1))) { ALOGE("Waiting for RpcServer to shut down (1s w/o progress). Join thread running: %d, " @@ -252,7 +281,7 @@ bool RpcServer::shutdown() { } std::vector<sp<RpcSession>> RpcServer::listSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); std::vector<sp<RpcSession>> sessions; for (auto& [id, session] : mSessions) { (void)id; @@ -262,12 +291,14 @@ std::vector<sp<RpcSession>> RpcServer::listSessions() { } size_t RpcServer::numUninitializedSessions() { - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); return mConnectingThreads.size(); } -void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen) { +void RpcServer::establishConnection( + sp<RpcServer>&& server, RpcTransportFd clientFd, std::array<uint8_t, kRpcAddressSize> addr, + size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) { // mShutdownTrigger can only be cleared once connection threads have joined. // It must be set before this thread is started LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr); @@ -275,7 +306,7 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie status_t status = OK; - int clientFdForLog = clientFd.get(); + int clientFdForLog = clientFd.fd.get(); auto client = server->mCtx->newTransport(std::move(clientFd), server->mShutdownTrigger.get()); if (client == nullptr) { ALOGE("Dropping accept4()-ed socket because sslAccept fails"); @@ -288,7 +319,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie RpcConnectionHeader header; if (status == OK) { iovec iov{&header, sizeof(header)}; - status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -302,8 +334,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie if (header.sessionIdSize == kSessionIdBytes) { sessionId.resize(header.sessionIdSize); iovec iov{sessionId.data(), sessionId.size()}; - status = - client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableReadFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, /*ancillaryFds=*/nullptr); if (status != OK) { ALOGE("Failed to read session ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -333,7 +365,8 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie }; iovec iov{&response, sizeof(response)}; - status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, {}); + status = client->interruptableWriteFully(server->mShutdownTrigger.get(), &iov, 1, + std::nullopt, nullptr); if (status != OK) { ALOGE("Failed to send new session response: %s", statusToString(status).c_str()); // still need to cleanup before we can return @@ -341,12 +374,12 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie } } - std::thread thisThread; + RpcMaybeThread thisThread; sp<RpcSession> session; { - std::unique_lock<std::mutex> _l(server->mLock); + RpcMutexUniqueLock _l(server->mLock); - auto threadId = server->mConnectingThreads.find(std::this_thread::get_id()); + auto threadId = server->mConnectingThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); @@ -380,24 +413,34 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie return; } - base::unique_fd fd(TEMP_FAILURE_RETRY( - open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW))); - if (!base::ReadFully(fd, sessionId.data(), sessionId.size())) { - ALOGE("Could not read from /dev/urandom to create session ID"); + auto status = getRandomBytes(sessionId.data(), sessionId.size()); + if (status != OK) { + ALOGE("Failed to read random session ID: %s", strerror(-status)); return; } } while (server->mSessions.end() != server->mSessions.find(sessionId)); - session = RpcSession::make(); + session = sp<RpcSession>::make(nullptr); session->setMaxIncomingThreads(server->mMaxThreads); if (!session->setProtocolVersion(protocolVersion)) return; + if (header.fileDescriptorTransportMode < + server->mSupportedFileDescriptorTransportModes.size() && + server->mSupportedFileDescriptorTransportModes.test( + header.fileDescriptorTransportMode)) { + session->setFileDescriptorTransportMode( + static_cast<RpcSession::FileDescriptorTransportMode>( + header.fileDescriptorTransportMode)); + } else { + ALOGE("Rejecting connection: FileDescriptorTransportMode is not supported: %hhu", + header.fileDescriptorTransportMode); + return; + } + // if null, falls back to server root sp<IBinder> sessionSpecificRoot; if (server->mRootObjectFactory != nullptr) { - sessionSpecificRoot = - server->mRootObjectFactory(reinterpret_cast<const sockaddr*>(&addr), - addrLen); + sessionSpecificRoot = server->mRootObjectFactory(addr.data(), addrLen); if (sessionSpecificRoot == nullptr) { ALOGE("Warning: server returned null from root object factory"); } @@ -438,22 +481,22 @@ void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clie // avoid strong cycle server = nullptr; - RpcSession::join(std::move(session), std::move(setupResult)); + joinFn(std::move(session), std::move(setupResult)); } status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str()); LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server."); - unique_fd serverFd(TEMP_FAILURE_RETRY( - socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); - if (serverFd == -1) { + RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY( + socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)))); + if (!transportFd.fd.ok()) { int savedErrno = errno; ALOGE("Could not create socket: %s", strerror(savedErrno)); return -savedErrno; } - if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) { + if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) { int savedErrno = errno; ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); return -savedErrno; @@ -463,7 +506,7 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { // the backlog is increased to a large number. // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced // to 1. - if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 50 /*backlog*/))) { + if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) { int savedErrno = errno; ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); return -savedErrno; @@ -471,7 +514,7 @@ status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) { LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str()); - if (status_t status = setupExternalServer(std::move(serverFd)); status != OK) { + if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) { ALOGE("Another thread has set up server while calling setupSocketServer. Race?"); return status; } @@ -484,7 +527,7 @@ void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) LOG_RPC_DETAIL("Dropping session with address %s", base::HexString(id.data(), id.size()).c_str()); - std::lock_guard<std::mutex> _l(mLock); + RpcMutexLockGuard _l(mLock); auto it = mSessions.find(id); LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %s", base::HexString(id.data(), id.size()).c_str()); @@ -498,18 +541,18 @@ void RpcServer::onSessionIncomingThreadEnded() { } bool RpcServer::hasServer() { - std::lock_guard<std::mutex> _l(mLock); - return mServer.ok(); + RpcMutexLockGuard _l(mLock); + return mServer.fd.ok(); } unique_fd RpcServer::releaseServer() { - std::lock_guard<std::mutex> _l(mLock); - return std::move(mServer); + RpcMutexLockGuard _l(mLock); + return std::move(mServer.fd); } status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { - std::lock_guard<std::mutex> _l(mLock); - if (mServer.ok()) { + RpcMutexLockGuard _l(mLock); + if (mServer.fd.ok()) { ALOGE("Each RpcServer can only have one server."); return INVALID_OPERATION; } @@ -517,4 +560,14 @@ status_t RpcServer::setupExternalServer(base::unique_fd serverFd) { return OK; } +bool RpcServer::hasActiveRequests() { + RpcMutexLockGuard _l(mLock); + for (const auto& [_, session] : mSessions) { + if (session->hasActiveRequests()) { + return true; + } + } + return !mServer.isInPollingState(); +} + } // namespace android diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index d40778a3d8..49843e5953 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -21,7 +21,6 @@ #include <dlfcn.h> #include <inttypes.h> #include <poll.h> -#include <pthread.h> #include <unistd.h> #include <string_view> @@ -34,19 +33,18 @@ #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> +#include <utils/Compat.h> #include <utils/String8.h> +#include "BuildFlags.h" #include "FdTrigger.h" +#include "OS.h" #include "RpcSocketAddress.h" #include "RpcState.h" #include "RpcWireFormat.h" #include "Utils.h" -#ifdef __GLIBC__ -extern "C" pid_t gettid(); -#endif - -#ifndef __ANDROID_RECOVERY__ +#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) #include <android_runtime/vm.h> #include <jni.h> #endif @@ -63,14 +61,14 @@ RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ct RpcSession::~RpcSession() { LOG_RPC_DETAIL("RpcSession destroyed %p", this); - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0, "Should not be able to destroy a session with servers in use."); } sp<RpcSession> RpcSession::make() { // Default is without TLS. - return make(RpcTransportCtxFactoryRaw::make()); + return make(makeDefaultRpcTransportCtxFactory()); } sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { @@ -80,34 +78,30 @@ sp<RpcSession> RpcSession::make(std::unique_ptr<RpcTransportCtxFactory> rpcTrans } void RpcSession::setMaxIncomingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), - "Must set max incoming threads before setting up connections, but has %zu " - "client(s) and %zu server(s)", - mConnections.mOutgoing.size(), mConnections.mIncoming.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set max incoming threads before setting up connections"); mMaxIncomingThreads = threads; } size_t RpcSession::getMaxIncomingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxIncomingThreads; } void RpcSession::setMaxOutgoingThreads(size_t threads) { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(), - "Must set max outgoing threads before setting up connections, but has %zu " - "client(s) and %zu server(s)", - mConnections.mOutgoing.size(), mConnections.mIncoming.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set max outgoing threads before setting up connections"); mMaxOutgoingThreads = threads; } size_t RpcSession::getMaxOutgoingThreads() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mMaxOutgoingThreads; } -bool RpcSession::setProtocolVersion(uint32_t version) { +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 " @@ -116,7 +110,9 @@ bool RpcSession::setProtocolVersion(uint32_t version) { return false; } - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(checkStarted && mStartedSetup, + "Must set protocol version before setting up connections"); if (mProtocolVersion && version > *mProtocolVersion) { ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u", *mProtocolVersion, version); @@ -127,11 +123,26 @@ bool RpcSession::setProtocolVersion(uint32_t version) { return true; } +bool RpcSession::setProtocolVersion(uint32_t version) { + return setProtocolVersionInternal(version, true); +} + std::optional<uint32_t> RpcSession::getProtocolVersion() { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); return mProtocolVersion; } +void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) { + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, + "Must set file descriptor transport mode before setting up connections"); + mFileDescriptorTransportMode = mode; +} + +RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() { + return mFileDescriptorTransportMode; +} + status_t RpcSession::setupUnixDomainClient(const char* path) { return setupSocketClient(UnixSocketAddress(path)); } @@ -151,14 +162,9 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { return NAME_NOT_FOUND; } -status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) { - // Why passing raw fd? When fd is passed as reference, Clang analyzer sees that the variable - // `fd` is a moved-from object. To work-around the issue, unwrap the raw fd from the outer `fd`, - // pass the raw fd by value to the lambda, and then finally wrap it in unique_fd inside the - // lambda. - return setupClient([&, raw = fd.release()](const std::vector<uint8_t>& sessionId, - bool incoming) -> status_t { - unique_fd fd(raw); +status_t RpcSession::setupPreconnectedClient(base::unique_fd fd, + std::function<unique_fd()>&& request) { + return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t { if (!fd.ok()) { fd = request(); if (!fd.ok()) return BAD_VALUE; @@ -167,7 +173,11 @@ status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_ ALOGE("setupPreconnectedClient: %s", res.error().message().c_str()); return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); } - return initAndAddConnection(std::move(fd), sessionId, incoming); + + RpcTransportFd transportFd(std::move(fd)); + status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming); + fd = unique_fd(); // Explicitly reset after move to avoid analyzer warning. + return status; }); } @@ -183,7 +193,8 @@ status_t RpcSession::addNullDebuggingClient() { return -savedErrno; } - auto server = mCtx->newTransport(std::move(serverFd), mShutdownTrigger.get()); + RpcTransportFd transportFd(std::move(serverFd)); + auto server = mCtx->newTransport(std::move(transportFd), mShutdownTrigger.get()); if (server == nullptr) { ALOGE("Unable to set up RpcTransport"); return UNKNOWN_ERROR; @@ -208,7 +219,7 @@ status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) { } bool RpcSession::shutdownAndWait(bool wait) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr, "Shutdown trigger not installed"); mShutdownTrigger->trigger(); @@ -221,6 +232,12 @@ bool RpcSession::shutdownAndWait(bool wait) { } _l.unlock(); + + if (status_t res = state()->sendObituaries(sp<RpcSession>::fromExisting(this)); res != OK) { + ALOGE("Failed to send obituaries as the RpcSession is shutting down: %s", + statusToString(res).c_str()); + } + mRpcBinderState->clear(); return true; @@ -255,7 +272,7 @@ status_t RpcSession::sendDecStrongToTarget(uint64_t address, size_t target) { status_t RpcSession::readId() { { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client."); } @@ -281,7 +298,7 @@ void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() { mCv.notify_all(); } -void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock, +void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session) { while (session->mConnections.mIncoming.size() > 0) { if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) { @@ -292,11 +309,11 @@ void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std:: } } -void RpcSession::preJoinThreadOwnership(std::thread thread) { - LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread"); +void RpcSession::preJoinThreadOwnership(RpcMaybeThread thread) { + LOG_ALWAYS_FATAL_IF(thread.get_id() != rpc_this_thread::get_id(), "Must own this thread"); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); mConnections.mThreads[thread.get_id()] = std::move(thread); } } @@ -323,7 +340,7 @@ RpcSession::PreJoinSetupResult RpcSession::preJoinSetup( } namespace { -#ifdef __ANDROID_RECOVERY__ +#if !defined(__ANDROID__) || defined(__ANDROID_RECOVERY__) class JavaThreadAttacher {}; #else // RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If @@ -403,8 +420,8 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult sp<RpcSession::EventListener> listener; { - std::lock_guard<std::mutex> _l(session->mMutex); - auto it = session->mConnections.mThreads.find(std::this_thread::get_id()); + RpcMutexLockGuard _l(session->mMutex); + auto it = session->mConnections.mThreads.find(rpc_this_thread::get_id()); LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end()); it->second.detach(); session->mConnections.mThreads.erase(it); @@ -437,10 +454,17 @@ sp<RpcServer> RpcSession::server() { status_t RpcSession::setupClient(const std::function<status_t(const std::vector<uint8_t>& sessionId, bool incoming)>& connectAndInit) { { - std::lock_guard<std::mutex> _l(mMutex); - LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0, - "Must only setup session once, but already has %zu clients", - mConnections.mOutgoing.size()); + RpcMutexLockGuard _l(mMutex); + LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once"); + mStartedSetup = true; + + if constexpr (!kEnableRpcThreads) { + LOG_ALWAYS_FATAL_IF(mMaxIncomingThreads > 0, + "Incoming threads are not supported on single-threaded libbinder"); + // mMaxIncomingThreads should not change from here to its use below, + // since we set mStartedSetup==true and setMaxIncomingThreads checks + // for that + } } if (auto status = initShutdownTrigger(); status != OK) return status; @@ -464,6 +488,9 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< mProtocolVersion = oldProtocolVersion; mConnections = {}; + + // clear mStartedSetup so that we can reuse this RpcSession + mStartedSetup = false; }); if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status; @@ -481,7 +508,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< sp<RpcSession>::fromExisting(this), &version); status != OK) return status; - if (!setProtocolVersion(version)) return BAD_VALUE; + if (!setProtocolVersionInternal(version, false)) return BAD_VALUE; } // TODO(b/189955605): we should add additional sessions dynamically @@ -549,12 +576,14 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, return -savedErrno; } - if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { + RpcTransportFd transportFd(std::move(serverFd)); + + if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) { int connErrno = errno; if (connErrno == EAGAIN || connErrno == EINPROGRESS) { // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or // EINPROGRESS (for others). Call poll() and getsockopt() to get the error. - status_t pollStatus = mShutdownTrigger->triggerablePoll(serverFd, POLLOUT); + status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT); if (pollStatus != OK) { ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s", statusToString(pollStatus).c_str()); @@ -562,8 +591,8 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, } // Set connErrno to the errno that connect() would have set if the fd were blocking. socklen_t connErrnoLen = sizeof(connErrno); - int ret = - getsockopt(serverFd.get(), SOL_SOCKET, SO_ERROR, &connErrno, &connErrnoLen); + int ret = getsockopt(transportFd.fd.get(), SOL_SOCKET, SO_ERROR, &connErrno, + &connErrnoLen); if (ret == -1) { int savedErrno = errno; ALOGE("Could not getsockopt() after connect() on non-blocking socket: %s. " @@ -585,16 +614,17 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, return -connErrno; } } - LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get()); + LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), + transportFd.fd.get()); - return initAndAddConnection(std::move(serverFd), sessionId, incoming); + return initAndAddConnection(std::move(transportFd), sessionId, incoming); } ALOGE("Ran out of retries to connect to %s", addr.toString().c_str()); return UNKNOWN_ERROR; } -status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_t>& sessionId, +status_t RpcSession::initAndAddConnection(RpcTransportFd fd, const std::vector<uint8_t>& sessionId, bool incoming) { LOG_ALWAYS_FATAL_IF(mShutdownTrigger == nullptr); auto server = mCtx->newTransport(std::move(fd), mShutdownTrigger.get()); @@ -613,6 +643,7 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ RpcConnectionHeader header{ .version = mProtocolVersion.value_or(RPC_WIRE_PROTOCOL_VERSION), .options = 0, + .fileDescriptorTransportMode = static_cast<uint8_t>(mFileDescriptorTransportMode), .sessionIdSize = static_cast<uint16_t>(sessionId.size()), }; @@ -621,8 +652,8 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } iovec headerIov{&header, sizeof(header)}; - auto sendHeaderStatus = - server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, {}); + auto sendHeaderStatus = server->interruptableWriteFully(mShutdownTrigger.get(), &headerIov, 1, + std::nullopt, nullptr); if (sendHeaderStatus != OK) { ALOGE("Could not write connection header to socket: %s", statusToString(sendHeaderStatus).c_str()); @@ -633,7 +664,8 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ iovec sessionIov{const_cast<void*>(static_cast<const void*>(sessionId.data())), sessionId.size()}; auto sendSessionIdStatus = - server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, {}); + server->interruptableWriteFully(mShutdownTrigger.get(), &sessionIov, 1, + std::nullopt, nullptr); if (sendSessionIdStatus != OK) { ALOGE("Could not write session ID ('%s') to socket: %s", base::HexString(sessionId.data(), sessionId.size()).c_str(), @@ -652,14 +684,14 @@ status_t RpcSession::initAndAddConnection(unique_fd fd, const std::vector<uint8_ } status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport) { - std::mutex mutex; - std::condition_variable joinCv; - std::unique_lock<std::mutex> lock(mutex); - std::thread thread; + RpcMutex mutex; + RpcConditionVariable joinCv; + RpcMutexUniqueLock lock(mutex); + RpcMaybeThread thread; sp<RpcSession> thiz = sp<RpcSession>::fromExisting(this); bool ownershipTransferred = false; - thread = std::thread([&]() { - std::unique_lock<std::mutex> threadLock(mutex); + thread = RpcMaybeThread([&]() { + RpcMutexUniqueLock threadLock(mutex); std::unique_ptr<RpcTransport> movedRpcTransport = std::move(rpcTransport); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) sp<RpcSession> session = thiz; @@ -675,6 +707,7 @@ status_t RpcSession::addIncomingConnection(std::unique_ptr<RpcTransport> rpcTran RpcSession::join(std::move(session), std::move(setupResult)); }); + rpcJoinIfSingleThreaded(thread); joinCv.wait(lock, [&] { return ownershipTransferred; }); LOG_ALWAYS_FATAL_IF(!ownershipTransferred); return OK; @@ -694,9 +727,9 @@ status_t RpcSession::initShutdownTrigger() { status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTransport, bool init) { sp<RpcConnection> connection = sp<RpcConnection>::make(); { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); connection->rpcTransport = std::move(rpcTransport); - connection->exclusiveTid = gettid(); + connection->exclusiveTid = rpcGetThreadId(); mConnections.mOutgoing.push_back(connection); } @@ -706,10 +739,7 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran mRpcBinderState->sendConnectionInit(connection, sp<RpcSession>::fromExisting(this)); } - { - std::lock_guard<std::mutex> _l(mMutex); - connection->exclusiveTid = std::nullopt; - } + clearConnectionTid(connection); return status; } @@ -722,6 +752,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene LOG_ALWAYS_FATAL_IF(mEventListener != nullptr); LOG_ALWAYS_FATAL_IF(eventListener == nullptr); LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr); + LOG_ALWAYS_FATAL_IF(mCtx != nullptr); mShutdownTrigger = FdTrigger::make(); if (mShutdownTrigger == nullptr) return false; @@ -735,7 +766,7 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport) { - std::lock_guard<std::mutex> _l(mMutex); + RpcMutexLockGuard _l(mMutex); if (mConnections.mIncoming.size() >= mMaxIncomingThreads) { ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)", @@ -753,7 +784,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( sp<RpcConnection> session = sp<RpcConnection>::make(); session->rpcTransport = std::move(rpcTransport); - session->exclusiveTid = gettid(); + session->exclusiveTid = rpcGetThreadId(); mConnections.mIncoming.push_back(session); mConnections.mMaxIncoming = mConnections.mIncoming.size(); @@ -762,7 +793,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( } bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { - std::unique_lock<std::mutex> _l(mMutex); + RpcMutexUniqueLock _l(mMutex); if (auto it = std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection); it != mConnections.mIncoming.end()) { @@ -779,6 +810,15 @@ bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { return false; } +void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) { + RpcMutexUniqueLock _l(mMutex); + connection->exclusiveTid = std::nullopt; + if (mConnections.mWaitingThreads > 0) { + _l.unlock(); + mAvailableConnectionCv.notify_one(); + } +} + std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) { return mCtx->getCertificate(format); } @@ -789,8 +829,8 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co connection->mConnection = nullptr; connection->mReentrant = false; - pid_t tid = gettid(); - std::unique_lock<std::mutex> _l(session->mMutex); + uint64_t tid = rpcGetThreadId(); + RpcMutexUniqueLock _l(session->mMutex); session->mConnections.mWaitingThreads++; while (true) { @@ -876,7 +916,7 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co return OK; } -void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive, +void RpcSession::ExclusiveConnection::findConnection(uint64_t tid, sp<RpcConnection>* exclusive, sp<RpcConnection>* available, std::vector<sp<RpcConnection>>& sockets, size_t socketsIndexHint) { @@ -908,13 +948,28 @@ RpcSession::ExclusiveConnection::~ExclusiveConnection() { // is using this fd, and it retains the right to it. So, we don't give up // exclusive ownership, and no thread is freed. if (!mReentrant && mConnection != nullptr) { - std::unique_lock<std::mutex> _l(mSession->mMutex); - mConnection->exclusiveTid = std::nullopt; - if (mSession->mConnections.mWaitingThreads > 0) { - _l.unlock(); - mSession->mAvailableConnectionCv.notify_one(); + mSession->clearConnectionTid(mConnection); + } +} + +bool RpcSession::hasActiveConnection(const std::vector<sp<RpcConnection>>& connections) { + for (const auto& connection : connections) { + if (connection->exclusiveTid != std::nullopt && !connection->rpcTransport->isWaiting()) { + return true; } } + return false; +} + +bool RpcSession::hasActiveRequests() { + RpcMutexUniqueLock _l(mMutex); + if (hasActiveConnection(mConnections.mIncoming)) { + return true; + } + if (hasActiveConnection(mConnections.mOutgoing)) { + return true; + } + return mConnections.mWaitingThreads != 0; } } // namespace android diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 6d89064a5d..c0e36c4322 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -21,12 +21,14 @@ #include <android-base/hex.h> #include <android-base/macros.h> #include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> #include "Debug.h" #include "RpcWireFormat.h" +#include "Utils.h" #include <random> @@ -34,7 +36,7 @@ namespace android { -using base::ScopeGuard; +using base::StringPrintf; #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { @@ -42,13 +44,22 @@ void rpcMaybeWaitToFlake() { [[clang::no_destroy]] static std::mutex m; unsigned num; { - std::lock_guard<std::mutex> lock(m); + RpcMutexLockGuard lock(m); num = r(); } if (num % 10 == 0) usleep(num % 1000); } #endif +static bool enableAncillaryFds(RpcSession::FileDescriptorTransportMode mode) { + switch (mode) { + case RpcSession::FileDescriptorTransportMode::NONE: + return false; + case RpcSession::FileDescriptorTransportMode::UNIX: + return true; + } +} + RpcState::RpcState() {} RpcState::~RpcState() {} @@ -77,7 +88,7 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind return INVALID_OPERATION; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // TODO(b/182939933): maybe move address out of BpBinder, and keep binder->address map @@ -153,7 +164,7 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t addr return BAD_VALUE; } - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) { @@ -188,7 +199,7 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t // extra reference counting packets now. if (binder->remoteBinder()) return OK; - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; auto it = mNodeForAddress.find(address); @@ -215,78 +226,105 @@ status_t RpcState::flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t return OK; } +status_t RpcState::sendObituaries(const sp<RpcSession>& session) { + RpcMutexUniqueLock _l(mNodeMutex); + + // Gather strong pointers to all of the remote binders for this session so + // we hold the strong references. remoteBinder() returns a raw pointer. + // Send the obituaries and drop the strong pointers outside of the lock so + // the destructors and the onBinderDied calls are not done while locked. + std::vector<sp<IBinder>> remoteBinders; + for (const auto& [_, binderNode] : mNodeForAddress) { + if (auto binder = binderNode.binder.promote()) { + remoteBinders.push_back(std::move(binder)); + } + } + _l.unlock(); + + for (const auto& binder : remoteBinders) { + if (binder->remoteBinder() && + binder->remoteBinder()->getPrivateAccessor().rpcSession() == session) { + binder->remoteBinder()->sendObituary(); + } + } + return OK; +} + size_t RpcState::countBinders() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); return mNodeForAddress.size(); } void RpcState::dump() { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); dumpLocked(); } void RpcState::clear() { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) { LOG_ALWAYS_FATAL_IF(!mNodeForAddress.empty(), "New state should be impossible after terminating!"); return; } + mTerminated = true; if (SHOULD_LOG_RPC_DETAIL) { ALOGE("RpcState::clear()"); dumpLocked(); } - // if the destructor of a binder object makes another RPC call, then calling - // decStrong could deadlock. So, we must hold onto these binders until - // mNodeMutex is no longer taken. - std::vector<sp<IBinder>> tempHoldBinder; - - mTerminated = true; + // invariants for (auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - LOG_ALWAYS_FATAL_IF(binder == nullptr, "Binder %p expected to be owned.", binder.get()); - - if (node.sentRef != nullptr) { - tempHoldBinder.push_back(node.sentRef); + bool guaranteedHaveBinder = node.timesSent > 0; + if (guaranteedHaveBinder) { + LOG_ALWAYS_FATAL_IF(node.sentRef == nullptr, + "Binder expected to be owned with address: %" PRIu64 " %s", address, + node.toString().c_str()); } } - mNodeForAddress.clear(); + // if the destructor of a binder object makes another RPC call, then calling + // decStrong could deadlock. So, we must hold onto these binders until + // mNodeMutex is no longer taken. + auto temp = std::move(mNodeForAddress); + mNodeForAddress.clear(); // RpcState isn't reusable, but for future/explicit _l.unlock(); - tempHoldBinder.clear(); // explicit + temp.clear(); // explicit } void RpcState::dumpLocked() { ALOGE("DUMP OF RpcState %p", this); ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size()); for (const auto& [address, node] : mNodeForAddress) { - sp<IBinder> binder = node.binder.promote(); - - const char* desc; - if (binder) { - if (binder->remoteBinder()) { - if (binder->remoteBinder()->isRpcBinder()) { - desc = "(rpc binder proxy)"; - } else { - desc = "(binder proxy)"; - } + ALOGE("- address: %" PRIu64 " %s", address, node.toString().c_str()); + } + ALOGE("END DUMP OF RpcState"); +} + +std::string RpcState::BinderNode::toString() const { + sp<IBinder> strongBinder = this->binder.promote(); + + const char* desc; + if (strongBinder) { + if (strongBinder->remoteBinder()) { + if (strongBinder->remoteBinder()->isRpcBinder()) { + desc = "(rpc binder proxy)"; } else { - desc = "(local binder)"; + desc = "(binder proxy)"; } } else { - desc = "(null)"; + desc = "(local binder)"; } - - ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s", - node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc); + } else { + desc = "(not promotable)"; } - ALOGE("END DUMP OF RpcState"); -} + return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}", + this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc); +} RpcState::CommandData::CommandData(size_t size) : mSize(size) { // The maximum size for regular binder is 1MB for all concurrent @@ -309,9 +347,11 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { mData.reset(new (std::nothrow) uint8_t[size]); } -status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcState::rpcSend( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", what, i + 1, niovs, connection->rpcTransport.get(), @@ -320,7 +360,8 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, if (status_t status = connection->rpcTransport->interruptableWriteFully(session->mShutdownTrigger.get(), - iovs, niovs, altPoll); + iovs, niovs, altPoll, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to write %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -331,11 +372,14 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, return OK; } -status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) { +status_t RpcState::rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { if (status_t status = connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), - iovs, niovs, {}); + iovs, niovs, std::nullopt, + ancillaryFds); status != OK) { LOG_RPC_DETAIL("Failed to read %s (%d iovs) on RpcTransport %p, error: %s", what, niovs, connection->rpcTransport.get(), statusToString(status).c_str()); @@ -355,7 +399,7 @@ status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& c const sp<RpcSession>& session, uint32_t* version) { RpcNewSessionResponse response; iovec iov{&response, sizeof(response)}; - if (status_t status = rpcRec(connection, session, "new session response", &iov, 1); + if (status_t status = rpcRec(connection, session, "new session response", &iov, 1, nullptr); status != OK) { return status; } @@ -369,14 +413,15 @@ status_t RpcState::sendConnectionInit(const sp<RpcSession::RpcConnection>& conne .msg = RPC_CONNECTION_INIT_OKAY, }; iovec iov{&init, sizeof(init)}; - return rpcSend(connection, session, "connection init", &iov, 1); + return rpcSend(connection, session, "connection init", &iov, 1, std::nullopt); } status_t RpcState::readConnectionInit(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session) { RpcOutgoingConnectionInit init; iovec iov{&init, sizeof(init)}; - if (status_t status = rpcRec(connection, session, "connection init", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "connection init", &iov, 1, nullptr); + status != OK) return status; static_assert(sizeof(init.msg) == sizeof(RPC_CONNECTION_INIT_OKAY)); @@ -448,20 +493,12 @@ status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection, status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection, const sp<IBinder>& binder, uint32_t code, const Parcel& data, const sp<RpcSession>& session, Parcel* reply, uint32_t flags) { - if (!data.isForRpc()) { - ALOGE("Refusing to send RPC with parcel not crafted for RPC call on binder %p code " - "%" PRIu32, - binder.get(), code); - return BAD_TYPE; - } - - if (data.objectsCount() != 0) { - ALOGE("Parcel at %p has attached objects but is being used in an RPC call on binder %p " - "code %" PRIu32, - &data, binder.get(), code); - return BAD_TYPE; + std::string errorMsg; + if (status_t status = validateParcel(session, data, &errorMsg); status != OK) { + ALOGE("Refusing to send RPC on binder %p code %" PRIu32 ": Parcel %p failed validation: %s", + binder.get(), code, &data, errorMsg.c_str()); + return status; } - uint64_t address; if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status; @@ -477,7 +514,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti uint64_t asyncNumber = 0; if (address != 0) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(address); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -493,14 +530,21 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti } } - LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) - - sizeof(RpcWireTransaction) < - data.dataSize(), - "Too much data %zu", data.dataSize()); + auto* rpcFields = data.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + uint32_t bodySize; + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(), + &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), + "Too much data %zu", data.dataSize()); RpcWireHeader command{ .command = RPC_COMMAND_TRANSACT, - .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()), + .bodySize = bodySize, }; RpcWireTransaction transaction{ @@ -508,6 +552,8 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .code = code, .flags = flags, .asyncNumber = asyncNumber, + // bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(data.dataSize()), }; constexpr size_t kWaitMaxUs = 1000000; @@ -516,31 +562,33 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti // Oneway calls have no sync point, so if many are sent before, whether this // is a twoway or oneway transaction, they may have filled up the socket. - // So, make sure we drain them before polling. - std::function<status_t()> drainRefs = [&] { - if (waitUs > kWaitLogUs) { - ALOGE("Cannot send command, trying to process pending refcounts. Waiting %zuus. Too " - "many oneway calls?", - waitUs); - } - - if (waitUs > 0) { - usleep(waitUs); - waitUs = std::min(kWaitMaxUs, waitUs * 2); - } else { - waitUs = 1; - } - - return drainCommands(connection, session, CommandType::CONTROL_ONLY); - }; + // So, make sure we drain them before polling iovec iovs[]{ {&command, sizeof(RpcWireHeader)}, {&transaction, sizeof(RpcWireTransaction)}, {const_cast<uint8_t*>(data.data()), data.dataSize()}, + objectTableSpan.toIovec(), }; - if (status_t status = - rpcSend(connection, session, "transaction", iovs, arraysize(iovs), drainRefs); + if (status_t status = rpcSend( + connection, session, "transaction", iovs, arraysize(iovs), + [&] { + if (waitUs > kWaitLogUs) { + ALOGE("Cannot send command, trying to process pending refcounts. Waiting " + "%zuus. Too many oneway calls?", + waitUs); + } + + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } + + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }, + rpcFields->mFds.get()); status != OK) { // TODO(b/167966510): need to undo onBinderLeaving - we know the // refcount isn't successfully transferred. @@ -560,54 +608,91 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti return waitForReply(connection, session, reply); } -static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount) { - (void)p; - delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data)); +static void cleanup_reply_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount) { + delete[] const_cast<uint8_t*>(data); (void)dataSize; LOG_ALWAYS_FATAL_IF(objects != nullptr); - LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount); + (void)objectsCount; } status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply) { + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; while (true) { iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1); + if (status_t status = rpcRec(connection, session, "command header (for reply)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) + ? &ancillaryFds + : nullptr); status != OK) return status; if (command.command == RPC_COMMAND_REPLY) break; - if (status_t status = processCommand(connection, session, command, CommandType::ANY); + if (status_t status = processCommand(connection, session, command, CommandType::ANY, + std::move(ancillaryFds)); status != OK) return status; + + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = decltype(ancillaryFds)(); } - CommandData data(command.bodySize); - if (!data.valid()) return NO_MEMORY; + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); - iovec iov{data.data(), command.bodySize}; - if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK) - return status; - - if (command.bodySize < sizeof(RpcWireReply)) { + if (command.bodySize < rpcReplyWireSize) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", sizeof(RpcWireReply), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data()); - if (rpcReply->status != OK) return rpcReply->status; - data.release(); - reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data), - nullptr, 0, cleanup_reply_data); + RpcWireReply rpcReply; + memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read - reply->markForRpc(session); + CommandData data(command.bodySize - rpcReplyWireSize); + if (!data.valid()) return NO_MEMORY; - return OK; + iovec iovs[]{ + {&rpcReply, rpcReplyWireSize}, + {data.data(), data.size()}, + }; + if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + status != OK) + return status; + + if (rpcReply.status != OK) return rpcReply.status; + + Span<const uint8_t> parcelSpan = {data.data(), data.size()}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() >= + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(rpcReply.parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size larger than available bytes: %" PRId32 " vs %zu. Terminating!", + rpcReply.parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireReply. Saw bodySize=%" PRId32 + " sizeofHeader=%zu parcelSize=%" PRId32 " objectTableBytesSize=%zu. Terminating!", + command.bodySize, rpcReplyWireSize, rpcReply.parcelDataSize, + objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + + data.release(); + return reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), cleanup_reply_data); } status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection, @@ -618,7 +703,7 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co }; { - std::lock_guard<std::mutex> _l(mNodeMutex); + RpcMutexLockGuard _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(addr); LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), @@ -643,31 +728,32 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co .bodySize = sizeof(RpcDecStrong), }; iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}}; - return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs)); + return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt); } status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get()); + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds; RpcWireHeader command; iovec iov{&command, sizeof(command)}; - if (status_t status = rpcRec(connection, session, "command header (for server)", &iov, 1); + if (status_t status = + rpcRec(connection, session, "command header (for server)", &iov, 1, + enableAncillaryFds(session->getFileDescriptorTransportMode()) ? &ancillaryFds + : nullptr); status != OK) return status; - return processCommand(connection, session, command, type); + return processCommand(connection, session, command, type, std::move(ancillaryFds)); } status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { - uint8_t buf; while (true) { - size_t num_bytes; - status_t status = connection->rpcTransport->peek(&buf, sizeof(buf), &num_bytes); + status_t status = connection->rpcTransport->pollRead(); if (status == WOULD_BLOCK) break; if (status != OK) return status; - if (!num_bytes) break; status = getAndExecuteCommand(connection, session, type); if (status != OK) return status; @@ -675,28 +761,33 @@ status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection return OK; } -status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command, - CommandType type) { +status_t RpcState::processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { +#ifdef BINDER_WITH_KERNEL_IPC IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull(); IPCThreadState::SpGuard spGuard{ .address = __builtin_frame_address(0), - .context = "processing binder RPC command", + .context = "processing binder RPC command (where RpcServer::setPerSessionRootObject is " + "used to distinguish callers)", }; const IPCThreadState::SpGuard* origGuard; if (kernelBinderState != nullptr) { origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - ScopeGuard guardUnguard = [&]() { + + base::ScopeGuard guardUnguard = [&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } }; +#endif // BINDER_WITH_KERNEL_IPC switch (command.command) { case RPC_COMMAND_TRANSACT: if (type != CommandType::ANY) return BAD_TYPE; - return processTransact(connection, session, command); + return processTransact(connection, session, command, std::move(ancillaryFds)); case RPC_COMMAND_DEC_STRONG: return processDecStrong(connection, session, command); } @@ -710,8 +801,10 @@ status_t RpcState::processCommand(const sp<RpcSession::RpcConnection>& connectio (void)session->shutdownAndWait(false); return DEAD_OBJECT; } -status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcWireHeader& command) { +status_t RpcState::processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); CommandData transactionData(command.bodySize); @@ -719,24 +812,26 @@ status_t RpcState::processTransact(const sp<RpcSession::RpcConnection>& connecti return NO_MEMORY; } iovec iov{transactionData.data(), transactionData.size()}; - if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1); status != OK) + if (status_t status = rpcRec(connection, session, "transaction body", &iov, 1, nullptr); + status != OK) return status; - return processTransactInternal(connection, session, std::move(transactionData)); + return processTransactInternal(connection, session, std::move(transactionData), + std::move(ancillaryFds)); } -static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize, +static void do_nothing_to_transact_data(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount) { - (void)p; (void)data; (void)dataSize; (void)objects; (void)objectsCount; } -status_t RpcState::processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData) { +status_t RpcState::processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) { // for 'recursive' calls to this, we have already read and processed the // binder from the transaction data and taken reference counts into account, // so it is cached here. @@ -780,7 +875,7 @@ processTransactInternalTailCall: (void)session->shutdownAndWait(false); replyStatus = BAD_VALUE; } else if (oneway) { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it->second.binder.promote() != target) { ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr); @@ -824,54 +919,85 @@ processTransactInternalTailCall: reply.markForRpc(session); if (replyStatus == OK) { + Span<const uint8_t> parcelSpan = {transaction->data, + transactionData.size() - + offsetof(RpcWireTransaction, data)}; + Span<const uint32_t> objectTableSpan; + if (session->getProtocolVersion().value() > + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + std::optional<Span<const uint8_t>> objectTableBytes = + parcelSpan.splitOff(transaction->parcelDataSize); + if (!objectTableBytes.has_value()) { + ALOGE("Parcel size (%" PRId32 ") greater than available bytes (%zu). Terminating!", + transaction->parcelDataSize, parcelSpan.byteSize()); + (void)session->shutdownAndWait(false); + return BAD_VALUE; + } + std::optional<Span<const uint32_t>> maybeSpan = + objectTableBytes->reinterpret<const uint32_t>(); + if (!maybeSpan.has_value()) { + ALOGE("Bad object table size inferred from RpcWireTransaction. Saw bodySize=%zu " + "sizeofHeader=%zu parcelSize=%" PRId32 + " objectTableBytesSize=%zu. Terminating!", + transactionData.size(), sizeof(RpcWireTransaction), + transaction->parcelDataSize, objectTableBytes->size); + return BAD_VALUE; + } + objectTableSpan = *maybeSpan; + } + Parcel data; // transaction->data is owned by this function. Parcel borrows this data and // only holds onto it for the duration of this function call. Parcel will be // deleted before the 'transactionData' object. - data.ipcSetDataReference(transaction->data, - transactionData.size() - offsetof(RpcWireTransaction, data), - nullptr /*object*/, 0 /*objectCount*/, - do_nothing_to_transact_data); - data.markForRpc(session); - if (target) { - bool origAllowNested = connection->allowNested; - connection->allowNested = !oneway; + replyStatus = + data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, + objectTableSpan.data, objectTableSpan.size, + std::move(ancillaryFds), do_nothing_to_transact_data); + // Reset to avoid spurious use-after-move warning from clang-tidy. + ancillaryFds = std::remove_reference<decltype(ancillaryFds)>::type(); - replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); + if (replyStatus == OK) { + if (target) { + bool origAllowNested = connection->allowNested; + connection->allowNested = !oneway; - connection->allowNested = origAllowNested; - } else { - LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { - replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); - break; - } - case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { - // for client connections, this should always report the value - // originally returned from the server, so this is asserting - // that it exists - replyStatus = reply.writeByteVector(session->mId); - break; - } - default: { - sp<RpcServer> server = session->server(); - if (server) { - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_ROOT: { - sp<IBinder> root = session->mSessionSpecificRootObject - ?: server->getRootObject(); - replyStatus = reply.writeStrongBinder(root); - break; - } - default: { - replyStatus = UNKNOWN_TRANSACTION; + connection->allowNested = origAllowNested; + } else { + LOG_RPC_DETAIL("Got special transaction %u", transaction->code); + + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { + replyStatus = reply.writeInt32(session->getMaxIncomingThreads()); + break; + } + case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { + // for client connections, this should always report the value + // originally returned from the server, so this is asserting + // that it exists + replyStatus = reply.writeByteVector(session->mId); + break; + } + default: { + sp<RpcServer> server = session->server(); + if (server) { + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_ROOT: { + sp<IBinder> root = session->mSessionSpecificRootObject + ?: server->getRootObject(); + replyStatus = reply.writeStrongBinder(root); + break; + } + default: { + replyStatus = UNKNOWN_TRANSACTION; + } } + } else { + ALOGE("Special command sent, but no server object attached."); } - } else { - ALOGE("Special command sent, but no server object attached."); } } } @@ -897,7 +1023,7 @@ processTransactInternalTailCall: // downside: asynchronous transactions may drown out synchronous // transactions. { - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); // last refcount dropped after this transaction happened if (it == mNodeForAddress.end()) return OK; @@ -943,49 +1069,69 @@ processTransactInternalTailCall: replyStatus = flushExcessBinderRefs(session, addr, target); } - LOG_ALWAYS_FATAL_IF(std::numeric_limits<int32_t>::max() - sizeof(RpcWireHeader) - - sizeof(RpcWireReply) < - reply.dataSize(), - "Too much data for reply %zu", reply.dataSize()); + std::string errorMsg; + if (status_t status = validateParcel(session, reply, &errorMsg); status != OK) { + ALOGE("Reply Parcel failed validation: %s", errorMsg.c_str()); + // Forward the error to the client of the transaction. + reply.freeData(); + reply.markForRpc(session); + replyStatus = status; + } + + auto* rpcFields = reply.maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); + + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); + + Span<const uint32_t> objectTableSpan = Span<const uint32_t>{rpcFields->mObjectPositions.data(), + rpcFields->mObjectPositions.size()}; + uint32_t bodySize; + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), + "Too much data for reply %zu", reply.dataSize()); RpcWireHeader cmdReply{ .command = RPC_COMMAND_REPLY, - .bodySize = static_cast<uint32_t>(sizeof(RpcWireReply) + reply.dataSize()), + .bodySize = bodySize, }; RpcWireReply rpcReply{ .status = replyStatus, + // NOTE: Not necessarily written to socket depending on session + // version. + // NOTE: bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast<uint32_t>(reply.dataSize()), + .reserved = {0, 0, 0}, }; - iovec iovs[]{ {&cmdReply, sizeof(RpcWireHeader)}, - {&rpcReply, sizeof(RpcWireReply)}, + {&rpcReply, rpcReplyWireSize}, {const_cast<uint8_t*>(reply.data()), reply.dataSize()}, + objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs)); + return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + rpcFields->mFds.get()); } status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command); - CommandData commandData(command.bodySize); - if (!commandData.valid()) { - return NO_MEMORY; - } - iovec iov{commandData.data(), commandData.size()}; - if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK) - return status; - if (command.bodySize != sizeof(RpcDecStrong)) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcDecStrong. Terminating!", sizeof(RpcDecStrong), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcDecStrong* body = reinterpret_cast<RpcDecStrong*>(commandData.data()); - uint64_t addr = RpcWireAddress::toRaw(body->address); - std::unique_lock<std::mutex> _l(mNodeMutex); + RpcDecStrong body; + iovec iov{&body, sizeof(RpcDecStrong)}; + if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1, nullptr); + status != OK) + return status; + + uint64_t addr = RpcWireAddress::toRaw(body.address); + RpcMutexUniqueLock _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr); @@ -1002,19 +1148,19 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return BAD_VALUE; } - if (it->second.timesSent < body->amount) { + if (it->second.timesSent < body.amount) { ALOGE("Record of sending binder %zu times, but requested decStrong for %" PRIu64 " of %u", - it->second.timesSent, addr, body->amount); + it->second.timesSent, addr, body.amount); return OK; } LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64, addr); - LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body->amount, + LOG_RPC_DETAIL("Processing dec strong of %" PRIu64 " by %u from %zu", addr, body.amount, it->second.timesSent); - it->second.timesSent -= body->amount; + it->second.timesSent -= body.amount; sp<IBinder> tempHold = tryEraseNode(it); _l.unlock(); tempHold = nullptr; // destructor may make binder calls on this session @@ -1022,6 +1168,50 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return OK; } +status_t RpcState::validateParcel(const sp<RpcSession>& session, const Parcel& parcel, + std::string* errorMsg) { + auto* rpcFields = parcel.maybeRpcFields(); + if (rpcFields == nullptr) { + *errorMsg = "Parcel not crafted for RPC call"; + return BAD_TYPE; + } + + if (rpcFields->mSession != session) { + *errorMsg = "Parcel's session doesn't match"; + return BAD_TYPE; + } + + uint32_t protocolVersion = session->getProtocolVersion().value(); + if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE && + !rpcFields->mObjectPositions.empty()) { + *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version " + "(%" PRIu32 ") is too old, must be at least %" PRIu32, + protocolVersion, + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE); + return BAD_VALUE; + } + + if (rpcFields->mFds && !rpcFields->mFds->empty()) { + switch (session->getFileDescriptorTransportMode()) { + case RpcSession::FileDescriptorTransportMode::NONE: + *errorMsg = + "Parcel has file descriptors, but no file descriptor transport is enabled"; + return FDS_NOT_ALLOWED; + case RpcSession::FileDescriptorTransportMode::UNIX: { + constexpr size_t kMaxFdsPerMsg = 253; + if (rpcFields->mFds->size() > kMaxFdsPerMsg) { + *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix " + "domain socket: %zu (max is %zu)", + rpcFields->mFds->size(), kMaxFdsPerMsg); + return BAD_VALUE; + } + } + } + } + + return OK; +} + sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) { sp<IBinder> ref; diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index f4a08942b3..7aab5eeae7 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -19,6 +19,7 @@ #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <map> #include <optional> @@ -139,6 +140,11 @@ public: */ [[nodiscard]] status_t flushExcessBinderRefs(const sp<RpcSession>& session, uint64_t address, const sp<IBinder>& binder); + /** + * Called when the RpcSession is shutdown. + * Send obituaries for each known remote binder with this session. + */ + [[nodiscard]] status_t sendObituaries(const sp<RpcSession>& session); size_t countBinders(); void dump(); @@ -178,28 +184,39 @@ private: size_t mSize; }; - [[nodiscard]] status_t rpcSend(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - int niovs, const std::function<status_t()>& altPoll = nullptr); - [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const char* what, iovec* iovs, - int niovs); + [[nodiscard]] status_t rpcSend( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = + nullptr); + [[nodiscard]] status_t rpcRec( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const char* what, iovec* iovs, int niovs, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr); [[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, Parcel* reply); - [[nodiscard]] status_t processCommand(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command, CommandType type); - [[nodiscard]] status_t processTransact(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - const RpcWireHeader& command); - [[nodiscard]] status_t processTransactInternal(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, - CommandData transactionData); + [[nodiscard]] status_t processCommand( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, CommandType type, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransact( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + const RpcWireHeader& command, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); + [[nodiscard]] status_t processTransactInternal( + const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, + CommandData transactionData, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds); [[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const RpcWireHeader& command); + // Whether `parcel` is compatible with `session`. + [[nodiscard]] static status_t validateParcel(const sp<RpcSession>& session, + const Parcel& parcel, std::string* errorMsg); + struct BinderNode { // Two cases: // A - local binder we are serving @@ -246,6 +263,8 @@ private: // // (no additional data specific to remote binders) + + std::string toString() const; }; // checks if there is any reference left to a node and erases it. If erase @@ -257,7 +276,7 @@ private: // false - session shutdown, halt [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node); - std::mutex mNodeMutex; + RpcMutex mNodeMutex; bool mTerminated = false; uint32_t mNextId = 0; // binders known by both sides of a session diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 7cfc78082e..65e8fac341 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -18,148 +18,176 @@ #include <log/log.h> #include <poll.h> +#include <stddef.h> #include <binder/RpcTransportRaw.h> #include "FdTrigger.h" #include "RpcState.h" +#include "RpcTransportUtils.h" namespace android { namespace { +// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. +constexpr size_t kMaxFdsPerMsg = 253; + // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: - explicit RpcTransportRaw(android::base::unique_fd socket) : mSocket(std::move(socket)) {} - status_t peek(void* buf, size_t size, size_t* out_size) override { - ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK)); + explicit RpcTransportRaw(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + status_t pollRead(void) override { + uint8_t buf; + ssize_t ret = TEMP_FAILURE_RETRY( + ::recv(mSocket.fd.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT)); if (ret < 0) { int savedErrno = errno; if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { return WOULD_BLOCK; } - LOG_RPC_DETAIL("RpcTransport peek(): %s", strerror(savedErrno)); + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); return -savedErrno; + } else if (ret == 0) { + return DEAD_OBJECT; } - *out_size = static_cast<size_t>(ret); return OK; } - template <typename SendOrReceive> - status_t interruptableReadOrWrite(FdTrigger* fdTrigger, iovec* iovs, int niovs, - SendOrReceive sendOrReceiveFun, const char* funName, - int16_t event, const std::function<status_t()>& altPoll) { - MAYBE_WAIT_IN_FLAKE_MODE; + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + bool sentFds = false; + auto send = [&](iovec* iovs, int niovs) -> ssize_t { + if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) { + if (ancillaryFds->size() > kMaxFdsPerMsg) { + // This shouldn't happen because we check the FD count in RpcState. + ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). " + "Aborting session.", + ancillaryFds->size(), kMaxFdsPerMsg); + errno = EINVAL; + return -1; + } - if (niovs < 0) { - return BAD_VALUE; - } + // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then + // use memcpy. + int fds[kMaxFdsPerMsg]; + for (size_t i = 0; i < ancillaryFds->size(); i++) { + fds[i] = std::visit([](const auto& fd) { return fd.get(); }, + ancillaryFds->at(i)); + } + const size_t fdsByteSize = sizeof(int) * ancillaryFds->size(); - // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we - // may never know we should be shutting down. - if (fdTrigger->isTriggered()) { - return DEAD_OBJECT; - } + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)]; - // If iovs has one or more empty vectors at the end and - // we somehow advance past all the preceding vectors and - // pass some or all of the empty ones to sendmsg/recvmsg, - // the call will return processSize == 0. In that case - // we should be returning OK but instead return DEAD_OBJECT. - // To avoid this problem, we make sure here that the last - // vector at iovs[niovs - 1] has a non-zero length. - while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { - niovs--; - } - if (niovs == 0) { - // The vectors are all empty, so we have nothing to send. - return OK; - } + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(fdsByteSize); + memcpy(CMSG_DATA(cmsg), fds, fdsByteSize); + + msg.msg_controllen = CMSG_SPACE(fdsByteSize); + + ssize_t processedSize = TEMP_FAILURE_RETRY( + sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC)); + if (processedSize > 0) { + sentFds = true; + } + return processedSize; + } - bool havePolled = false; - while (true) { msghdr msg{ .msg_iov = iovs, // posix uses int, glibc uses size_t. niovs is a // non-negative int and can be cast to either. .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), }; - ssize_t processSize = - TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL)); - - if (processSize < 0) { - int savedErrno = errno; + return TEMP_FAILURE_RETRY(sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, + altPoll); + } - // Still return the error on later passes, since it would expose - // a problem with polling - if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) { - LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno)); - return -savedErrno; + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { + auto recv = [&](iovec* iovs, int niovs) -> ssize_t { + if (ancillaryFds != nullptr) { + int fdBuffer[kMaxFdsPerMsg]; + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + ssize_t processSize = + TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL)); + if (processSize < 0) { + return -1; } - } else if (processSize == 0) { - return DEAD_OBJECT; - } else { - while (processSize > 0 && niovs > 0) { - auto& iov = iovs[0]; - if (static_cast<size_t>(processSize) < iov.iov_len) { - // Advance the base of the current iovec - iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; - iov.iov_len -= processSize; + + for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks + // application devs to memcpy the data to ensure memory alignment. + size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0); + LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check + memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen); + size_t fdCount = dataLen / sizeof(int); + ancillaryFds->reserve(ancillaryFds->size() + fdCount); + for (size_t i = 0; i < fdCount; i++) { + ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i])); + } break; } - - // The current iovec was fully written - processSize -= iov.iov_len; - iovs++; - niovs--; } - if (niovs == 0) { - LOG_ALWAYS_FATAL_IF(processSize > 0, - "Reached the end of iovecs " - "with %zd bytes remaining", - processSize); - return OK; - } - } - if (altPoll) { - if (status_t status = altPoll(); status != OK) return status; - if (fdTrigger->isTriggered()) { - return DEAD_OBJECT; + if (msg.msg_flags & MSG_CTRUNC) { + ALOGE("msg was truncated. Aborting session."); + errno = EPIPE; + return -1; } - } else { - if (status_t status = fdTrigger->triggerablePoll(mSocket.get(), event); - status != OK) - return status; - if (!havePolled) havePolled = true; - } - } - } - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT, + return processSize; + } + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll); } - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, recvmsg, "recvmsg", POLLIN, - altPoll); - } + virtual bool isWaiting() { return mSocket.isInPollingState(); } private: - base::unique_fd mSocket; + android::RpcTransportFd mSocket; }; // RpcTransportCtx with TLS disabled. class RpcTransportCtxRaw : public RpcTransportCtx { public: - std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const { - return std::make_unique<RpcTransportRaw>(std::move(fd)); + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const { + return std::make_unique<RpcTransportRaw>(std::move(socket)); } std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp new file mode 100644 index 0000000000..453279c0a5 --- /dev/null +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTransportTipcAndroid" + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <log/log.h> +#include <poll.h> +#include <trusty/tipc.h> + +#include "FdTrigger.h" +#include "RpcState.h" +#include "RpcTransportUtils.h" + +using android::base::Error; +using android::base::Result; + +namespace android { + +namespace { + +// RpcTransport for writing Trusty IPC clients in Android. +class RpcTransportTipcAndroid : public RpcTransport { +public: + explicit RpcTransportTipcAndroid(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + + status_t pollRead() override { + if (mReadBufferPos < mReadBufferSize) { + // We have more data in the read buffer + return OK; + } + + // Trusty IPC device is not a socket, so MSG_PEEK is not available + pollfd pfd{.fd = mSocket.fd.get(), .events = static_cast<int16_t>(POLLIN), .revents = 0}; + ssize_t ret = TEMP_FAILURE_RETRY(::poll(&pfd, 1, 0)); + if (ret < 0) { + int savedErrno = errno; + if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { + return WOULD_BLOCK; + } + + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); + return -savedErrno; + } + + if (pfd.revents & POLLNVAL) { + return BAD_VALUE; + } + if (pfd.revents & POLLERR) { + return DEAD_OBJECT; + } + if (pfd.revents & POLLHUP) { + return DEAD_OBJECT; + } + if (pfd.revents & POLLIN) { + return OK; + } + + return WOULD_BLOCK; + } + + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override { + auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // TODO: send ancillaryFds. For now, we just abort if anyone tries + // to send any. + LOG_ALWAYS_FATAL_IF(ancillaryFds != nullptr && !ancillaryFds->empty(), + "File descriptors are not supported on Trusty yet"); + return TEMP_FAILURE_RETRY(tipc_send(mSocket.fd.get(), iovs, niovs, nullptr, 0)); + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, writeFn, "tipc_send", + POLLOUT, altPoll); + } + + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) + override { + auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { + // Fill the read buffer at most once per readFn call, then try to + // return as much of it as possible. If the input iovecs are spread + // across multiple messages that require multiple fillReadBuffer + // calls, we expect the caller to advance the iovecs past the first + // read and call readFn as many times as needed to get all the data + status_t ret = fillReadBuffer(); + if (ret != OK) { + // We need to emulate a Linux read call, which sets errno on + // error and returns -1 + errno = -ret; + return -1; + } + + ssize_t processSize = 0; + for (size_t i = 0; i < niovs && mReadBufferPos < mReadBufferSize; i++) { + auto& iov = iovs[i]; + size_t numBytes = std::min(iov.iov_len, mReadBufferSize - mReadBufferPos); + memcpy(iov.iov_base, mReadBuffer.get() + mReadBufferPos, numBytes); + mReadBufferPos += numBytes; + processSize += numBytes; + } + + return processSize; + }; + return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, readFn, "read", POLLIN, + altPoll); + } + + bool isWaiting() override { return mSocket.isInPollingState(); } + +private: + status_t fillReadBuffer() { + if (mReadBufferPos < mReadBufferSize) { + return OK; + } + + if (!mReadBuffer) { + // Guarantee at least kDefaultBufferSize bytes + mReadBufferCapacity = std::max(mReadBufferCapacity, kDefaultBufferSize); + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + } + + // Reset the size and position in case we have to exit with an error. + // After we read a message into the buffer, we update the size + // with the actual value. + mReadBufferPos = 0; + mReadBufferSize = 0; + + while (true) { + ssize_t processSize = TEMP_FAILURE_RETRY( + read(mSocket.fd.get(), mReadBuffer.get(), mReadBufferCapacity)); + if (processSize == 0) { + return DEAD_OBJECT; + } else if (processSize < 0) { + int savedErrno = errno; + if (savedErrno == EMSGSIZE) { + // Buffer was too small, double it and retry + if (__builtin_mul_overflow(mReadBufferCapacity, 2, &mReadBufferCapacity)) { + return NO_MEMORY; + } + mReadBuffer.reset(new (std::nothrow) uint8_t[mReadBufferCapacity]); + if (!mReadBuffer) { + return NO_MEMORY; + } + continue; + } else { + LOG_RPC_DETAIL("RpcTransport fillBuffer(): %s", strerror(savedErrno)); + return -savedErrno; + } + } else { + mReadBufferSize = static_cast<size_t>(processSize); + return OK; + } + } + } + + RpcTransportFd mSocket; + + // For now, we copy all the input data into a temporary buffer because + // we might get multiple interruptableReadFully calls per message, but + // the tipc device only allows one read call. We read every message into + // this temporary buffer, then return pieces of it from our method. + // + // The special transaction GET_MAX_THREADS takes 40 bytes, so the default + // size should start pretty high. + static constexpr size_t kDefaultBufferSize = 64; + std::unique_ptr<uint8_t[]> mReadBuffer; + size_t mReadBufferPos = 0; + size_t mReadBufferSize = 0; + size_t mReadBufferCapacity = 0; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcAndroid : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd fd, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcAndroid>(std::move(fd)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcAndroid::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcAndroid>(); +} + +const char* RpcTransportCtxFactoryTipcAndroid::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcAndroid::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcAndroid>( + new RpcTransportCtxFactoryTipcAndroid()); +} + +} // namespace android diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index bc68c379ce..3e98ecca9b 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -181,9 +181,10 @@ public: // |sslError| should be from Ssl::getError(). // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise // return error. Also return error if |fdTrigger| is triggered before or during poll(). - status_t pollForSslError(android::base::borrowed_fd fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::function<status_t()>& altPoll) { + status_t pollForSslError( + const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { switch (sslError) { case SSL_ERROR_WANT_READ: return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); @@ -197,11 +198,12 @@ public: private: bool mHandled = false; - status_t handlePoll(int event, android::base::borrowed_fd fd, FdTrigger* fdTrigger, - const char* fnString, const std::function<status_t()>& altPoll) { + status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger, + const char* fnString, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { status_t ret; if (altPoll) { - ret = altPoll(); + ret = (*altPoll)(); if (fdTrigger->isTriggered()) ret = DEAD_OBJECT; } else { ret = fdTrigger->triggerablePoll(fd, event); @@ -275,23 +277,30 @@ private: class RpcTransportTls : public RpcTransport { public: - RpcTransportTls(android::base::unique_fd socket, Ssl ssl) + RpcTransportTls(RpcTransportFd socket, Ssl ssl) : mSocket(std::move(socket)), mSsl(std::move(ssl)) {} - status_t peek(void* buf, size_t size, size_t* out_size) override; - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override; - status_t interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override; + status_t pollRead(void) override; + status_t interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) + override; + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; + + bool isWaiting() { return mSocket.isInPollingState(); }; private: - android::base::unique_fd mSocket; + android::RpcTransportFd mSocket; Ssl mSsl; }; // Error code is errno. -status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) { - size_t todo = std::min<size_t>(size, std::numeric_limits<int>::max()); - auto [ret, errorQueue] = mSsl.call(SSL_peek, buf, static_cast<int>(todo)); +status_t RpcTransportTls::pollRead(void) { + uint8_t buf; + auto [ret, errorQueue] = mSsl.call(SSL_peek, &buf, sizeof(buf)); if (ret < 0) { int err = mSsl.getError(ret); if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { @@ -304,12 +313,15 @@ status_t RpcTransportTls::peek(void* buf, size_t size, size_t* out_size) { } errorQueue.clear(); LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret); - *out_size = static_cast<size_t>(ret); return OK; } -status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcTransportTls::interruptableWriteFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -340,7 +352,7 @@ status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* i int sslError = mSsl.getError(writeSize); // TODO(b/195788248): BIO should contain the FdTrigger, and send(2) / recv(2) should be // triggerablePoll()-ed. Then additionalEvent is no longer necessary. - status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, + status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger, "SSL_write", POLLIN, altPoll); if (pollStatus != OK) return pollStatus; // Do not advance buffer. Try SSL_write() again. @@ -350,8 +362,12 @@ status_t RpcTransportTls::interruptableWriteFully(FdTrigger* fdTrigger, iovec* i return OK; } -status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) { +status_t RpcTransportTls::interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { + (void)ancillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -384,7 +400,7 @@ status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* io return DEAD_OBJECT; } int sslError = mSsl.getError(readSize); - status_t pollStatus = errorQueue.pollForSslError(mSocket.get(), sslError, fdTrigger, + status_t pollStatus = errorQueue.pollForSslError(mSocket, sslError, fdTrigger, "SSL_read", 0, altPoll); if (pollStatus != OK) return pollStatus; // Do not advance buffer. Try SSL_read() again. @@ -395,8 +411,8 @@ status_t RpcTransportTls::interruptableReadFully(FdTrigger* fdTrigger, iovec* io } // For |ssl|, set internal FD to |fd|, and do handshake. Handshake is triggerable by |fdTrigger|. -bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdTrigger) { - bssl::UniquePtr<BIO> bio = newSocketBio(fd); +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()); (void)bio.release(); // SSL_set_bio takes ownership. @@ -416,8 +432,8 @@ bool setFdAndDoHandshake(Ssl* ssl, android::base::borrowed_fd fd, FdTrigger* fdT return false; } int sslError = ssl->getError(ret); - status_t pollStatus = - errorQueue.pollForSslError(fd, sslError, fdTrigger, "SSL_do_handshake", 0, {}); + status_t pollStatus = errorQueue.pollForSslError(socket, sslError, fdTrigger, + "SSL_do_handshake", 0, std::nullopt); if (pollStatus != OK) return false; } } @@ -428,7 +444,7 @@ public: typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>> static std::unique_ptr<RpcTransportCtxTls> create( std::shared_ptr<RpcCertificateVerifier> verifier, RpcAuth* auth); - std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, + std::unique_ptr<RpcTransport> newTransport(RpcTransportFd fd, FdTrigger* fdTrigger) const override; std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override; @@ -499,15 +515,15 @@ std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create( return ret; } -std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::base::unique_fd fd, +std::unique_ptr<RpcTransport> RpcTransportCtxTls::newTransport(android::RpcTransportFd socket, FdTrigger* fdTrigger) const { bssl::UniquePtr<SSL> ssl(SSL_new(mCtx.get())); TEST_AND_RETURN(nullptr, ssl != nullptr); Ssl wrapped(std::move(ssl)); preHandshake(&wrapped); - TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, fd, fdTrigger)); - return std::make_unique<RpcTransportTls>(std::move(fd), std::move(wrapped)); + TEST_AND_RETURN(nullptr, setFdAndDoHandshake(&wrapped, socket, fdTrigger)); + return std::make_unique<RpcTransportTls>(std::move(socket), std::move(wrapped)); } class RpcTransportCtxTlsServer : public RpcTransportCtxTls { diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h new file mode 100644 index 0000000000..32f0db805d --- /dev/null +++ b/libs/binder/RpcTransportUtils.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <poll.h> + +#include "FdTrigger.h" +#include "RpcState.h" + +namespace android { + +template <typename SendOrReceive> +status_t interruptableReadOrWrite( + const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs, + SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, + const std::optional<android::base::function_ref<status_t()>>& altPoll) { + MAYBE_WAIT_IN_FLAKE_MODE; + + if (niovs < 0) { + return BAD_VALUE; + } + + // Since we didn't poll, we need to manually check to see if it was triggered. Otherwise, we + // may never know we should be shutting down. + if (fdTrigger->isTriggered()) { + return DEAD_OBJECT; + } + + // If iovs has one or more empty vectors at the end and + // we somehow advance past all the preceding vectors and + // pass some or all of the empty ones to sendmsg/recvmsg, + // the call will return processSize == 0. In that case + // we should be returning OK but instead return DEAD_OBJECT. + // To avoid this problem, we make sure here that the last + // vector at iovs[niovs - 1] has a non-zero length. + while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { + niovs--; + } + if (niovs == 0) { + // The vectors are all empty, so we have nothing to send. + return OK; + } + + bool havePolled = false; + while (true) { + ssize_t processSize = sendOrReceiveFun(iovs, niovs); + if (processSize < 0) { + int savedErrno = errno; + + // Still return the error on later passes, since it would expose + // a problem with polling + if (havePolled || (savedErrno != EAGAIN && savedErrno != EWOULDBLOCK)) { + LOG_RPC_DETAIL("RpcTransport %s(): %s", funName, strerror(savedErrno)); + return -savedErrno; + } + } else if (processSize == 0) { + return DEAD_OBJECT; + } else { + while (processSize > 0 && niovs > 0) { + auto& iov = iovs[0]; + if (static_cast<size_t>(processSize) < iov.iov_len) { + // Advance the base of the current iovec + iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; + iov.iov_len -= processSize; + break; + } + + // The current iovec was fully written + processSize -= iov.iov_len; + iovs++; + niovs--; + } + if (niovs == 0) { + LOG_ALWAYS_FATAL_IF(processSize > 0, + "Reached the end of iovecs " + "with %zd bytes remaining", + processSize); + return OK; + } + } + + if (altPoll) { + if (status_t status = (*altPoll)(); status != OK) return status; + if (fdTrigger->isTriggered()) { + return DEAD_OBJECT; + } + } else { + if (status_t status = fdTrigger->triggerablePoll(socket, event); status != OK) + return status; + if (!havePolled) havePolled = true; + } + } +} + +} // namespace android diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp new file mode 100644 index 0000000000..ea49eef676 --- /dev/null +++ b/libs/binder/RpcTrusty.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTrusty" + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcAndroid.h> +#include <trusty/tipc.h> + +namespace android { + +using android::base::unique_fd; + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port) { + auto session = RpcSession::make(RpcTransportCtxFactoryTipcAndroid::make()); + auto request = [=] { + int tipcFd = tipc_connect(device, port); + if (tipcFd < 0) { + LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd; + return unique_fd(); + } + return unique_fd(tipcFd); + }; + if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) { + LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str(); + return nullptr; + } + return session->getRootObject(); +} + +} // namespace android diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h index 171550e620..ff1b01a213 100644 --- a/libs/binder/RpcWireFormat.h +++ b/libs/binder/RpcWireFormat.h @@ -45,7 +45,8 @@ static_assert(sizeof(RpcWireAddress) == sizeof(uint64_t)); struct RpcConnectionHeader { uint32_t version; // maximum supported by caller uint8_t options; - uint8_t reservered[9]; + uint8_t fileDescriptorTransportMode; + uint8_t reservered[8]; // Follows is sessionIdSize bytes. // if size is 0, this is requesting a new session. uint16_t sessionIdSize; @@ -108,6 +109,10 @@ enum : uint32_t { // serialization is like: // |RpcWireHeader|struct desginated by 'command'| (over and over again) +// +// When file descriptors are included in out-of-band data (e.g. in unix domain +// sockets), they are always paired with the RpcWireHeader bytes of the +// transaction or reply the file descriptors belong to. struct RpcWireHeader { uint32_t command; // RPC_COMMAND_* @@ -131,7 +136,10 @@ struct RpcWireTransaction { uint64_t asyncNumber; - uint32_t reserved[4]; + // The size of the Parcel data directly following RpcWireTransaction. + uint32_t parcelDataSize; + + uint32_t reserved[3]; uint8_t data[]; }; @@ -139,9 +147,23 @@ static_assert(sizeof(RpcWireTransaction) == 40); struct RpcWireReply { int32_t status; // transact return - uint8_t data[]; + + // -- Fields below only transmitted starting at protocol version 1 -- + + // The size of the Parcel data directly following RpcWireReply. + uint32_t parcelDataSize; + + uint32_t reserved[3]; + + // Byte size of RpcWireReply in the wire protocol. + static size_t wireSize(uint32_t protocolVersion) { + if (protocolVersion == 0) { + return sizeof(int32_t); + } + return sizeof(RpcWireReply); + } }; -static_assert(sizeof(RpcWireReply) == 4); +static_assert(sizeof(RpcWireReply) == 20); #pragma clang diagnostic pop diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index 83b97d04c6..dba65878fb 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -139,6 +139,9 @@ status_t Status::readFromParcel(const Parcel& parcel) { mMessage = String8(message.value_or(String16())); // Skip over the remote stack trace data + const size_t remote_start = parcel.dataPosition(); + // Get available size before reading more + const size_t remote_avail = parcel.dataAvail(); int32_t remote_stack_trace_header_size; status = parcel.readInt32(&remote_stack_trace_header_size); if (status != OK) { @@ -146,13 +149,16 @@ status_t Status::readFromParcel(const Parcel& parcel) { return status; } if (remote_stack_trace_header_size < 0 || - static_cast<size_t>(remote_stack_trace_header_size) > parcel.dataAvail()) { + static_cast<size_t>(remote_stack_trace_header_size) > remote_avail) { android_errorWriteLog(0x534e4554, "132650049"); setFromStatusT(UNKNOWN_ERROR); return UNKNOWN_ERROR; } - parcel.setDataPosition(parcel.dataPosition() + remote_stack_trace_header_size); + + if (remote_stack_trace_header_size != 0) { + parcel.setDataPosition(remote_start + remote_stack_trace_header_size); + } if (mException == EX_SERVICE_SPECIFIC) { status = parcel.readInt32(&mErrorCode); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index ebb0d2731c..c91d56c95e 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -28,9 +28,6 @@ "name": "binderLibTest" }, { - "name": "binderRpcTest" - }, - { "name": "binderStabilityTest" }, { @@ -83,5 +80,24 @@ { "name": "rustBinderSerializationTest" } + ], + "presubmit-large": [ + { + "name": "binderRpcTest" + }, + { + "name": "binderRpcTestNoKernel" + }, + { + "name": "binderRpcTestSingleThreaded" + }, + { + "name": "binderRpcTestSingleThreadedNoKernel" + } + ], + "hwasan-presubmit": [ + { + "name": "binderLibTest" + } ] } diff --git a/libs/binder/Trace.cpp b/libs/binder/Trace.cpp new file mode 100644 index 0000000000..1ebfa1a165 --- /dev/null +++ b/libs/binder/Trace.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Trace.h> +#include <cutils/trace.h> + +namespace android { +namespace binder { + +void atrace_begin(uint64_t tag, const char* name) { + ::atrace_begin(tag, name); +} + +void atrace_end(uint64_t tag) { + ::atrace_end(tag); +} + +} // namespace binder +} // namespace android diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index d2a5be1102..0314b0fea7 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -18,24 +18,10 @@ #include <string.h> -using android::base::ErrnoError; -using android::base::Result; - namespace android { void zeroMemory(uint8_t* data, size_t size) { memset(data, 0, size); } -Result<void> setNonBlocking(android::base::borrowed_fd fd) { - int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); - if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; - } - if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { - return ErrnoError() << "Could not set non-blocking flag for fd"; - } - return {}; -} - } // namespace android diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index ff2fad8834..e04199c75a 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,12 +14,13 @@ * limitations under the License. */ -#include <cstdint> #include <stddef.h> +#include <sys/uio.h> +#include <cstdint> +#include <optional> -#include <android-base/result.h> -#include <android-base/unique_fd.h> #include <log/log.h> +#include <utils/Errors.h> #define TEST_AND_RETURN(value, expr) \ do { \ @@ -34,6 +35,39 @@ namespace android { // avoid optimizations void zeroMemory(uint8_t* data, size_t size); -android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +// View of contiguous sequence. Similar to std::span. +template <typename T> +struct Span { + T* data = nullptr; + size_t size = 0; + + size_t byteSize() { return size * sizeof(T); } + + iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; } + + // Truncates `this` to a length of `offset` and returns a span with the + // remainder. + // + // `std::nullopt` iff offset > size. + std::optional<Span<T>> splitOff(size_t offset) { + if (offset > size) { + return std::nullopt; + } + Span<T> rest = {data + offset, size - offset}; + size = offset; + return rest; + } + + // Returns nullopt if the byte size of `this` isn't evenly divisible by sizeof(U). + template <typename U> + std::optional<Span<U>> reinterpret() const { + // Only allow casting from bytes for simplicity. + static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>); + if (size % sizeof(U) != 0) { + return std::nullopt; + } + return Span<U>{reinterpret_cast<U*>(data), size / sizeof(U)}; + } +}; } // namespace android diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 46223bb00e..08dbd13bf4 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -59,6 +59,8 @@ public: virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; void withLock(const std::function<void()>& doWithLock); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); virtual BBinder* localBinder(); @@ -103,6 +105,12 @@ public: [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd, const sp<IBinder>& keepAliveBinder); + // Start recording transactions to the unique_fd in data. + // See BinderRecordReplay.h for more details. + [[nodiscard]] status_t startRecordingTransactions(const Parcel& data); + // Stop the current recording. + [[nodiscard]] status_t stopRecordingTransactions(); + protected: virtual ~BBinder(); @@ -129,7 +137,7 @@ private: friend ::android::internal::Stability; int16_t mStability; bool mParceled; - uint8_t mReserved0; + bool mRecordingOn; #ifdef __LP64__ int32_t mReserved1; diff --git a/libs/binder/include/binder/BinderRecordReplay.h b/libs/binder/include/binder/BinderRecordReplay.h new file mode 100644 index 0000000000..25ed5e5ff8 --- /dev/null +++ b/libs/binder/include/binder/BinderRecordReplay.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <mutex> + +namespace android { + +namespace binder::debug { + +// Warning: Transactions are sequentially recorded to the file descriptor in a +// non-stable format. A detailed description of the recording format can be found in +// BinderRecordReplay.cpp. + +class RecordedTransaction { +public: + // Filled with the first transaction from fd. + static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd); + // Filled with the arguments. + static std::optional<RecordedTransaction> fromDetails(uint32_t code, uint32_t flags, + const Parcel& data, const Parcel& reply, + status_t err); + RecordedTransaction(RecordedTransaction&& t) noexcept; + + [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const; + + uint32_t getCode() const; + uint32_t getFlags() const; + uint64_t getDataSize() const; + uint64_t getReplySize() const; + int32_t getReturnedStatus() const; + uint32_t getVersion() const; + const Parcel& getDataParcel() const; + const Parcel& getReplyParcel() const; + +private: + RecordedTransaction() = default; + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wpadded" + struct TransactionHeader { + uint32_t code = 0; + uint32_t flags = 0; + uint64_t dataSize = 0; + uint64_t replySize = 0; + int32_t statusReturned = 0; + uint32_t version = 0; // !0 iff Rpc + }; +#pragma clang diagnostic pop + static_assert(sizeof(TransactionHeader) == 32); + static_assert(sizeof(TransactionHeader) % 8 == 0); + + TransactionHeader mHeader; + Parcel mSent; + Parcel mReply; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" + uint8_t mReserved[40]; +#pragma clang diagnostic pop +}; + +} // namespace binder::debug + +} // namespace android diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 19ad5e6efe..57e103d39e 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <utils/Mutex.h> @@ -72,6 +73,8 @@ public: virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; void withLock(const std::function<void()>& doWithLock); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); virtual BpBinder* remoteBinder(); @@ -87,6 +90,12 @@ public: std::optional<int32_t> getDebugBinderHandle() const; + // Start recording transactions to the unique_fd. + // See BinderRecordReplay.h for more details. + status_t startRecordingBinder(const android::base::unique_fd& fd); + // Stop the current recording. + status_t stopRecordingBinder(); + class ObjectManager { public: ObjectManager(); @@ -96,6 +105,8 @@ public: IBinder::object_cleanup_func func); void* find(const void* objectID) const; void* detach(const void* objectID); + sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, + const void* makeArgs); void kill(); @@ -104,9 +115,9 @@ public: ObjectManager& operator=(const ObjectManager&); struct entry_t { - void* object; - void* cleanupCookie; - IBinder::object_cleanup_func func; + void* object = nullptr; + void* cleanupCookie = nullptr; + IBinder::object_cleanup_func func = nullptr; }; std::map<const void*, entry_t> mObjects; diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h new file mode 100644 index 0000000000..8b3fc1cc10 --- /dev/null +++ b/libs/binder/include/binder/Delegate.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> + +#ifndef __BIONIC__ +#ifndef __assert + +// defined differently by liblog +#pragma push_macro("LOG_PRI") +#ifdef LOG_PRI +#undef LOG_PRI +#endif +#include <syslog.h> +#pragma pop_macro("LOG_PRI") + +#define __assert(a, b, c) \ + do { \ + syslog(LOG_ERR, a ": " c); \ + abort(); \ + } while (false) +#endif // __assert +#endif // __BIONIC__ + +namespace android { + +/* + * Used to manage AIDL's *Delegator types. + * This is used to: + * - create a new *Delegator object that delegates to the binder argument. + * - or return an existing *Delegator object that already delegates to the + * binder argument. + * - or return the underlying delegate binder if the binder argument is a + * *Delegator itself. + * + * @param binder - the binder to delegate to or unwrap + * + * @return pointer to the *Delegator object or the unwrapped binder object + */ +template <typename T> +sp<T> delegate(const sp<T>& binder) { + const void* isDelegatorId = &T::descriptor; + const void* hasDelegatorId = &T::descriptor + 1; + // is binder itself a delegator? + if (T::asBinder(binder)->findObject(isDelegatorId)) { + if (T::asBinder(binder)->findObject(hasDelegatorId)) { + __assert(__FILE__, __LINE__, + "This binder has a delegator and is also delegator itself! This is " + "likely an unintended mixing of binders."); + return nullptr; + } + // unwrap the delegator + return static_cast<typename T::DefaultDelegator*>(binder.get())->getImpl(); + } + + struct MakeArgs { + const sp<T>* binder; + const void* id; + } makeArgs; + makeArgs.binder = &binder; + makeArgs.id = isDelegatorId; + + // the binder is not a delegator, so construct one + sp<IBinder> newDelegator = T::asBinder(binder)->lookupOrCreateWeak( + hasDelegatorId, + [](const void* args) -> sp<IBinder> { + auto delegator = sp<typename T::DefaultDelegator>::make( + *static_cast<const MakeArgs*>(args)->binder); + // make sure we know this binder is a delegator by attaching a unique ID + (void)delegator->attachObject(static_cast<const MakeArgs*>(args)->id, + reinterpret_cast<void*>(0x1), nullptr, nullptr); + return delegator; + }, + static_cast<const void*>(&makeArgs)); + return sp<typename T::DefaultDelegator>::cast(newDelegator); +} + +} // namespace android diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 43fc5ff1a9..e75d548bd8 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -56,6 +56,8 @@ public: LAST_CALL_TRANSACTION = 0x00ffffff, PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'), + START_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'S', 'R', 'D'), + STOP_RECORDING_TRANSACTION = B_PACK_CHARS('_', 'E', 'R', 'D'), DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'), SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'), INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), @@ -284,6 +286,9 @@ public: virtual BBinder* localBinder(); virtual BpBinder* remoteBinder(); + typedef sp<IBinder> (*object_make_func)(const void* makeArgs); + sp<IBinder> lookupOrCreateWeak(const void* objectID, object_make_func make, + const void* makeArgs); protected: virtual ~IBinder(); diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 706783093c..9f7e2c87b8 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -72,9 +72,9 @@ class BnInterface : public INTERFACE, public BBinder public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; @@ -85,9 +85,9 @@ class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp<IBinder>& remote); + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index bf02099ffb..c01e92f043 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -28,6 +28,10 @@ typedef int uid_t; // --------------------------------------------------------------------------- namespace android { +/** + * Kernel binder thread state. All operations here refer to kernel binder. This + * object is allocated per-thread. + */ class IPCThreadState { public: @@ -213,9 +217,9 @@ private: void clearCaller(); static void threadDestructor(void *st); - static void freeBuffer(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + static void freeBuffer(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); + static void logExtendedError(); const sp<ProcessState> mProcess; Vector<BBinder*> mPendingStrongDerefs; diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 15dd28f08e..c7177bd8eb 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -26,9 +26,10 @@ namespace android { // --------------------------------------------------------------------------- -class MemoryHeapBase : public virtual BnMemoryHeap +class MemoryHeapBase : public BnMemoryHeap { public: + static constexpr auto MEMFD_ALLOW_SEALING_FLAG = 0x00000800; enum { READ_ONLY = IMemoryHeap::READ_ONLY, // memory won't be mapped locally, but will be mapped in the remote @@ -48,7 +49,7 @@ public: // Clients of shared files can seal at anytime via syscall, leading to // TOC/TOU issues if additional seals prevent access from the creating // process. Alternatively, seccomp fcntl(). - MEMFD_ALLOW_SEALING = 0x00000800 + MEMFD_ALLOW_SEALING = FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG }; /* diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index e2b2c5128d..54692398c6 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -20,6 +20,7 @@ #include <map> // for legacy reasons #include <string> #include <type_traits> +#include <variant> #include <vector> #include <android-base/unique_fd.h> @@ -76,6 +77,11 @@ public: size_t dataCapacity() const; status_t setDataSize(size_t size); + + // this must only be used to set a data position that was previously returned from + // dataPosition(). If writes are made, the exact same types of writes must be made (e.g. + // auto i = p.dataPosition(); p.writeInt32(0); p.setDataPosition(i); p.writeInt32(1);). + // Writing over objects, such as file descriptors and binders, is not supported. void setDataPosition(size_t pos) const; status_t setDataCapacity(size_t size); @@ -592,24 +598,33 @@ public: void print(TextOutput& to, uint32_t flags = 0) const; private: - typedef void (*release_func)(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsSize); + // `objects` and `objectsSize` always 0 for RPC Parcels. + typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsSize); uintptr_t ipcData() const; size_t ipcDataSize() const; uintptr_t ipcObjects() const; size_t ipcObjectsCount() const; - void ipcSetDataReference(const uint8_t* data, size_t dataSize, - const binder_size_t* objects, size_t objectsCount, - release_func relFunc); + void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, + size_t objectsCount, release_func relFunc); + // Takes ownership even when an error is returned. + status_t rpcSetDataReference( + const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, + const uint32_t* objectTable, size_t objectTableSize, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds, + release_func relFunc); status_t finishWrite(size_t len); void releaseObjects(); void acquireObjects(); status_t growData(size_t len); + // Clear the Parcel and set the capacity to `desired`. + // Doesn't reset the RPC session association. status_t restartWrite(size_t desired); + // Set the capacity to `desired`, truncating the Parcel if necessary. status_t continueWrite(size_t desired); + status_t truncateRpcObjects(size_t newObjectsSize); status_t writePointer(uintptr_t val); status_t readPointer(uintptr_t *pArg) const; uintptr_t readPointer() const; @@ -1169,10 +1184,20 @@ private: c->clear(); // must clear before resizing/reserving otherwise move ctors may be called. if constexpr (is_pointer_equivalent_array_v<T>) { // could consider POD without gaps and alignment of 4. - auto data = reinterpret_cast<const T*>( - readInplace(static_cast<size_t>(size) * sizeof(T))); + size_t dataLen; + if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) { + return -EOVERFLOW; + } + auto data = reinterpret_cast<const T*>(readInplace(dataLen)); if (data == nullptr) return BAD_VALUE; - c->insert(c->begin(), data, data + size); // insert should do a reserve(). + // std::vector::insert and similar methods will require type-dependent + // byte alignment when inserting from a const iterator such as `data`, + // e.g. 8 byte alignment for int64_t, and so will not work if `data` + // is 4 byte aligned (which is all Parcel guarantees). Copying + // the contents into the vector directly, where possible, circumvents + // this. + c->resize(size); + memcpy(c->data(), data, dataLen); } else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char16_t>) { c->reserve(size); // avoids default initialization @@ -1247,19 +1272,57 @@ private: uint8_t* mData; size_t mDataSize; size_t mDataCapacity; - mutable size_t mDataPos; - binder_size_t* mObjects; - size_t mObjectsSize; - size_t mObjectsCapacity; - mutable size_t mNextObjectHint; - mutable bool mObjectsSorted; + mutable size_t mDataPos; - mutable bool mRequestHeaderPresent; + // Fields only needed when parcelling for "kernel Binder". + struct KernelFields { + binder_size_t* mObjects = nullptr; + size_t mObjectsSize = 0; + size_t mObjectsCapacity = 0; + mutable size_t mNextObjectHint = 0; - mutable size_t mWorkSourceRequestHeaderPosition; + mutable size_t mWorkSourceRequestHeaderPosition = 0; + mutable bool mRequestHeaderPresent = false; + + mutable bool mObjectsSorted = false; + mutable bool mFdsKnown = true; + mutable bool mHasFds = false; + }; + // Fields only needed when parcelling for RPC Binder. + struct RpcFields { + RpcFields(const sp<RpcSession>& session); + + // Should always be non-null. + const sp<RpcSession> mSession; + + enum ObjectType : int32_t { + TYPE_BINDER_NULL = 0, + TYPE_BINDER = 1, + // FD to be passed via native transport (Trusty IPC or UNIX domain socket). + TYPE_NATIVE_FILE_DESCRIPTOR = 2, + }; + + // Sorted. + std::vector<uint32_t> mObjectPositions; + + // File descriptors referenced by the parcel data. Should be indexed + // using the offsets in the parcel data. Don't assume the list is in the + // same order as `mObjectPositions`. + // + // Boxed to save space. Lazy allocated. + std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds; + }; + std::variant<KernelFields, RpcFields> mVariantFields; + + // Pointer to KernelFields in mVariantFields if present. + KernelFields* maybeKernelFields() { return std::get_if<KernelFields>(&mVariantFields); } + const KernelFields* maybeKernelFields() const { + return std::get_if<KernelFields>(&mVariantFields); + } + // Pointer to RpcFields in mVariantFields if present. + RpcFields* maybeRpcFields() { return std::get_if<RpcFields>(&mVariantFields); } + const RpcFields* maybeRpcFields() const { return std::get_if<RpcFields>(&mVariantFields); } - mutable bool mFdsKnown; - mutable bool mHasFds; bool mAllowFds; // if this parcelable is involved in a secure transaction, force the @@ -1268,7 +1331,6 @@ private: release_func mOwner; - sp<RpcSession> mSession; size_t mReserved; class Blob { diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 675585ecef..9679a5f477 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -29,6 +29,10 @@ namespace android { class IPCThreadState; +/** + * Kernel binder process state. All operations here refer to kernel binder. This + * object is allocated per process. + */ class ProcessState : public virtual RefBase { public: static sp<ProcessState> self(); @@ -84,14 +88,15 @@ public: void setCallRestriction(CallRestriction restriction); /** - * Get the max number of threads that the kernel can start. - * - * Note: this is the lower bound. Additional threads may be started. + * Get the max number of threads that have joined the thread pool. + * This includes kernel started threads, user joined threads and polling + * threads if used. */ - size_t getThreadPoolMaxThreadCount() const; + size_t getThreadPoolMaxTotalThreadCount() const; enum class DriverFeature { ONEWAY_SPAM_DETECTION, + EXTENDED_ERROR, }; // Determine whether a feature is supported by the binder driver. static bool isDriverFeatureEnabled(const DriverFeature feature); @@ -125,15 +130,19 @@ private: void* mVMStart; // Protects thread count and wait variables below. - pthread_mutex_t mThreadCountLock; + mutable pthread_mutex_t mThreadCountLock; // Broadcast whenever mWaitingForThreads > 0 pthread_cond_t mThreadCountDecrement; // Number of binder threads current executing a command. size_t mExecutingThreadsCount; // Number of threads calling IPCThreadState::blockUntilThreadAvailable() size_t mWaitingForThreads; - // Maximum number for binder threads allowed for this process. + // Maximum number of lazy threads to be started in the threadpool by the kernel. size_t mMaxThreads; + // Current number of threads inside the thread pool. + size_t mCurrentThreads; + // Current number of pooled threads inside the thread pool. + size_t mKernelStartedThreads; // Time when thread pool was emptied int64_t mStarvationStartTimeMs; diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 6b31812d17..2c99334fa4 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -18,6 +18,7 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcSession.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -28,6 +29,7 @@ namespace android { class FdTrigger; +class RpcServerTrusty; class RpcSocketAddress; /** @@ -114,6 +116,15 @@ public: void setProtocolVersion(uint32_t version); /** + * Set the supported transports for sending and receiving file descriptors. + * + * Clients will propose a mode when connecting. If the mode is not in the + * provided list, the connection will be rejected. + */ + void setSupportedFileDescriptorTransportModes( + const std::vector<RpcSession::FileDescriptorTransportMode>& modes); + + /** * The root object can be retrieved by any client, without any * authentication. TODO(b/183988761) * @@ -125,9 +136,17 @@ public: */ void setRootObjectWeak(const wp<IBinder>& binder); /** - * Allows a root object to be created for each session - */ - void setPerSessionRootObject(std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& object); + * Allows a root object to be created for each session. + * + * Takes one argument: a callable that is invoked once per new session. + * The callable takes two arguments: a type-erased pointer to an OS- and + * transport-specific address structure, e.g., sockaddr_vm for vsock, and + * an integer representing the size in bytes of that structure. The + * callable should validate the size, then cast the type-erased pointer + * to a pointer to the actual type of the address, e.g., const void* to + * const sockaddr_vm*. + */ + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object); sp<IBinder> getRootObject(); /** @@ -168,34 +187,47 @@ public: std::vector<sp<RpcSession>> listSessions(); size_t numUninitializedSessions(); + /** + * Whether any requests are currently being processed. + */ + bool hasActiveRequests(); + ~RpcServer(); private: + friend RpcServerTrusty; friend sp<RpcServer>; explicit RpcServer(std::unique_ptr<RpcTransportCtx> ctx); void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen); + static constexpr size_t kRpcAddressSize = 128; + static void establishConnection( + sp<RpcServer>&& server, RpcTransportFd clientFd, + std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen, + std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn); [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address); const std::unique_ptr<RpcTransportCtx> mCtx; size_t mMaxThreads = 1; std::optional<uint32_t> mProtocolVersion; - base::unique_fd mServer; // socket we are accepting sessions on + // A mode is supported if the N'th bit is on, where N is the mode enum's value. + std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set( + static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE)); + RpcTransportFd mServer; // socket we are accepting sessions on - std::mutex mLock; // for below - std::unique_ptr<std::thread> mJoinThread; + RpcMutex mLock; // for below + std::unique_ptr<RpcMaybeThread> mJoinThread; bool mJoinThreadRunning = false; - std::map<std::thread::id, std::thread> mConnectingThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mConnectingThreads; + sp<IBinder> mRootObject; wp<IBinder> mRootObjectWeak; - std::function<sp<IBinder>(const sockaddr*, socklen_t)> mRootObjectFactory; + std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory; std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions; std::unique_ptr<FdTrigger> mShutdownTrigger; - std::condition_variable mShutdownCv; + RpcConditionVariable mShutdownCv; }; } // namespace android diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index a5794428de..a25ba987c8 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,21 +15,23 @@ */ #pragma once +#include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> +#include <binder/RpcThreads.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <map> #include <optional> -#include <thread> #include <vector> namespace android { class Parcel; class RpcServer; +class RpcServerTrusty; class RpcSocketAddress; class RpcState; class RpcTransport; @@ -37,7 +39,13 @@ class FdTrigger; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000; -constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0; +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL; + +// Starting with this version: +// +// * RpcWireReply is larger (4 bytes -> 20). +// * RpcWireTransaction and RpcWireReplyV1 include the parcel data size. +constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1; /** * This represents a session (group of connections) between a client @@ -88,6 +96,18 @@ public: [[nodiscard]] bool setProtocolVersion(uint32_t version); std::optional<uint32_t> getProtocolVersion(); + enum class FileDescriptorTransportMode : uint8_t { + NONE = 0, + // Send file descriptors via unix domain socket ancillary data. + UNIX = 1, + }; + + /** + * Set the transport for sending and receiving file descriptors. + */ + void setFileDescriptorTransportMode(FileDescriptorTransportMode mode); + FileDescriptorTransportMode getFileDescriptorTransportMode(); + /** * This should be called once per thread, matching 'join' in the remote * process. @@ -169,6 +189,11 @@ public: */ [[nodiscard]] status_t sendDecStrong(const BpBinder* binder); + /** + * Whether any requests are currently being processed. + */ + bool hasActiveRequests(); + ~RpcSession(); /** @@ -183,9 +208,14 @@ public: private: friend sp<RpcSession>; friend RpcServer; + friend RpcServerTrusty; friend RpcState; explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx); + // internal version of setProtocolVersion that + // optionally skips the mStartedSetup check + [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted); + // for 'target', see RpcState::sendDecStrongToTarget [[nodiscard]] status_t sendDecStrongToTarget(uint64_t address, size_t target); @@ -199,10 +229,10 @@ private: public: void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; - void waitForShutdown(std::unique_lock<std::mutex>& lock, const sp<RpcSession>& session); + void waitForShutdown(RpcMutexUniqueLock& lock, const sp<RpcSession>& session); private: - std::condition_variable mCv; + RpcConditionVariable mCv; }; friend WaitForShutdownListener; @@ -211,7 +241,7 @@ private: // whether this or another thread is currently using this fd to make // or receive transactions. - std::optional<pid_t> exclusiveTid; + std::optional<uint64_t> exclusiveTid; bool allowNested = false; }; @@ -225,7 +255,7 @@ private: // // transfer ownership of thread (usually done while a lock is taken on the // structure which originally owns the thread) - void preJoinThreadOwnership(std::thread thread); + void preJoinThreadOwnership(RpcMaybeThread thread); // pass FD to thread and read initial connection information struct PreJoinSetupResult { // Server connection object associated with this @@ -244,7 +274,7 @@ private: [[nodiscard]] status_t setupOneSocketConnection(const RpcSocketAddress& address, const std::vector<uint8_t>& sessionId, bool incoming); - [[nodiscard]] status_t initAndAddConnection(base::unique_fd fd, + [[nodiscard]] status_t initAndAddConnection(RpcTransportFd fd, const std::vector<uint8_t>& sessionId, bool incoming); [[nodiscard]] status_t addIncomingConnection(std::unique_ptr<RpcTransport> rpcTransport); @@ -257,9 +287,15 @@ private: sp<RpcConnection> assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport); [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection); + void clearConnectionTid(const sp<RpcConnection>& connection); [[nodiscard]] status_t initShutdownTrigger(); + /** + * Checks whether any connection is active (Not polling on fd) + */ + bool hasActiveConnection(const std::vector<sp<RpcConnection>>& connections); + enum class ConnectionUse { CLIENT, CLIENT_ASYNC, @@ -276,7 +312,7 @@ private: const sp<RpcConnection>& get() { return mConnection; } private: - static void findConnection(pid_t tid, sp<RpcConnection>* exclusive, + static void findConnection(uint64_t tid, sp<RpcConnection>* exclusive, sp<RpcConnection>* available, std::vector<sp<RpcConnection>>& sockets, size_t socketsIndexHint); @@ -306,7 +342,7 @@ private: // For a more complicated case, the client might itself open up a thread to // serve calls to the server at all times (e.g. if it hosts a callback) - wp<RpcServer> mForServer; // maybe null, for client sessions + wp<RpcServer> mForServer; // maybe null, for client sessions sp<WaitForShutdownListener> mShutdownListener; // used for client sessions wp<EventListener> mEventListener; // mForServer if server, mShutdownListener if client @@ -320,13 +356,15 @@ private: std::unique_ptr<RpcState> mRpcBinderState; - std::mutex mMutex; // for all below + RpcMutex mMutex; // for all below + bool mStartedSetup = false; size_t mMaxIncomingThreads = 0; size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; std::optional<uint32_t> mProtocolVersion; + FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE; - std::condition_variable mAvailableConnectionCv; // for mWaitingThreads + RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads struct ThreadState { size_t mWaitingThreads = 0; @@ -335,7 +373,7 @@ private: std::vector<sp<RpcConnection>> mOutgoing; size_t mMaxIncoming = 0; std::vector<sp<RpcConnection>> mIncoming; - std::map<std::thread::id, std::thread> mThreads; + std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads; } mConnections; }; diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h new file mode 100644 index 0000000000..8abf04eaf0 --- /dev/null +++ b/libs/binder/include/binder/RpcThreads.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <pthread.h> + +#include <android-base/threads.h> + +#include <functional> +#include <memory> +#include <thread> + +namespace android { + +#ifdef BINDER_RPC_SINGLE_THREADED +class RpcMutex { +public: + void lock() {} + void unlock() {} +}; + +class RpcMutexUniqueLock { +public: + RpcMutexUniqueLock(RpcMutex&) {} + void unlock() {} +}; + +class RpcMutexLockGuard { +public: + RpcMutexLockGuard(RpcMutex&) {} +}; + +class RpcConditionVariable { +public: + void notify_one() {} + void notify_all() {} + + void wait(RpcMutexUniqueLock&) {} + + template <typename Predicate> + void wait(RpcMutexUniqueLock&, Predicate stop_waiting) { + LOG_ALWAYS_FATAL_IF(!stop_waiting(), "RpcConditionVariable::wait condition not met"); + } + + template <typename Duration> + std::cv_status wait_for(RpcMutexUniqueLock&, const Duration&) { + return std::cv_status::no_timeout; + } + + template <typename Duration, typename Predicate> + bool wait_for(RpcMutexUniqueLock&, const Duration&, Predicate stop_waiting) { + return stop_waiting(); + } +}; + +class RpcMaybeThread { +public: + RpcMaybeThread() = default; + + template <typename Function, typename... Args> + RpcMaybeThread(Function&& f, Args&&... args) { + // std::function requires a copy-constructible closure, + // so we need to wrap both the function and its arguments + // in a shared pointer that std::function can copy internally + struct Vars { + std::decay_t<Function> f; + std::tuple<std::decay_t<Args>...> args; + + explicit Vars(Function&& f, Args&&... args) + : f(std::move(f)), args(std::move(args)...) {} + }; + auto vars = std::make_shared<Vars>(std::forward<Function>(f), std::forward<Args>(args)...); + mFunc = [vars]() { std::apply(std::move(vars->f), std::move(vars->args)); }; + } + + void join() { + if (mFunc) { + // Move mFunc into a temporary so we can clear mFunc before + // executing the callback. This avoids infinite recursion if + // the callee then calls join() again directly or indirectly. + decltype(mFunc) func = nullptr; + mFunc.swap(func); + func(); + } + } + void detach() { join(); } + + class id { + public: + bool operator==(const id&) const { return true; } + bool operator!=(const id&) const { return false; } + bool operator<(const id&) const { return false; } + bool operator<=(const id&) const { return true; } + bool operator>(const id&) const { return false; } + bool operator>=(const id&) const { return true; } + }; + + id get_id() const { return id(); } + +private: + std::function<void(void)> mFunc; +}; + +namespace rpc_this_thread { +static inline RpcMaybeThread::id get_id() { + return RpcMaybeThread::id(); +} +} // namespace rpc_this_thread + +static inline uint64_t rpcGetThreadId() { + return 0; +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread& t) { + t.join(); +} +#else // BINDER_RPC_SINGLE_THREADED +using RpcMutex = std::mutex; +using RpcMutexUniqueLock = std::unique_lock<std::mutex>; +using RpcMutexLockGuard = std::lock_guard<std::mutex>; +using RpcConditionVariable = std::condition_variable; +using RpcMaybeThread = std::thread; +namespace rpc_this_thread = std::this_thread; + +static inline uint64_t rpcGetThreadId() { + return base::GetThreadId(); +} + +static inline void rpcJoinIfSingleThreaded(RpcMaybeThread&) {} +#endif // BINDER_RPC_SINGLE_THREADED + +} // namespace android diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index 751c4f905d..fd52a3a1a9 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -20,18 +20,24 @@ #include <functional> #include <memory> +#include <optional> #include <string> +#include <variant> +#include <vector> +#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> #include <binder/RpcCertificateFormat.h> +#include <binder/RpcThreads.h> #include <sys/uio.h> namespace android { class FdTrigger; +struct RpcTransportFd; // Represents a socket connection. // No thread-safety is guaranteed for these APIs. @@ -39,8 +45,15 @@ class RpcTransport { public: virtual ~RpcTransport() = default; - // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled. - [[nodiscard]] virtual status_t peek(void *buf, size_t size, size_t *out_size) = 0; + /** + * Poll the transport to check whether there is any data ready to read. + * + * Return: + * OK - There is data available on this transport + * WOULDBLOCK - No data is available + * error - any other error + */ + [[nodiscard]] virtual status_t pollRead(void) = 0; /** * Read (or write), but allow to be interrupted by a trigger. @@ -52,16 +65,32 @@ public: * to read/write data. If this returns an error, that error is returned from * this function. * + * ancillaryFds - FDs to be sent via UNIX domain dockets or Trusty IPC. When + * reading, if `ancillaryFds` is null, any received FDs will be silently + * dropped and closed (by the OS). Appended values will always be unique_fd, + * the variant type is used to avoid extra copies elsewhere. + * * Return: * OK - succeeded in completely processing 'size' * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::function<status_t()> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::function<status_t()> &altPoll) = 0; + const std::optional<android::base::function_ref<status_t()>> &altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + + /** + * Check whether any threads are blocked while polling the transport + * for read operations + * Return: + * True - Specifies that there is active polling on transport. + * False - No active polling on transport + */ + [[nodiscard]] virtual bool isWaiting() = 0; protected: RpcTransport() = default; @@ -78,7 +107,7 @@ public: // Implementation details: for TLS, this function may incur I/O. |fdTrigger| may be used // to interrupt I/O. This function blocks until handshake is finished. [[nodiscard]] virtual std::unique_ptr<RpcTransport> newTransport( - android::base::unique_fd fd, FdTrigger *fdTrigger) const = 0; + android::RpcTransportFd fd, FdTrigger *fdTrigger) const = 0; // Return the preconfigured certificate of this context. // @@ -111,4 +140,36 @@ protected: RpcTransportCtxFactory() = default; }; +struct RpcTransportFd { +private: + mutable bool isPolling{false}; + + void setPollingState(bool state) const { isPolling = state; } + +public: + base::unique_fd fd; + + RpcTransportFd() = default; + explicit RpcTransportFd(base::unique_fd &&descriptor) + : isPolling(false), fd(std::move(descriptor)) {} + + RpcTransportFd(RpcTransportFd &&transportFd) noexcept + : isPolling(transportFd.isPolling), fd(std::move(transportFd.fd)) {} + + RpcTransportFd &operator=(RpcTransportFd &&transportFd) noexcept { + fd = std::move(transportFd.fd); + isPolling = transportFd.isPolling; + return *this; + } + + RpcTransportFd &operator=(base::unique_fd &&descriptor) noexcept { + fd = std::move(descriptor); + isPolling = false; + return *this; + } + + bool isInPollingState() const { return isPolling; } + friend class FdTrigger; +}; + } // namespace android diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h new file mode 100644 index 0000000000..99378428ad --- /dev/null +++ b/libs/binder/include/binder/Trace.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cutils/trace.h> +#include <stdint.h> + +namespace android { +namespace binder { + +// Trampoline functions allowing generated aidls to trace binder transactions without depending on +// libcutils/libutils +void atrace_begin(uint64_t tag, const char* name); +void atrace_end(uint64_t tag); + +class ScopedTrace { +public: + inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); } + + inline ~ScopedTrace() { atrace_end(mTag); } + +private: + uint64_t mTag; +}; + +} // namespace binder +} // namespace android diff --git a/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h new file mode 100644 index 0000000000..4a4172a3a7 --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTransportTipcAndroid.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Wraps the transport layer of RPC. Implementation uses Trusty IPC. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory for writing Trusty IPC clients in Android. +class RpcTransportCtxFactoryTipcAndroid : public RpcTransportCtxFactory { +public: + static std::unique_ptr<RpcTransportCtxFactory> make(); + + std::unique_ptr<RpcTransportCtx> newServerCtx() const override; + std::unique_ptr<RpcTransportCtx> newClientCtx() const override; + const char* toCString() const override; + +private: + RpcTransportCtxFactoryTipcAndroid() = default; +}; + +} // namespace android diff --git a/libs/binder/include_trusty/binder/RpcTrusty.h b/libs/binder/include_trusty/binder/RpcTrusty.h new file mode 100644 index 0000000000..f124e0c824 --- /dev/null +++ b/libs/binder/include_trusty/binder/RpcTrusty.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> + +namespace android { + +sp<IBinder> RpcTrustyConnect(const char* device, const char* port); + +} // namespace android diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index bf2b25b265..a3d42b76fe 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -38,10 +38,10 @@ bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* contex << " error: " << statusToString(status).c_str(); return false; } - server->setPerSessionRootObject([=](const sockaddr* addr, socklen_t addrlen) { - LOG_ALWAYS_FATAL_IF(addr->sa_family != AF_VSOCK, "address is not a vsock"); + server->setPerSessionRootObject([=](const void* addr, size_t addrlen) { LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated"); const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr); + LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock"); return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext)); }); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 79c8c8f88b..33d28e65c5 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -57,6 +57,7 @@ cc_library { srcs: [ "ibinder.cpp", "ibinder_jni.cpp", + "libbinder.cpp", "parcel.cpp", "parcel_jni.cpp", "process.cpp", @@ -113,6 +114,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 28e3ff43ff..28d1f16d64 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -14,21 +14,19 @@ * limitations under the License. */ +#include <android-base/logging.h> #include <android/binder_ibinder.h> #include <android/binder_ibinder_platform.h> -#include <android/binder_libbinder.h> -#include "ibinder_internal.h" - #include <android/binder_stability.h> #include <android/binder_status.h> -#include "parcel_internal.h" -#include "status_internal.h" - -#include <android-base/logging.h> #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> #include <private/android_filesystem_config.h> +#include "ibinder_internal.h" +#include "parcel_internal.h" +#include "status_internal.h" + using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; @@ -36,6 +34,7 @@ using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; using ::android::wp; @@ -63,6 +62,9 @@ struct Value { wp<ABpBinder> binder; }; void clean(const void* id, void* obj, void* cookie) { + // be weary of leaks! + // LOG(INFO) << "Deleting an ABpBinder"; + CHECK(id == kId) << id << " " << obj << " " << cookie; delete static_cast<Value*>(obj); @@ -133,7 +135,8 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder."; + << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) + << "'."; } return false; } @@ -237,26 +240,11 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce } ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) - : AIBinder(nullptr /*clazz*/), BpRefBase(binder) { + : AIBinder(nullptr /*clazz*/), mRemote(binder) { CHECK(binder != nullptr); } ABpBinder::~ABpBinder() {} -void ABpBinder::onLastStrongRef(const void* id) { - // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for - // the ABpBinder to be deleted. Even though we have no more references on the ABpBinder - // (BpRefBase), the remote object may still exist (for instance, if we - // receive it from another process, before the ABpBinder is attached). - - ABpBinderTag::Value* value = - static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId)); - CHECK_NE(nullptr, value) << "ABpBinder must always be attached"; - - remote()->withLock([&]() { value->binder = nullptr; }); - - BpRefBase::onLastStrongRef(id); -} - sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) { if (binder == nullptr) { return nullptr; @@ -458,7 +446,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { LOG(ERROR) << __func__ - << ": removed reference to death recipient but unlink failed."; + << ": removed reference to death recipient but unlink failed: " + << statusToString(status); } return PruneStatusT(status); } @@ -539,7 +528,8 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -550,7 +540,8 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -625,7 +616,8 @@ void* AIBinder_getUserData(AIBinder* binder) { binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder + << ") and in (" << in << ")."; return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); @@ -671,7 +663,9 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { - LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK."; + LOG(ERROR) << __func__ + << ": Only user-defined transactions can be made from the NDK, but requested: " + << code; return STATUS_UNKNOWN_TRANSACTION; } @@ -682,7 +676,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" + << in << "), and out (" << out << ")."; return STATUS_UNEXPECTED_NULL; } @@ -785,17 +780,6 @@ const char* AIBinder_getCallingSid() { return ::android::IPCThreadState::self()->getCallingSid(); } -android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) { - if (binder == nullptr) return nullptr; - return binder->getBinder(); -} - -AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) { - sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder); - AIBinder_incStrong(ndkBinder.get()); - return ndkBinder.get(); -} - void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) { binder->asABBinder()->setMinSchedulerPolicy(policy, priority); } diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 730e51b3e3..d7098e85bf 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -91,7 +91,7 @@ struct ABBinder : public AIBinder, public ::android::BBinder { // This binder object may be remote or local (even though it is 'Bp'). The implication if it is // local is that it is an IBinder object created outside of the domain of libbinder_ndk. -struct ABpBinder : public AIBinder, public ::android::BpRefBase { +struct ABpBinder : public AIBinder { // Looks up to see if this object has or is an existing ABBinder or ABpBinder object, otherwise // it creates an ABpBinder object. static ::android::sp<AIBinder> lookupOrCreateFromBinder( @@ -99,14 +99,13 @@ struct ABpBinder : public AIBinder, public ::android::BpRefBase { virtual ~ABpBinder(); - void onLastStrongRef(const void* id) override; - - ::android::sp<::android::IBinder> getBinder() override { return remote(); } + ::android::sp<::android::IBinder> getBinder() override { return mRemote; } ABpBinder* asABpBinder() override { return this; } private: friend android::sp<ABpBinder>; explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); + ::android::sp<::android::IBinder> mRemote; }; struct AIBinder_Class { 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 c8e78fc55d..78bcb432ab 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -43,10 +43,12 @@ namespace ndk { /** - * analog using std::shared_ptr for internally held refcount + * Binder analog to using std::shared_ptr for an internally held refcount. * * ref must be called at least one time during the lifetime of this object. The recommended way to * construct this object is with SharedRefBase::make. + * + * If you need a "this" shared reference analogous to shared_from_this, use this->ref(). */ class SharedRefBase { public: diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index 0bf1e3dba9..95eee26eed 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -1639,7 +1639,6 @@ static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) return AParcel_writeParcelable(parcel, value); } else { static_assert(dependent_false_v<T>, "unrecognized type"); - return STATUS_OK; } } @@ -1707,7 +1706,6 @@ static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) return AParcel_readParcelable(parcel, value); } else { static_assert(dependent_false_v<T>, "unrecognized type"); - return STATUS_OK; } } diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index f45aa7631b..c1f2620770 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -58,6 +58,9 @@ class AParcelableHolder { #endif AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, AParcel_getDataSize(other.mParcel.get())); + } else { + syslog(LOG_ERR, + "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); } } #endif @@ -192,6 +195,9 @@ class AParcelableHolder { if (__ANDROID_API__ >= 31) { #endif AParcel_reset(mParcel.get()); + } else { + syslog(LOG_ERR, + "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); } } @@ -201,6 +207,29 @@ class AParcelableHolder { inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; } inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; } inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; } +#if __ANDROID_API__ >= 31 + inline AParcelableHolder& operator=(const AParcelableHolder& rhs) { + // AParcelableHolder has been introduced in 31. +#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ + if (__builtin_available(android 31, *)) { +#else + if (__ANDROID_API__ >= 31) { +#endif + this->reset(); + if (this->mStability != rhs.mStability) { + syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!", + this->mStability, rhs.mStability); + abort(); + } + AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(rhs.mParcel.get())); + } else { + syslog(LOG_ERR, + "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!"); + } + return *this; + } +#endif private: mutable ndk::ScopedAParcel mParcel; diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index ef71a81328..d7840ec1f0 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -162,7 +162,12 @@ std::string ToString(const _T& t) { } else if constexpr (std::is_same_v<bool, _T>) { return t ? "true" : "false"; } else if constexpr (std::is_same_v<char16_t, _T>) { + // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the + // warnings. There's no replacement in the standard library yet. + _Pragma("clang diagnostic push") + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\""); return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(t); + _Pragma("clang diagnostic pop"); } else if constexpr (std::is_arithmetic_v<_T>) { return std::to_string(t); } else if constexpr (std::is_same_v<std::string, _T>) { diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index 6880d86e1a..8e288b307e 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -56,6 +56,12 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en * If the binder is null, null is returned. If this binder object was originally an IBinder object, * the original java object will be returned. * + * WARNING: this function returns global and local references. This can be + * figured out using GetObjectRefType. Though, when this function is called + * from within a Java context, the local ref will automatically be cleaned + * up. If this is called outside of a Java frame, + * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. + * * Available since API level 29. * * \param env Java environment. Must not be null. diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 84575811f0..f68612c3ba 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -59,6 +59,11 @@ void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); /** * Sets the position within the parcel. * + * This must be called with a position that has been previously returned from + * AParcel_getDataPosition. If writes are made after setting the data position, they must + * be made in the exact same sequence used before resetting data position. Writing over + * objects such as binders or file descriptors is not supported. + * * Available since API level 29. * * \param parcel The parcel of which to set the position. diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h index f0c00e87e6..dfe12a1cf7 100644 --- a/libs/binder/ndk/include_platform/android/binder_libbinder.h +++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h @@ -19,7 +19,9 @@ #if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) #include <android/binder_ibinder.h> +#include <android/binder_parcel.h> #include <binder/IBinder.h> +#include <binder/Parcel.h> /** * Get libbinder version of binder from AIBinder. @@ -47,4 +49,26 @@ android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder); */ AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder); +/** + * View libbinder version of parcel from AParcel (mutable). + * + * The lifetime of the returned parcel is the lifetime of the input AParcel. + * Do not ues this reference after dropping the AParcel. + * + * \param parcel non-null parcel with ownership retained by client + * \return platform parcel object + */ +android::Parcel* AParcel_viewPlatformParcel(AParcel* parcel); + +/** + * View libbinder version of parcel from AParcel (const version). + * + * The lifetime of the returned parcel is the lifetime of the input AParcel. + * Do not ues this reference after dropping the AParcel. + * + * \param parcel non-null parcel with ownership retained by client + * \return platform parcel object + */ +const android::Parcel* AParcel_viewPlatformParcel(const AParcel* parcel); + #endif diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index d0cd11f6fc..683a43372b 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -98,9 +98,9 @@ static inline void AIBinder_forceDowngradeToLocalStability(AIBinder* binder) { * This interface has system<->vendor stability */ // b/227835797 - can't use __INTRODUCED_IN(30) because old targets load this code -#if __ANDROID_MIN_SDK_VERSION__ < 30 +#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 __attribute__((weak)) -#endif // __ANDROID_MIN_SDK_VERSION__ < 30 +#endif // defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 void AIBinder_markVintfStability(AIBinder* binder); __END_DECLS diff --git a/libs/binder/ndk/libbinder.cpp b/libs/binder/ndk/libbinder.cpp new file mode 100644 index 0000000000..f94d81d8fb --- /dev/null +++ b/libs/binder/ndk/libbinder.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_libbinder.h> + +#include "ibinder_internal.h" +#include "parcel_internal.h" + +using ::android::IBinder; +using ::android::Parcel; +using ::android::sp; + +sp<IBinder> AIBinder_toPlatformBinder(AIBinder* binder) { + if (binder == nullptr) return nullptr; + return binder->getBinder(); +} + +AIBinder* AIBinder_fromPlatformBinder(const sp<IBinder>& binder) { + sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ndkBinder.get()); + return ndkBinder.get(); +} + +Parcel* AParcel_viewPlatformParcel(AParcel* parcel) { + return parcel->get(); +} + +const Parcel* AParcel_viewPlatformParcel(const AParcel* parcel) { + return parcel->get(); +} diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 3824a1b44c..259a73618b 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -89,12 +89,12 @@ LIBBINDER_NDK { # introduced=29 AStatus_getStatus; AStatus_isOk; AStatus_newOk; - ABinderProcess_joinThreadPool; # apex llndk - ABinderProcess_setThreadPoolMaxThreadCount; # apex llndk - ABinderProcess_startThreadPool; # apex llndk - AServiceManager_addService; # apex llndk - AServiceManager_checkService; # apex llndk - AServiceManager_getService; # apex llndk + ABinderProcess_joinThreadPool; # systemapi llndk + ABinderProcess_setThreadPoolMaxThreadCount; # systemapi llndk + ABinderProcess_startThreadPool; # systemapi llndk + AServiceManager_addService; # systemapi llndk + AServiceManager_checkService; # systemapi llndk + AServiceManager_getService; # systemapi llndk }; LIBBINDER_NDK30 { # introduced=30 @@ -105,30 +105,30 @@ LIBBINDER_NDK30 { # introduced=30 AStatus_deleteDescription; AParcel_fromJavaParcel; - AIBinder_markSystemStability; # apex + AIBinder_markSystemStability; # systemapi AIBinder_markVendorStability; # llndk - AIBinder_markVintfStability; # apex llndk - AIBinder_Class_setHandleShellCommand; # apex llndk + AIBinder_markVintfStability; # systemapi llndk + AIBinder_Class_setHandleShellCommand; # systemapi llndk }; LIBBINDER_NDK31 { # introduced=31 global: - ABinderProcess_handlePolledCommands; # apex - ABinderProcess_setupPolling; # apex - AIBinder_getCallingSid; # apex - AIBinder_setRequestingSid; # apex + ABinderProcess_handlePolledCommands; # systemapi + ABinderProcess_setupPolling; # systemapi + AIBinder_getCallingSid; # systemapi + AIBinder_setRequestingSid; # systemapi AParcel_markSensitive; # systemapi llndk - AServiceManager_forEachDeclaredInstance; # apex llndk - AServiceManager_forceLazyServicesPersist; # apex llndk - AServiceManager_isDeclared; # apex llndk - AServiceManager_isUpdatableViaApex; # apex + AServiceManager_forEachDeclaredInstance; # systemapi llndk + AServiceManager_forceLazyServicesPersist; # systemapi llndk + AServiceManager_isDeclared; # systemapi llndk + AServiceManager_isUpdatableViaApex; # systemapi AServiceManager_reRegister; # llndk - AServiceManager_registerLazyService; # apex llndk + AServiceManager_registerLazyService; # systemapi llndk AServiceManager_setActiveServicesCallback; # llndk AServiceManager_tryUnregister; # llndk - AServiceManager_waitForService; # apex llndk + AServiceManager_waitForService; # systemapi llndk - AIBinder_forceDowngradeToSystemStability; # apex + AIBinder_forceDowngradeToSystemStability; # systemapi AIBinder_forceDowngradeToVendorStability; # llndk AIBinder_Class_getDescriptor; @@ -146,8 +146,8 @@ LIBBINDER_NDK33 { # introduced=33 AIBinder_Class_disableInterfaceTokenHeader; AIBinder_DeathRecipient_setOnUnlinked; AIBinder_isHandlingTransaction; - AIBinder_setInheritRt; # llndk - AIBinder_setMinSchedulerPolicy; # llndk + AIBinder_setInheritRt; # systemapi llndk + AIBinder_setMinSchedulerPolicy; # systemapi llndk AParcel_marshal; AParcel_unmarshal; }; @@ -158,6 +158,7 @@ LIBBINDER_NDK_PLATFORM { extern "C++" { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; + AParcel_viewPlatformParcel*; }; local: *; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 1b136dcb8e..6d29238758 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -661,6 +661,15 @@ TEST(NdkBinder, ConvertToPlatformBinder) { } } +TEST(NdkBinder, ConvertToPlatformParcel) { + ndk::ScopedAParcel parcel = ndk::ScopedAParcel(AParcel_create()); + EXPECT_EQ(OK, AParcel_writeInt32(parcel.get(), 42)); + + android::Parcel* pparcel = AParcel_viewPlatformParcel(parcel.get()); + pparcel->setDataPosition(0); + EXPECT_EQ(42, pparcel->readInt32()); +} + class MyResultReceiver : public BnResultReceiver { public: Mutex mMutex; diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 355b3b4b89..0ec618352a 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -15,9 +15,10 @@ rust_library { "libutils", ], rustlibs: [ - "liblibc", "libbinder_ndk_sys", "libdowncast_rs", + "liblazy_static", + "liblibc", ], host_supported: true, vendor_available: true, @@ -143,23 +144,6 @@ rust_bindgen { min_sdk_version: "Tiramisu", } -// TODO(b/184872979): remove once the Rust API is created. -rust_bindgen { - name: "libbinder_rpc_unstable_bindgen", - wrapper_src: ":libbinder_rpc_unstable_header", - crate_name: "binder_rpc_unstable_bindgen", - source_stem: "bindings", - shared_libs: [ - "libutils", - ], - apex_available: [ - "com.android.compos", - "com.android.uwb", - "com.android.virt", - ], - min_sdk_version: "Tiramisu", -} - rust_test { name: "libbinder_rs-internal_test", crate_name: "binder", @@ -170,9 +154,10 @@ rust_test { "libbinder_ndk", ], rustlibs: [ - "liblibc", "libbinder_ndk_sys", "libdowncast_rs", + "liblazy_static", + "liblibc", ], } @@ -185,13 +170,3 @@ rust_test { clippy_lints: "none", lints: "none", } - -rust_test { - name: "libbinder_rpc_unstable_bindgen_test", - srcs: [":libbinder_rpc_unstable_bindgen"], - crate_name: "binder_rpc_unstable_bindgen", - test_suites: ["general-tests"], - auto_gen_config: true, - clippy_lints: "none", - lints: "none", -} diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 9dcef4260e..2d2bf7c582 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -28,22 +28,22 @@ //! //! [`Tokio`]: crate::Tokio -use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong}; use binder::binder_impl::BinderAsyncRuntime; +use binder::{BinderAsyncPool, BoxFuture, FromIBinder, StatusCode, Strong}; use std::future::Future; /// Retrieve an existing service for a particular interface, sleeping for a few /// seconds if it doesn't yet exist. -pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { +pub async fn get_interface<T: FromIBinder + ?Sized + 'static>( + name: &str, +) -> Result<Strong<T>, StatusCode> { if binder::is_handling_transaction() { // See comment in the BinderAsyncPool impl. return binder::get_interface::<T>(name); } let name = name.to_string(); - let res = tokio::task::spawn_blocking(move || { - binder::get_interface::<T>(&name) - }).await; + let res = tokio::task::spawn_blocking(move || binder::get_interface::<T>(&name)).await; // The `is_panic` branch is not actually reachable in Android as we compile // with `panic = abort`. @@ -58,16 +58,16 @@ pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Res /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. -pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { +pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>( + name: &str, +) -> Result<Strong<T>, StatusCode> { if binder::is_handling_transaction() { // See comment in the BinderAsyncPool impl. return binder::wait_for_interface::<T>(name); } let name = name.to_string(); - let res = tokio::task::spawn_blocking(move || { - binder::wait_for_interface::<T>(&name) - }).await; + let res = tokio::task::spawn_blocking(move || binder::wait_for_interface::<T>(&name)).await; // The `is_panic` branch is not actually reachable in Android as we compile // with `panic = abort`. diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp new file mode 100644 index 0000000000..f16939036c --- /dev/null +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -0,0 +1,59 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_library { + name: "librpcbinder_rs", + crate_name: "rpcbinder", + srcs: ["src/lib.rs"], + shared_libs: [ + "libutils", + ], + rustlibs: [ + "libbinder_ndk_sys", + "libbinder_rpc_unstable_bindgen", + "libbinder_rs", + "libdowncast_rs", + "liblibc", + ], + apex_available: [ + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +// TODO(b/184872979): remove once the RPC Binder API is stabilised. +rust_bindgen { + name: "libbinder_rpc_unstable_bindgen", + wrapper_src: ":libbinder_rpc_unstable_header", + crate_name: "binder_rpc_unstable_bindgen", + visibility: [":__subpackages__"], + source_stem: "bindings", + shared_libs: [ + "libbinder_rpc_unstable", + "libutils", + ], + apex_available: [ + "com.android.compos", + "com.android.uwb", + "com.android.virt", + ], + min_sdk_version: "Tiramisu", +} + +rust_test { + name: "libbinder_rpc_unstable_bindgen_test", + srcs: [":libbinder_rpc_unstable_bindgen"], + crate_name: "binder_rpc_unstable_bindgen", + test_suites: ["general-tests"], + auto_gen_config: true, + clippy_lints: "none", + lints: "none", +} diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs new file mode 100644 index 0000000000..dfc6f06b14 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/client.rs @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use binder::{ + unstable_api::{new_spibinder, AIBinder}, + FromIBinder, SpIBinder, StatusCode, Strong, +}; +use std::os::{ + raw::{c_int, c_void}, + unix::io::RawFd, +}; + +/// Connects to an RPC Binder server over vsock. +pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> { + // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can + // safely be taken by new_spibinder. + unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port) as *mut AIBinder) } +} + +/// Connects to an RPC Binder server for a particular interface over vsock. +pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>( + cid: u32, + port: u32, +) -> Result<Strong<T>, StatusCode> { + interface_cast(get_vsock_rpc_service(cid, port)) +} + +/// Connects to an RPC Binder server, using the given callback to get (and take ownership of) +/// file descriptors already connected to it. +pub fn get_preconnected_rpc_service( + mut request_fd: impl FnMut() -> Option<RawFd>, +) -> Option<SpIBinder> { + // Double reference the factory because trait objects aren't FFI safe. + let mut request_fd_ref: RequestFd = &mut request_fd; + let param = &mut request_fd_ref as *mut RequestFd as *mut c_void; + + // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the + // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership + // of param, only passing it to request_fd_wrapper. + unsafe { + new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient( + Some(request_fd_wrapper), + param, + ) as *mut AIBinder) + } +} + +type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>; + +unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int { + // 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(); + if let Some(fd) = request_fd() { + fd + } else { + -1 + } +} + +/// Connects to an RPC Binder server for a particular interface, using the given callback to get +/// (and take ownership of) file descriptors already connected to it. +pub fn get_preconnected_rpc_interface<T: FromIBinder + ?Sized>( + request_fd: impl FnMut() -> Option<RawFd>, +) -> Result<Strong<T>, StatusCode> { + interface_cast(get_preconnected_rpc_service(request_fd)) +} + +fn interface_cast<T: FromIBinder + ?Sized>( + service: Option<SpIBinder>, +) -> Result<Strong<T>, StatusCode> { + if let Some(service) = service { + FromIBinder::try_from(service) + } else { + Err(StatusCode::NAME_NOT_FOUND) + } +} diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs new file mode 100644 index 0000000000..a5eea61798 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! API for RPC Binder services. + +mod client; +mod server; + +pub use client::{ + get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface, + get_vsock_rpc_service, +}; +pub use server::{run_rpc_server, run_rpc_server_with_factory}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs new file mode 100644 index 0000000000..d98a439da4 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use binder::{unstable_api::AsNative, SpIBinder}; +use std::{os::raw, ptr::null_mut}; + +/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock +/// port. +/// +/// If and when the server is ready for connections (it is listening on the port), `on_ready` is +/// called to allow appropriate action to be taken - e.g. to notify clients that they may now +/// attempt to connect. +/// +/// The current thread is joined to the binder thread pool to handle incoming messages. +/// +/// Returns true if the server has shutdown normally, false if it failed in some way. +pub fn run_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool +where + F: FnOnce(), +{ + let mut ready_notifier = ReadyNotifier(Some(on_ready)); + ready_notifier.run_server(service, port) +} + +struct ReadyNotifier<F>(Option<F>) +where + F: FnOnce(); + +impl<F> ReadyNotifier<F> +where + F: FnOnce(), +{ + fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool { + let service = service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder; + let param = self.as_void_ptr(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // RunRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only + // uses them before it returns, which is during the lifetime of `self`. + unsafe { + binder_rpc_unstable_bindgen::RunRpcServerCallback( + service, + port, + Some(Self::ready_callback), + param, + ) + } + } + + fn as_void_ptr(&mut self) -> *mut raw::c_void { + self as *mut _ as *mut raw::c_void + } + + unsafe extern "C" fn ready_callback(param: *mut raw::c_void) { + // SAFETY: This is only ever called by `RunRpcServerCallback`, within the lifetime of the + // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly + // aligned non-null pointer to an initialized instance). + let ready_notifier = param as *mut Self; + ready_notifier.as_mut().unwrap().notify() + } + + fn notify(&mut self) { + if let Some(on_ready) = self.0.take() { + on_ready(); + } + } +} + +type RpcServerFactoryRef<'a> = &'a mut (dyn FnMut(u32) -> Option<SpIBinder> + Send + Sync); + +/// Runs a binder RPC server, using the given factory function to construct a binder service +/// implementation for each connection. +/// +/// The current thread is joined to the binder thread pool to handle incoming messages. +/// +/// Returns true if the server has shutdown normally, false if it failed in some way. +pub fn run_rpc_server_with_factory( + port: u32, + mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync, +) -> bool { + // Double reference the factory because trait objects aren't FFI safe. + // NB: The type annotation is necessary to ensure that we have a `dyn` rather than an `impl`. + let mut factory_ref: RpcServerFactoryRef = &mut factory; + let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void; + + // SAFETY: `factory_wrapper` is only ever called by `RunRpcServerWithFactory`, with context + // taking the pointer value above (so a properly aligned non-null pointer to an initialized + // `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made + // after `RunRpcServerWithFactory` returns). + unsafe { + binder_rpc_unstable_bindgen::RunRpcServerWithFactory(Some(factory_wrapper), context, port) + } +} + +unsafe extern "C" fn factory_wrapper( + cid: u32, + context: *mut raw::c_void, +) -> *mut binder_rpc_unstable_bindgen::AIBinder { + // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by + // `run_rpc_server_with_factory`, and we are still within the lifetime of the value it is + // pointing to. + let factory_ptr = context as *mut RpcServerFactoryRef; + let factory = factory_ptr.as_mut().unwrap(); + + if let Some(mut service) = factory(cid) { + service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder + } else { + null_mut() + } +} diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 5d6206de0b..63c8684fa0 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -17,7 +17,7 @@ //! Trait definitions for binder objects use crate::error::{status_t, Result, StatusCode}; -use crate::parcel::{Parcel, BorrowedParcel}; +use crate::parcel::{BorrowedParcel, Parcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; @@ -133,7 +133,7 @@ impl TryFrom<i32> for Stability { match stability { 0 => Ok(Local), 1 => Ok(Vintf), - _ => Err(StatusCode::BAD_VALUE) + _ => Err(StatusCode::BAD_VALUE), } } } @@ -158,7 +158,12 @@ pub trait Remotable: Send + Sync { /// Handle and reply to a request to invoke a transaction on this object. /// /// `reply` may be [`None`] if the sender does not expect a reply. - fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>; + fn on_transact( + &self, + code: TransactionCode, + data: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, + ) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. @@ -454,28 +459,19 @@ impl<I: FromIBinder + ?Sized> Weak<I> { /// Construct a new weak reference from a strong reference fn new(binder: &Strong<I>) -> Self { let weak_binder = binder.as_binder().downgrade(); - Weak { - weak_binder, - interface_type: PhantomData, - } + Weak { weak_binder, interface_type: PhantomData } } /// Upgrade this weak reference to a strong reference if the binder object /// is still alive pub fn upgrade(&self) -> Result<Strong<I>> { - self.weak_binder - .promote() - .ok_or(StatusCode::DEAD_OBJECT) - .and_then(FromIBinder::try_from) + self.weak_binder.promote().ok_or(StatusCode::DEAD_OBJECT).and_then(FromIBinder::try_from) } } impl<I: FromIBinder + ?Sized> Clone for Weak<I> { fn clone(&self) -> Self { - Self { - weak_binder: self.weak_binder.clone(), - interface_type: PhantomData, - } + Self { weak_binder: self.weak_binder.clone(), interface_type: PhantomData } } } @@ -614,7 +610,12 @@ pub trait InterfaceClassMethods { /// 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. - unsafe extern "C" fn on_dump(binder: *mut sys::AIBinder, fd: i32, args: *mut *const c_char, num_args: u32) -> status_t; + unsafe extern "C" fn on_dump( + binder: *mut sys::AIBinder, + fd: i32, + args: *mut *const c_char, + num_args: u32, + ) -> status_t; } /// Interface for transforming a generic SpIBinder into a specific remote diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs index 579f9f90b8..d90acafaeb 100644 --- a/libs/binder/rust/src/binder_async.rs +++ b/libs/binder/rust/src/binder_async.rs @@ -41,7 +41,10 @@ pub trait BinderAsyncPool { /// boxed `Future` trait object, and including `after_spawn` in the trait function /// allows the caller to avoid double-boxing if they want to do anything to the value /// returned from the spawned thread. - fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>> + fn spawn<'a, F1, F2, Fut, A, B, E>( + spawn_me: F1, + after_spawn: F2, + ) -> BoxFuture<'a, Result<B, E>> where F1: FnOnce() -> A, F2: FnOnce(A) -> Fut, diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs index 2598ebc804..f6b09ed8fe 100644 --- a/libs/binder/rust/src/error.rs +++ b/libs/binder/rust/src/error.rs @@ -18,7 +18,7 @@ use crate::binder::AsNative; use crate::sys; use std::error; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::result; @@ -104,6 +104,10 @@ unsafe impl Sync for Status {} // A thread-local `AStatus` would not be valid. unsafe impl Send for Status {} +fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> { + CString::new(message.as_ref()).ok() +} + impl Status { /// Create a status object representing a successful transaction. pub fn ok() -> Self { @@ -146,6 +150,11 @@ impl Status { Self(ptr) } + /// Creates a status object from a service specific error. + pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status { + Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref()) + } + /// Create a status object from an exception code pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status { if let Some(message) = message { @@ -158,6 +167,14 @@ impl Status { } } + /// Creates a status object from an exception code and message. + pub fn new_exception_str<T: AsRef<str>>( + exception: ExceptionCode, + message: Option<T>, + ) -> Status { + Self::new_exception(exception, message.and_then(to_cstring).as_deref()) + } + /// Create a status object from a raw `AStatus` pointer. /// /// # Safety @@ -371,3 +388,41 @@ unsafe impl AsNative<sys::AStatus> for Status { self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn make_service_specific_error() { + let status = Status::new_service_specific_error_str(-42, Some("message")); + + assert!(!status.is_ok()); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: message'".to_string() + ); + } + + #[test] + fn make_exception() { + let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message")); + + assert!(!status.is_ok()); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): 'message'".to_string()); + } + + #[test] + fn make_exception_null() { + let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two")); + + assert!(!status.is_ok()); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string()); + } +} diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index dbfb1c20a5..195d9ac160 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -106,11 +106,12 @@ mod state; use binder_ndk_sys as sys; -pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; +pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, Status, StatusCode}; pub use native::{ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, + LazyServiceGuard, }; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; pub use proxy::{ diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index b7c7ae4b54..3a6daddffd 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -22,6 +22,7 @@ use crate::parcel::{BorrowedParcel, Serialize}; use crate::proxy::SpIBinder; use crate::sys; +use lazy_static::lazy_static; use std::convert::TryFrom; use std::ffi::{c_void, CStr, CString}; use std::fs::File; @@ -30,6 +31,7 @@ use std::ops::Deref; use std::os::raw::c_char; use std::os::unix::io::FromRawFd; use std::slice; +use std::sync::Mutex; /// Rust wrapper around Binder remotable objects. /// @@ -97,10 +99,7 @@ impl<T: Remotable> Binder<T> { // ends. sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; - let mut binder = Binder { - ibinder, - rust_object, - }; + let mut binder = Binder { ibinder, rust_object }; binder.mark_stability(stability); binder } @@ -335,11 +334,18 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // We don't own this file, so we need to be careful not to drop it. let file = ManuallyDrop::new(File::from_raw_fd(fd)); - if args.is_null() { + if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; } - let args = slice::from_raw_parts(args, num_args as usize); - let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); + + let args = if args.is_null() || num_args == 0 { + vec![] + } else { + slice::from_raw_parts(args, num_args as usize) + .iter() + .map(|s| CStr::from_ptr(*s)) + .collect() + }; let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); @@ -413,10 +419,7 @@ impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> { // We are transferring the ownership of the AIBinder into the new Binder // object. let mut ibinder = ManuallyDrop::new(ibinder); - Ok(Binder { - ibinder: ibinder.as_native_mut(), - rust_object: userdata as *mut B, - }) + Ok(Binder { ibinder: ibinder.as_native_mut(), rust_object: userdata as *mut B }) } } @@ -486,6 +489,8 @@ pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result< /// If persist is true then shut down will be blocked until this function is called again with /// persist false. If this is to be the initial state, call this function before calling /// register_lazy_service. +/// +/// 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. @@ -493,6 +498,59 @@ pub fn force_lazy_services_persist(persist: bool) { } } +/// An RAII object to ensure a process which registers lazy services is not killed. During the +/// lifetime of any of these objects the service manager will not not kill the process even if none +/// of its lazy services are in use. +#[must_use] +#[derive(Debug)] +pub struct LazyServiceGuard { + // Prevent construction outside this module. + _private: (), +} + +lazy_static! { + // Count of how many LazyServiceGuard objects are in existence. + static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0); +} + +impl LazyServiceGuard { + /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this + /// process. + pub fn new() -> Self { + let mut count = GUARD_COUNT.lock().unwrap(); + *count += 1; + if *count == 1 { + // It's important that we make this call with the mutex held, to make sure + // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly + // sequenced. (That also means we can't just use an AtomicU64.) + force_lazy_services_persist(true); + } + Self { _private: () } + } +} + +impl Drop for LazyServiceGuard { + fn drop(&mut self) { + let mut count = GUARD_COUNT.lock().unwrap(); + *count -= 1; + if *count == 0 { + force_lazy_services_persist(false); + } + } +} + +impl Clone for LazyServiceGuard { + fn clone(&self) -> Self { + Self::new() + } +} + +impl Default for LazyServiceGuard { + fn default() -> Self { + Self::new() + } +} + /// Tests often create a base BBinder instance; so allowing the unit /// type to be remotable translates nicely to Binder::new(()). impl Remotable for () { diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 256fa8bea6..53a24afcf0 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -22,10 +22,10 @@ use crate::proxy::SpIBinder; use crate::sys; use std::convert::TryInto; +use std::fmt; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ptr::{self, NonNull}; -use std::fmt; mod file_descriptor; mod parcelable; @@ -33,8 +33,8 @@ mod parcelable_holder; pub use self::file_descriptor::ParcelFileDescriptor; pub use self::parcelable::{ - Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, - Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, + Deserialize, DeserializeArray, DeserializeOption, Parcelable, Serialize, SerializeArray, + SerializeOption, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG, }; pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; @@ -78,9 +78,7 @@ impl Parcel { // a valid pointer. If it fails, the process will crash. sys::AParcel_create() }; - Self { - ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") - } + Self { ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") } } /// Create an owned reference to a parcel object from a raw pointer. @@ -116,10 +114,7 @@ impl Parcel { // lifetime of the returned `BorrowedParcel` is tied to `self`, so the // borrow checker will ensure that the `AParcel` can only be accessed // via the `BorrowParcel` until it goes out of scope. - BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - } + BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData } } /// Get an immutable borrowed view into the contents of this `Parcel`. @@ -127,9 +122,7 @@ impl Parcel { // Safety: Parcel and BorrowedParcel are both represented in the same // way as a NonNull<sys::AParcel> due to their use of repr(transparent), // so casting references as done here is valid. - unsafe { - &*(self as *const Parcel as *const BorrowedParcel<'_>) - } + unsafe { &*(self as *const Parcel as *const BorrowedParcel<'_>) } } } @@ -165,10 +158,7 @@ impl<'a> BorrowedParcel<'a> { /// since this is a mutable borrow, it must have exclusive access to the /// AParcel for the duration of the borrow. pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> { - Some(Self { - ptr: NonNull::new(ptr)?, - _lifetime: PhantomData, - }) + Some(Self { ptr: NonNull::new(ptr)?, _lifetime: PhantomData }) } /// Get a sub-reference to this reference to the parcel. @@ -177,10 +167,7 @@ impl<'a> BorrowedParcel<'a> { // lifetime of the returned `BorrowedParcel` is tied to `self`, so the // borrow checker will ensure that the `AParcel` can only be accessed // via the `BorrowParcel` until it goes out of scope. - BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - } + BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData } } } @@ -269,7 +256,7 @@ impl<'a> BorrowedParcel<'a> { /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> where - for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>, { let start = self.get_data_position(); self.write(&0i32)?; @@ -324,17 +311,17 @@ impl<'a> BorrowedParcel<'a> { /// /// This appends `size` bytes of data from `other` starting at offset /// `start` to the current parcel, or returns an error if not possible. - pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + pub fn append_from( + &mut self, + other: &impl AsNative<sys::AParcel>, + start: i32, + size: i32, + ) -> Result<()> { 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, - ) + sys::AParcel_appendFrom(other.as_native(), self.as_native_mut(), start, size) }; status_result(status) } @@ -406,7 +393,7 @@ impl Parcel { /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> where - for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>, { self.borrowed().sized_write(f) } @@ -438,7 +425,12 @@ impl Parcel { /// /// This appends `size` bytes of data from `other` starting at offset /// `start` to the current parcel, or returns an error if not possible. - pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + pub fn append_from( + &mut self, + other: &impl AsNative<sys::AParcel>, + start: i32, + size: i32, + ) -> Result<()> { self.borrowed().append_from(other, start, size) } @@ -492,7 +484,7 @@ impl<'a> BorrowedParcel<'a> { /// pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>, { let start = self.get_data_position(); let parcelable_size: i32 = self.read()?; @@ -500,17 +492,13 @@ impl<'a> BorrowedParcel<'a> { return Err(StatusCode::BAD_VALUE); } - let end = start.checked_add(parcelable_size) - .ok_or(StatusCode::BAD_VALUE)?; + let end = start.checked_add(parcelable_size).ok_or(StatusCode::BAD_VALUE)?; if end > self.get_data_size() { return Err(StatusCode::NOT_ENOUGH_DATA); } let subparcel = ReadableSubParcel { - parcel: BorrowedParcel { - ptr: self.ptr, - _lifetime: PhantomData, - }, + parcel: BorrowedParcel { ptr: self.ptr, _lifetime: PhantomData }, end_position: end, }; f(subparcel)?; @@ -633,7 +621,7 @@ impl Parcel { /// pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>, { self.borrowed_ref().sized_read(f) } @@ -716,15 +704,13 @@ impl Drop for Parcel { impl fmt::Debug for Parcel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Parcel") - .finish() + f.debug_struct("Parcel").finish() } } impl<'a> fmt::Debug for BorrowedParcel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BorrowedParcel") - .finish() + f.debug_struct("BorrowedParcel").finish() } } @@ -813,10 +799,7 @@ fn test_read_data() { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<f32>().unwrap(), - 1143139100000000000000000000.0 - ); + assert_eq!(parcel.read::<f32>().unwrap(), 1143139100000000000000000000.0); assert_eq!(parcel.read::<f32>().unwrap(), 40.043392); unsafe { @@ -842,10 +825,7 @@ fn test_utf8_utf16_conversions() { unsafe { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<Option<String>>().unwrap().unwrap(), - "Hello, Binder!", - ); + assert_eq!(parcel.read::<Option<String>>().unwrap().unwrap(), "Hello, Binder!",); unsafe { assert!(parcel.set_data_position(start).is_ok()); } @@ -864,13 +844,7 @@ fn test_utf8_utf16_conversions() { assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok()); assert!(parcel - .write( - &[ - String::from("str4"), - String::from("str5"), - String::from("str6"), - ][..] - ) + .write(&[String::from("str4"), String::from("str5"), String::from("str6"),][..]) .is_ok()); let s1 = "Hello, Binder!"; @@ -882,14 +856,8 @@ fn test_utf8_utf16_conversions() { assert!(parcel.set_data_position(start).is_ok()); } - assert_eq!( - parcel.read::<Vec<String>>().unwrap(), - ["str1", "str2", "str3"] - ); - assert_eq!( - parcel.read::<Vec<String>>().unwrap(), - ["str4", "str5", "str6"] - ); + assert_eq!(parcel.read::<Vec<String>>().unwrap(), ["str1", "str2", "str3"]); + assert_eq!(parcel.read::<Vec<String>>().unwrap(), ["str4", "str5", "str6"]); assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]); } @@ -900,9 +868,9 @@ fn test_sized_write() { let arr = [1i32, 2i32, 3i32]; - parcel.sized_write(|subparcel| { - subparcel.write(&arr[..]) - }).expect("Could not perform sized write"); + parcel + .sized_write(|subparcel| subparcel.write(&arr[..])) + .expect("Could not perform sized write"); // i32 sub-parcel length + i32 array length + 3 i32 elements let expected_len = 20i32; @@ -913,15 +881,9 @@ fn test_sized_write() { parcel.set_data_position(start).unwrap(); } - assert_eq!( - expected_len, - parcel.read().unwrap(), - ); + assert_eq!(expected_len, parcel.read().unwrap(),); - assert_eq!( - parcel.read::<Vec<i32>>().unwrap(), - &arr, - ); + assert_eq!(parcel.read::<Vec<i32>>().unwrap(), &arr,); } #[test] diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index 4d3d59afb0..de6d64934a 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -15,7 +15,7 @@ */ use super::{ - Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray, + BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, }; use crate::binder::AsNative; @@ -115,10 +115,7 @@ impl DeserializeOption for ParcelFileDescriptor { // 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, - ))?; + status_result(sys::AParcel_readParcelFileDescriptor(parcel.as_native(), &mut fd))?; } if fd < 0 { Ok(None) @@ -136,9 +133,7 @@ impl DeserializeOption for ParcelFileDescriptor { impl Deserialize for ParcelFileDescriptor { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { - Deserialize::deserialize(parcel) - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + 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 0c7e48df0c..c241e4d2d3 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -22,8 +22,8 @@ use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; +use std::mem::{self, ManuallyDrop, MaybeUninit}; use std::os::raw::{c_char, c_ulong}; -use std::mem::{self, MaybeUninit, ManuallyDrop}; use std::ptr; use std::slice; @@ -109,17 +109,14 @@ unsafe extern "C" fn serialize_element<T: Serialize>( // so the function signature matches what bindgen generates. let index = index as usize; - let slice: &[T] = slice::from_raw_parts(array.cast(), index+1); + let slice: &[T] = slice::from_raw_parts(array.cast(), index + 1); let mut parcel = match BorrowedParcel::from_raw(parcel) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; - slice[index].serialize(&mut parcel) - .err() - .unwrap_or(StatusCode::OK) - as status_t + slice[index].serialize(&mut parcel).err().unwrap_or(StatusCode::OK) as status_t } /// Helper trait for types that can be deserialized as arrays. @@ -265,10 +262,7 @@ unsafe extern "C" fn allocate_vec_with_buffer<T>( /// /// 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 { +unsafe extern "C" fn allocate_vec<T>(data: *mut c_void, len: i32) -> bool { let vec = &mut *(data as *mut Option<Vec<MaybeUninit<T>>>); if len < 0 { *vec = None; @@ -286,7 +280,6 @@ unsafe extern "C" fn allocate_vec<T>( true } - macro_rules! parcelable_primitives { { $( @@ -303,11 +296,7 @@ unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> { // has the same alignment and size as T, so the pointer to the vector // allocation will be compatible. let mut vec = ManuallyDrop::new(vec); - Vec::from_raw_parts( - vec.as_mut_ptr().cast(), - vec.len(), - vec.capacity(), - ) + Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) } macro_rules! impl_parcelable { @@ -522,11 +511,7 @@ impl SerializeOption for str { // `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, - )) + status_result(sys::AParcel_writeString(parcel.as_native_mut(), ptr::null(), -1)) }, Some(s) => unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -540,10 +525,7 @@ impl SerializeOption for str { status_result(sys::AParcel_writeString( parcel.as_native_mut(), s.as_ptr() as *const c_char, - s.as_bytes() - .len() - .try_into() - .or(Err(StatusCode::BAD_VALUE))?, + s.as_bytes().len().try_into().or(Err(StatusCode::BAD_VALUE))?, )) }, } @@ -602,9 +584,7 @@ impl DeserializeArray for Option<String> {} impl Deserialize for String { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { - Deserialize::deserialize(parcel) - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + Deserialize::deserialize(parcel).transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } } @@ -704,10 +684,7 @@ impl Serialize for Status { // 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(), - )) + status_result(sys::AParcel_writeStatusHeader(parcel.as_native_mut(), self.as_native())) } } } @@ -881,8 +858,7 @@ macro_rules! impl_deserialize_for_parcelable { Ok(()) } else { use $crate::Parcelable; - this.get_or_insert_with(Self::default) - .read_from_parcel(parcel) + this.get_or_insert_with(Self::default).read_from_parcel(parcel) } } } @@ -915,8 +891,8 @@ impl<T: DeserializeOption> DeserializeOption for Box<T> { #[cfg(test)] mod tests { - use crate::parcel::Parcel; use super::*; + use crate::parcel::Parcel; #[test] fn test_custom_parcelable() { @@ -934,11 +910,11 @@ mod tests { impl Deserialize for Custom { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Ok(Custom( - parcel.read()?, - parcel.read()?, - parcel.read()?, - parcel.read::<Option<Vec<String>>>()?.unwrap(), - )) + parcel.read()?, + parcel.read()?, + parcel.read()?, + parcel.read::<Option<Vec<String>>>()?.unwrap(), + )) } } @@ -1157,12 +1133,7 @@ mod tests { assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); - let f32s = [ - std::f32::NAN, - std::f32::INFINITY, - 1.23456789, - std::f32::EPSILON, - ]; + let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON]; unsafe { assert!(parcel.set_data_position(start).is_ok()); @@ -1178,12 +1149,7 @@ mod tests { assert!(vec[0].is_nan()); assert_eq!(vec[1..], f32s[1..]); - let f64s = [ - std::f64::NAN, - std::f64::INFINITY, - 1.234567890123456789, - std::f64::EPSILON, - ]; + let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON]; 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 432da5dfd7..c829d37dca 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -48,10 +48,7 @@ impl<T> AnyParcelable for T where T: DowncastSync + Parcelable + std::fmt::Debug #[derive(Debug, Clone)] enum ParcelableHolderData { Empty, - Parcelable { - parcelable: Arc<dyn AnyParcelable>, - name: String, - }, + Parcelable { parcelable: Arc<dyn AnyParcelable>, name: String }, Parcel(Parcel), } @@ -77,10 +74,7 @@ pub struct ParcelableHolder { impl ParcelableHolder { /// Construct a new `ParcelableHolder` with the given stability. pub fn new(stability: Stability) -> Self { - Self { - data: Mutex::new(ParcelableHolderData::Empty), - stability, - } + Self { data: Mutex::new(ParcelableHolderData::Empty), stability } } /// Reset the contents of this `ParcelableHolder`. @@ -101,10 +95,8 @@ impl ParcelableHolder { return Err(StatusCode::BAD_VALUE); } - *self.data.get_mut().unwrap() = ParcelableHolderData::Parcelable { - parcelable: p, - name: T::get_descriptor().into(), - }; + *self.data.get_mut().unwrap() = + ParcelableHolderData::Parcelable { parcelable: p, name: T::get_descriptor().into() }; Ok(()) } @@ -130,10 +122,7 @@ impl ParcelableHolder { let mut data = self.data.lock().unwrap(); match *data { ParcelableHolderData::Empty => Ok(None), - ParcelableHolderData::Parcelable { - ref parcelable, - ref name, - } => { + ParcelableHolderData::Parcelable { ref parcelable, ref name } => { if name != parcelable_desc { return Err(StatusCode::BAD_VALUE); } @@ -199,10 +188,7 @@ impl Parcelable for ParcelableHolder { let mut data = self.data.lock().unwrap(); match *data { ParcelableHolderData::Empty => parcel.write(&0i32), - ParcelableHolderData::Parcelable { - ref parcelable, - ref name, - } => { + ParcelableHolderData::Parcelable { ref parcelable, ref name } => { let length_start = parcel.get_data_position(); parcel.write(&0i32)?; @@ -251,9 +237,7 @@ impl Parcelable for ParcelableHolder { // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we // only go up to i32::MAX because that's what our API uses everywhere let data_start = parcel.get_data_position(); - let data_end = data_start - .checked_add(data_size) - .ok_or(StatusCode::BAD_VALUE)?; + let data_end = data_start.checked_add(data_size).ok_or(StatusCode::BAD_VALUE)?; let mut new_parcel = Parcel::new(); new_parcel.append_from(parcel, data_start, data_size)?; diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 4df557bee0..254efaed9a 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -22,7 +22,8 @@ use crate::binder::{ }; use crate::error::{status_result, Result, StatusCode}; use crate::parcel::{ - Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, + BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, + SerializeArray, SerializeOption, }; use crate::sys; @@ -132,6 +133,14 @@ impl SpIBinder { } } +fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> { + if let Some(service) = service { + FromIBinder::try_from(service) + } else { + Err(StatusCode::NAME_NOT_FOUND) + } +} + pub mod unstable_api { use super::{sys, SpIBinder}; @@ -431,10 +440,7 @@ impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> { - parcel - .read_binder() - .transpose() - .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + parcel.read_binder().transpose().unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) } } @@ -563,6 +569,9 @@ impl Drop for WpIBinder { /// 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. +/// +/// Dropping the `DeathRecipient` will `unlink_to_death` any binders it is +/// currently linked to. #[repr(C)] pub struct DeathRecipient { recipient: *mut sys::AIBinder_DeathRecipient, @@ -610,7 +619,10 @@ impl DeathRecipient { // // 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>)); + sys::AIBinder_DeathRecipient_setOnUnlinked( + recipient, + Some(Self::cookie_decr_refcount::<F>), + ); } DeathRecipient { recipient, @@ -776,21 +788,13 @@ pub fn wait_for_service(name: &str) -> Option<SpIBinder> { /// Retrieve an existing service for a particular interface, blocking for a few /// seconds if it doesn't yet exist. pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - let service = get_service(name); - match service { - Some(service) => FromIBinder::try_from(service), - None => Err(StatusCode::NAME_NOT_FOUND), - } + interface_cast(get_service(name)) } /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - let service = wait_for_service(name); - match service { - Some(service) => FromIBinder::try_from(service), - None => Err(StatusCode::NAME_NOT_FOUND), - } + interface_cast(wait_for_service(name)) } /// Check if a service is declared (e.g. in a VINTF manifest) diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index 0aef744f98..cc18741a4e 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -124,7 +124,8 @@ impl ThreadState { /// kernel is too old to support this feature. pub fn with_calling_sid<T, F>(check_permission: F) -> T where - for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T { + for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T, + { // Safety: AIBinder_getCallingSid returns a c-string pointer // that is valid for a transaction. Also, the string returned // is thread local. By restricting the lifetime of the CStr diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index c9d6af0d75..4e10fa915d 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -60,9 +60,7 @@ fn main() -> Result<(), &'static str> { if let Some(extension_name) = extension_name { let extension = BnTest::new_binder(TestService::new(&extension_name), BinderFeatures::default()); - service - .set_extension(&mut extension.as_binder()) - .expect("Could not add extension"); + service.set_extension(&mut extension.as_binder()).expect("Could not add extension"); } binder::add_service(&service_name, service.as_binder()) .expect("Could not register service"); @@ -73,10 +71,7 @@ fn main() -> Result<(), &'static str> { } fn print_usage() { - eprintln!( - "Usage: {} SERVICE_NAME [EXTENSION_NAME]", - RUST_SERVICE_BINARY - ); + eprintln!("Usage: {} SERVICE_NAME [EXTENSION_NAME]", RUST_SERVICE_BINARY); eprintln!(concat!( "Spawn a Binder test service identified by SERVICE_NAME,", " optionally with an extesion named EXTENSION_NAME", @@ -90,10 +85,7 @@ struct TestService { impl TestService { fn new(s: &str) -> Self { - Self { - s: s.to_string(), - dump_args: Mutex::new(Vec::new()), - } + Self { s: s.to_string(), dump_args: Mutex::new(Vec::new()) } } } @@ -111,11 +103,15 @@ impl TryFrom<u32> for TestTransactionCode { fn try_from(c: u32) -> Result<Self, Self::Error> { match c { _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test), - _ if c == TestTransactionCode::GetDumpArgs as u32 => Ok(TestTransactionCode::GetDumpArgs), + _ if c == TestTransactionCode::GetDumpArgs as u32 => { + Ok(TestTransactionCode::GetDumpArgs) + } _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } - _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction), + _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => { + Ok(TestTransactionCode::GetIsHandlingTransaction) + } _ => Err(StatusCode::UNKNOWN_TRANSACTION), } } @@ -200,15 +196,16 @@ fn on_transact( TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), - TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?), + TestTransactionCode::GetIsHandlingTransaction => { + reply.write(&service.get_is_handling_transaction()?) + } } } impl ITest for BpTest { fn test(&self) -> Result<String, StatusCode> { let reply = - self.binder - .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?; + self.binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?; reply.read() } @@ -243,31 +240,45 @@ impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { let binder = self.binder.clone(); P::spawn( move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + |reply| async move { reply?.read() }, ) } fn get_dump_args(&self) -> binder::BoxFuture<'static, Result<Vec<String>, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())) + }, + |reply| async move { reply?.read() }, ) } fn get_selinux_context(&self) -> binder::BoxFuture<'static, Result<String, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact( + TestTransactionCode::GetSelinuxContext as TransactionCode, + 0, + |_| Ok(()), + ) + }, + |reply| async move { reply?.read() }, ) } fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, Result<bool, StatusCode>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())), - |reply| async move { reply?.read() } + move || { + binder.transact( + TestTransactionCode::GetIsHandlingTransaction as TransactionCode, + 0, + |_| Ok(()), + ) + }, + |reply| async move { reply?.read() }, ) } } @@ -374,7 +385,7 @@ mod tests { use binder_tokio::Tokio; - use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; + use super::{BnTest, IATest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY}; pub struct ScopedServiceProcess(Child); @@ -405,9 +416,7 @@ mod tests { impl Drop for ScopedServiceProcess { fn drop(&mut self) { self.0.kill().expect("Could not kill child process"); - self.0 - .wait() - .expect("Could not wait for child process to die"); + self.0.wait().expect("Could not wait for child process to die"); } } @@ -428,10 +437,7 @@ mod tests { ); // The service manager service isn't an ITest, so this must fail. - assert_eq!( - binder::get_interface::<dyn ITest>("manager").err(), - Some(StatusCode::BAD_TYPE) - ); + assert_eq!(binder::get_interface::<dyn ITest>("manager").err(), Some(StatusCode::BAD_TYPE)); assert_eq!( binder::get_interface::<dyn IATest<Tokio>>("manager").err(), Some(StatusCode::BAD_TYPE) @@ -450,7 +456,9 @@ mod tests { Some(StatusCode::NAME_NOT_FOUND) ); assert_eq!( - binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(), + binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist") + .await + .err(), Some(StatusCode::NAME_NOT_FOUND) ); @@ -511,8 +519,9 @@ mod tests { async fn trivial_client_async() { let service_name = "trivial_client_test"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!(test_client.test().await.unwrap(), "trivial_client_test"); } @@ -529,8 +538,9 @@ mod tests { async fn wait_for_trivial_client_async() { let service_name = "wait_for_trivial_client_test"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::wait_for_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test"); } @@ -539,9 +549,7 @@ mod tests { let mut out_ptr = ptr::null_mut(); assert_eq!(selinux_sys::getcon(&mut out_ptr), 0); assert!(!out_ptr.is_null()); - CStr::from_ptr(out_ptr) - .to_str() - .expect("context was invalid UTF-8") + CStr::from_ptr(out_ptr).to_str().expect("context was invalid UTF-8") } } @@ -551,18 +559,16 @@ mod tests { let _process = ScopedServiceProcess::new(service_name); let test_client: Strong<dyn ITest> = binder::get_interface(service_name).expect("Did not get manager binder service"); - assert_eq!( - test_client.get_selinux_context().unwrap(), - get_expected_selinux_context() - ); + assert_eq!(test_client.get_selinux_context().unwrap(), get_expected_selinux_context()); } #[tokio::test] async fn get_selinux_context_async() { let service_name = "get_selinux_context_async"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); assert_eq!( test_client.get_selinux_context().await.unwrap(), get_expected_selinux_context() @@ -586,13 +592,11 @@ mod tests { async fn get_selinux_context_async_to_sync() { let service_name = "get_selinux_context"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); let test_client = test_client.into_sync(); - assert_eq!( - test_client.get_selinux_context().unwrap(), - get_expected_selinux_context() - ); + assert_eq!(test_client.get_selinux_context().unwrap(), get_expected_selinux_context()); } struct Bools { @@ -605,10 +609,7 @@ mod tests { self.binder_died.load(Ordering::Relaxed) } fn assert_died(&self) { - assert!( - self.is_dead(), - "Did not receive death notification" - ); + assert!(self.is_dead(), "Did not receive death notification"); } fn assert_dropped(&self) { assert!( @@ -639,9 +640,7 @@ mod tests { let mut death_recipient = { let flag = binder_died.clone(); - let set_on_drop = SetOnDrop { - binder_dealloc: binder_dealloc.clone(), - }; + let set_on_drop = SetOnDrop { binder_dealloc: binder_dealloc.clone() }; DeathRecipient::new(move || { flag.store(true, Ordering::Relaxed); // Force the closure to take ownership of set_on_drop. When the closure is @@ -650,14 +649,9 @@ mod tests { }) }; - binder - .link_to_death(&mut death_recipient) - .expect("link_to_death failed"); + binder.link_to_death(&mut death_recipient).expect("link_to_death failed"); - let bools = Bools { - binder_died, - binder_dealloc, - }; + let bools = Bools { binder_died, binder_dealloc }; (bools, death_recipient) } @@ -675,9 +669,7 @@ mod tests { let (bools, recipient) = register_death_notification(&mut remote); drop(service_process); - remote - .ping_binder() - .expect_err("Service should have died already"); + remote.ping_binder().expect_err("Service should have died already"); // Pause to ensure any death notifications get delivered thread::sleep(Duration::from_secs(1)); @@ -701,22 +693,15 @@ mod tests { let (bools, mut recipient) = register_death_notification(&mut remote); - remote - .unlink_to_death(&mut recipient) - .expect("Could not unlink death notifications"); + remote.unlink_to_death(&mut recipient).expect("Could not unlink death notifications"); drop(service_process); - remote - .ping_binder() - .expect_err("Service should have died already"); + remote.ping_binder().expect_err("Service should have died already"); // Pause to ensure any death notifications get delivered thread::sleep(Duration::from_secs(1)); - assert!( - !bools.is_dead(), - "Received unexpected death notification after unlinking", - ); + assert!(!bools.is_dead(), "Received unexpected death notification after unlinking",); bools.assert_not_dropped(); drop(recipient); @@ -771,9 +756,7 @@ mod tests { let dump_args = ["dump", "args", "for", "testing"]; let null_out = File::open("/dev/null").expect("Could not open /dev/null"); - remote - .dump(&null_out, &dump_args) - .expect("Could not dump remote service"); + remote.dump(&null_out, &dump_args).expect("Could not dump remote service"); let remote_args = test_client.get_dump_args().expect("Could not fetched dumped args"); assert_eq!(dump_args, remote_args[..], "Remote args don't match call to dump"); @@ -799,9 +782,7 @@ mod tests { let mut remote = binder::get_service(service_name); assert!(remote.is_binder_alive()); - let extension = remote - .get_extension() - .expect("Could not check for an extension"); + let extension = remote.get_extension().expect("Could not check for an extension"); assert!(extension.is_none()); } @@ -811,9 +792,7 @@ mod tests { let mut remote = binder::get_service(service_name); assert!(remote.is_binder_alive()); - let maybe_extension = remote - .get_extension() - .expect("Could not check for an extension"); + let maybe_extension = remote.get_extension().expect("Could not check for an extension"); let extension = maybe_extension.expect("Remote binder did not have an extension"); @@ -850,15 +829,12 @@ mod tests { #[test] fn reassociate_rust_binder() { let service_name = "testing_service"; - let service_ibinder = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ) - .as_binder(); + let service_ibinder = + BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()) + .as_binder(); - let service: Strong<dyn ITest> = service_ibinder - .into_interface() - .expect("Could not reassociate the generic ibinder"); + let service: Strong<dyn ITest> = + service_ibinder.into_interface().expect("Could not reassociate the generic ibinder"); assert_eq!(service.test().unwrap(), service_name); } @@ -866,10 +842,7 @@ mod tests { #[test] fn weak_binder_upgrade() { let service_name = "testing_service"; - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); let weak = Strong::downgrade(&service); @@ -882,10 +855,8 @@ mod tests { fn weak_binder_upgrade_dead() { let service_name = "testing_service"; let weak = { - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = + BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); Strong::downgrade(&service) }; @@ -896,10 +867,7 @@ mod tests { #[test] fn weak_binder_clone() { let service_name = "testing_service"; - let service = BnTest::new_binder( - TestService::new(service_name), - BinderFeatures::default(), - ); + let service = BnTest::new_binder(TestService::new(service_name), BinderFeatures::default()); let weak = Strong::downgrade(&service); let cloned = weak.clone(); @@ -915,14 +883,10 @@ mod tests { #[test] #[allow(clippy::eq_op)] fn binder_ord() { - let service1 = BnTest::new_binder( - TestService::new("testing_service1"), - BinderFeatures::default(), - ); - let service2 = BnTest::new_binder( - TestService::new("testing_service2"), - BinderFeatures::default(), - ); + let service1 = + BnTest::new_binder(TestService::new("testing_service1"), BinderFeatures::default()); + let service2 = + BnTest::new_binder(TestService::new("testing_service2"), BinderFeatures::default()); assert!((service1 >= service1)); assert!((service1 <= service1)); @@ -931,20 +895,20 @@ mod tests { #[test] fn binder_parcel_mixup() { - let service1 = BnTest::new_binder( - TestService::new("testing_service1"), - BinderFeatures::default(), - ); - let service2 = BnTest::new_binder( - TestService::new("testing_service2"), - BinderFeatures::default(), - ); + let service1 = + BnTest::new_binder(TestService::new("testing_service1"), BinderFeatures::default()); + let service2 = + BnTest::new_binder(TestService::new("testing_service2"), BinderFeatures::default()); let service1 = service1.as_binder(); let service2 = service2.as_binder(); let parcel = service1.prepare_transact().unwrap(); - let res = service2.submit_transact(super::TestTransactionCode::Test as TransactionCode, parcel, 0); + let res = service2.submit_transact( + super::TestTransactionCode::Test as TransactionCode, + parcel, + 0, + ); match res { Ok(_) => panic!("submit_transact should fail"), @@ -967,15 +931,18 @@ mod tests { // Should also be false in spawned thread. std::thread::spawn(|| { assert!(!binder::is_handling_transaction()); - }).join().unwrap(); + }) + .join() + .unwrap(); } #[tokio::test] async fn get_is_handling_transaction_async() { let service_name = "get_is_handling_transaction_async"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Strong<dyn IATest<Tokio>> = - binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name) + .await + .expect("Did not get manager binder service"); // Should be true externally. assert!(test_client.get_is_handling_transaction().await.unwrap()); @@ -985,11 +952,15 @@ mod tests { // Should also be false in spawned task. tokio::spawn(async { assert!(!binder::is_handling_transaction()); - }).await.unwrap(); + }) + .await + .unwrap(); // And in spawn_blocking task. tokio::task::spawn_blocking(|| { assert!(!binder::is_handling_transaction()); - }).await.unwrap(); + }) + .await + .unwrap(); } } diff --git a/libs/binder/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp index ec780f28a1..3f59dab3a9 100644 --- a/libs/binder/rust/tests/serialization.cpp +++ b/libs/binder/rust/tests/serialization.cpp @@ -381,7 +381,7 @@ TEST_F(SerializationTest, SerializeFileDescriptor) { string expected = "TestingFileDescriptors"; vector<char> buf(expected.length()); base::ReadFully(file_descriptors[0].release(), buf.data(), buf.size()); - ASSERT_EQ(expected, string(buf.data())); + ASSERT_EQ(expected, string(buf.data(), expected.length())); } TEST_F(SerializationTest, SerializeIBinder) { diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index b62da7be03..6220db4b28 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -23,7 +23,7 @@ use binder::{ }; // Import from impl API for testing only, should not be necessary as long as you // are using AIDL. -use binder::binder_impl::{BorrowedParcel, Binder, TransactionCode}; +use binder::binder_impl::{Binder, BorrowedParcel, TransactionCode}; use std::ffi::{c_void, CStr, CString}; use std::sync::Once; @@ -64,13 +64,7 @@ macro_rules! assert_eq { macro_rules! assert { ($expr:expr) => { if !$expr { - eprintln!( - "assertion failed: `{:?}`, {}:{}:{}", - $expr, - file!(), - line!(), - column!() - ); + eprintln!("assertion failed: `{:?}`, {}:{}:{}", $expr, file!(), line!(), column!()); return Err(StatusCode::FAILED_TRANSACTION); } }; @@ -117,11 +111,9 @@ fn on_transact( ) -> Result<(), StatusCode> { match code { bindings::Transaction_TEST_BOOL => { - assert_eq!(parcel.read::<bool>()?, true); - assert_eq!(parcel.read::<bool>()?, false); - assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { - bindings::TESTDATA_BOOL - }); + assert!(parcel.read::<bool>()?); + assert!(!parcel.read::<bool>()?); + assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL }); assert_eq!(parcel.read::<Option<Vec<bool>>>()?, None); reply.write(&true)?; @@ -148,9 +140,7 @@ fn on_transact( assert_eq!(parcel.read::<u16>()?, 0); assert_eq!(parcel.read::<u16>()?, 1); assert_eq!(parcel.read::<u16>()?, u16::max_value()); - assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { - bindings::TESTDATA_CHARS - }); + assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None); reply.write(&0u16)?; @@ -163,9 +153,7 @@ fn on_transact( assert_eq!(parcel.read::<i32>()?, 0); assert_eq!(parcel.read::<i32>()?, 1); assert_eq!(parcel.read::<i32>()?, i32::max_value()); - assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { - bindings::TESTDATA_I32 - }); + assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None); reply.write(&0i32)?; @@ -178,9 +166,7 @@ fn on_transact( assert_eq!(parcel.read::<i64>()?, 0); assert_eq!(parcel.read::<i64>()?, 1); assert_eq!(parcel.read::<i64>()?, i64::max_value()); - assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { - bindings::TESTDATA_I64 - }); + assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None); reply.write(&0i64)?; @@ -193,9 +179,7 @@ fn on_transact( assert_eq!(parcel.read::<u64>()?, 0); assert_eq!(parcel.read::<u64>()?, 1); assert_eq!(parcel.read::<u64>()?, u64::max_value()); - assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { - bindings::TESTDATA_U64 - }); + assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None); reply.write(&0u64)?; @@ -232,16 +216,9 @@ fn on_transact( let s: Option<String> = parcel.read()?; assert_eq!(s, None); let s: Option<Vec<Option<String>>> = parcel.read()?; - for (s, expected) in s - .unwrap() - .iter() - .zip(unsafe { bindings::TESTDATA_STRS }.iter()) - { - let expected = unsafe { - expected - .as_ref() - .and_then(|e| CStr::from_ptr(e).to_str().ok()) - }; + for (s, expected) in s.unwrap().iter().zip(unsafe { bindings::TESTDATA_STRS }.iter()) { + let expected = + 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()?; @@ -252,10 +229,7 @@ fn on_transact( .iter() .map(|s| { s.as_ref().map(|s| { - CStr::from_ptr(s) - .to_str() - .expect("String was not UTF-8") - .to_owned() + CStr::from_ptr(s).to_str().expect("String was not UTF-8").to_owned() }) }) .collect() @@ -284,12 +258,8 @@ 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 = + unsafe { SERVICE.as_ref().expect("Global binder service not initialized").clone() }; reply.write(&service)?; reply.write(&(None as Option<&SpIBinder>))?; reply.write(&[Some(&service), None][..])?; @@ -300,10 +270,7 @@ fn on_transact( assert!(status.is_ok()); let status: Status = parcel.read()?; assert_eq!(status.exception_code(), ExceptionCode::NULL_POINTER); - assert_eq!( - status.get_description(), - "Status(-4, EX_NULL_POINTER): 'a status message'" - ); + assert_eq!(status.get_description(), "Status(-4, EX_NULL_POINTER): 'a status message'"); let status: Status = parcel.read()?; assert_eq!(status.service_specific_error(), 42); assert_eq!( diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index a3533d831b..92d132f37c 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -141,6 +141,7 @@ aidl_interface { unstable: true, srcs: [ "BinderRpcTestClientInfo.aidl", + "BinderRpcTestServerConfig.aidl", "BinderRpcTestServerInfo.aidl", "IBinderRpcCallback.aidl", "IBinderRpcSession.aidl", @@ -167,7 +168,6 @@ cc_library_static { "libbinder_tls_shared_deps", ], shared_libs: [ - "libbinder", "libbase", "liblog", ], @@ -185,25 +185,72 @@ cc_library_static { ], } -cc_test { - name: "binderRpcTest", +cc_defaults { + name: "binderRpcTest_common_defaults", host_supported: true, target: { darwin: { enabled: false, }, + }, + defaults: [ + "binder_test_defaults", + ], + + static_libs: [ + "libbinder_tls_static", + "libbinder_tls_test_utils", + "binderRpcTestIface-cpp", + "binderRpcTestIface-ndk", + ], +} + +cc_defaults { + name: "binderRpcTest_service_defaults", + defaults: [ + "binderRpcTest_common_defaults", + ], + gtest: false, + auto_gen_config: false, + srcs: [ + "binderRpcTestCommon.cpp", + "binderRpcTestService.cpp", + ], +} + +cc_defaults { + name: "binderRpcTest_defaults", + target: { android: { test_suites: ["vts"], }, }, defaults: [ - "binder_test_defaults", - "libbinder_tls_shared_deps", + "binderRpcTest_common_defaults", ], srcs: [ "binderRpcTest.cpp", + "binderRpcTestCommon.cpp", ], + + test_suites: ["general-tests"], + require_root: true, + + data_bins: [ + "binder_rpc_test_service", + "binder_rpc_test_service_no_kernel", + "binder_rpc_test_service_single_threaded", + "binder_rpc_test_service_single_threaded_no_kernel", + ], +} + +cc_defaults { + name: "binderRpcTest_shared_defaults", + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], + shared_libs: [ "libbinder", "libbinder_ndk", @@ -212,14 +259,133 @@ cc_test { "libcutils", "liblog", ], +} + +cc_defaults { + name: "binderRpcTest_static_defaults", + + shared_libs: [ + "libutils", + // libcrypto_static is not visible to this module + "libcrypto", + ], static_libs: [ - "libbinder_tls_static", - "libbinder_tls_test_utils", - "binderRpcTestIface-cpp", - "binderRpcTestIface-ndk", + "libbase", + "libcutils", + "liblog", + "libssl", + ], + + cflags: [ + // Disable tests that require shared libraries, + // e.g., libbinder.so or libbinder_ndk.so + "-DBINDER_TEST_NO_SHARED_LIBS", + ], +} + +cc_test { + // The module name cannot start with "binderRpcTest" because + // then atest tries to execute it as part of binderRpcTest + name: "binder_rpc_test_service", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], +} + +cc_test { + name: "binder_rpc_test_service_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binder_rpc_test_service_single_threaded_no_kernel", + defaults: [ + "binderRpcTest_service_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", + ], +} + +cc_test { + name: "binderRpcTest", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_shared_defaults", + "libbinder_tls_shared_deps", + ], + + // Add the Trusty mock library as a fake dependency so it gets built + required: [ + "libbinder_on_trusty_mock", + ], +} + +cc_test { + name: "binderRpcTestNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + static_libs: [ + "libbinder_rpc_no_kernel", + ], +} + +cc_test { + name: "binderRpcTestSingleThreaded", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + "-DBINDER_WITH_KERNEL_IPC", + ], + static_libs: [ + "libbinder_rpc_single_threaded", + ], +} + +cc_test { + name: "binderRpcTestSingleThreadedNoKernel", + defaults: [ + "binderRpcTest_defaults", + "binderRpcTest_static_defaults", + ], + cflags: [ + "-DBINDER_RPC_SINGLE_THREADED", + ], + static_libs: [ + "libbinder_rpc_single_threaded_no_kernel", ], - test_suites: ["general-tests"], - require_root: true, } cc_test { @@ -319,7 +485,6 @@ cc_test { "libbinder", "libutils", ], - clang: true, cflags: [ "-g", "-Wno-missing-field-initializers", @@ -440,6 +605,7 @@ cc_test { shared_libs: [ "libbinder", "liblog", + "libcutils", "libutils", "libutilscallstack", "libbase", @@ -524,3 +690,37 @@ cc_test { ], test_suites: ["general-tests"], } + +cc_defaults { + name: "service_fuzzer_defaults", + static_libs: [ + "libbase", + "libbinder_random_parcel", + "libcutils", + ], + target: { + android: { + shared_libs: [ + "libbinder_ndk", + "libbinder", + "libutils", + ], + }, + host: { + static_libs: [ + "libbinder_ndk", + "libbinder", + "libutils", + ], + }, + darwin: { + enabled: false, + }, + }, + fuzz_config: { + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + }, +} diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl new file mode 100644 index 0000000000..34d74bee0d --- /dev/null +++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable BinderRpcTestServerConfig { + int numThreads; + int[] serverSupportedFileDescriptorTransportModes; + int socketType; + int rpcSecurity; + int serverVersion; + int vsockPort; + @utf8InCpp String addr; +} diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index fdd02a4435..b15a2251ef 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -24,6 +24,9 @@ interface IBinderRpcTest { // number of known RPC binders to process, RpcState::countBinders by session int[] countBinders(); + // Return a null binder with a non-nullable return type. + IBinder getNullBinder(); + // Caller sends server, callee pings caller's server and returns error code. int pingMe(IBinder binder); @nullable IBinder repeatBinder(@nullable IBinder binder); @@ -64,4 +67,8 @@ interface IBinderRpcTest { void scheduleShutdown(); void useKernelBinderCallingId(); + + ParcelFileDescriptor echoAsFile(@utf8InCpp String content); + + ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files); } diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index e1f5ed5381..55a3916112 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -15,8 +15,12 @@ */ #include <android-base/logging.h> -#include <binder/Parcel.h> +#include <binder/Binder.h> #include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <cutils/trace.h> #include <gtest/gtest.h> #include <utils/CallStack.h> @@ -24,6 +28,8 @@ #include <functional> #include <vector> +static android::String8 gEmpty(""); // make sure first allocation from optimization runs + struct DestructionAction { DestructionAction(std::function<void()> f) : mF(std::move(f)) {} ~DestructionAction() { mF(); }; @@ -124,12 +130,18 @@ DestructionAction ScopeDisallowMalloc() { }); } +using android::BBinder; +using android::defaultServiceManager; using android::IBinder; +using android::IServiceManager; +using android::OK; using android::Parcel; -using android::String16; -using android::defaultServiceManager; +using android::RpcServer; +using android::RpcSession; using android::sp; -using android::IServiceManager; +using android::status_t; +using android::statusToString; +using android::String16; static sp<IBinder> GetRemoteBinder() { // This gets binder representing the service manager @@ -175,6 +187,36 @@ TEST(BinderAllocation, SmallTransaction) { EXPECT_EQ(mallocs, 1); } +TEST(RpcBinderAllocation, SetupRpcServer) { + std::string tmp = getenv("TMPDIR") ?: "/tmp"; + std::string addr = tmp + "/binderRpcBenchmark"; + (void)unlink(addr.c_str()); + auto server = RpcServer::make(); + server->setRootObject(sp<BBinder>::make()); + + CHECK_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(); + + auto remoteBinder = session->getRootObject(); + + size_t mallocs = 0, totalBytes = 0; + { + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + totalBytes += bytes; + }); + CHECK_EQ(OK, remoteBinder->pingBinder()); + } + EXPECT_EQ(mallocs, 1); + EXPECT_EQ(totalBytes, 40); +} + int main(int argc, char** argv) { if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); @@ -182,5 +224,10 @@ int main(int argc, char** argv) { return 1; } ::testing::InitGoogleTest(&argc, argv); + + // if tracing is enabled, take in one-time cost + (void)ATRACE_INIT(); + (void)ATRACE_GET_ENABLED_TAGS(); + return RUN_ALL_TESTS(); } diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp index ce2770f943..b6aed0db28 100644 --- a/libs/binder/tests/binderBinderUnitTest.cpp +++ b/libs/binder/tests/binderBinderUnitTest.cpp @@ -15,10 +15,11 @@ */ #include <binder/Binder.h> -#include <binder/IBinder.h> +#include <binder/IInterface.h> #include <gtest/gtest.h> using android::BBinder; +using android::IBinder; using android::OK; using android::sp; @@ -48,3 +49,49 @@ TEST(Binder, AttachExtension) { binder->setExtension(ext); EXPECT_EQ(ext, binder->getExtension()); } + +struct MyCookie { + bool* deleted; +}; + +class UniqueBinder : public BBinder { +public: + UniqueBinder(const void* c) : cookie(reinterpret_cast<const MyCookie*>(c)) { + *cookie->deleted = false; + } + ~UniqueBinder() { *cookie->deleted = true; } + const MyCookie* cookie; +}; + +static sp<IBinder> make(const void* arg) { + return sp<UniqueBinder>::make(arg); +} + +TEST(Binder, LookupOrCreateWeak) { + auto binder = sp<BBinder>::make(); + bool deleted; + MyCookie cookie = {&deleted}; + sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie); + EXPECT_NE(binder, createdBinder); + + sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie); + EXPECT_EQ(createdBinder, lookedUpBinder); + EXPECT_FALSE(deleted); +} + +TEST(Binder, LookupOrCreateWeakDropSp) { + auto binder = sp<BBinder>::make(); + bool deleted1 = false; + bool deleted2 = false; + MyCookie cookie1 = {&deleted1}; + MyCookie cookie2 = {&deleted2}; + sp<IBinder> createdBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie1); + EXPECT_NE(binder, createdBinder); + + createdBinder.clear(); + EXPECT_TRUE(deleted1); + + sp<IBinder> lookedUpBinder = binder->lookupOrCreateWeak(kObjectId1, make, &cookie2); + EXPECT_EQ(&cookie2, sp<UniqueBinder>::cast(lookedUpBinder)->cookie); + EXPECT_FALSE(deleted2); +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index b1e17b7892..6e1c8ac16a 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -30,6 +30,7 @@ #include <android-base/properties.h> #include <android-base/result-gmock.h> #include <android-base/result.h> +#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <binder/Binder.h> @@ -82,6 +83,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 String16 binderLibTestServiceName = String16("test.binderLib"); @@ -115,6 +117,12 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_ECHO_VECTOR, BINDER_LIB_TEST_REJECT_OBJECTS, BINDER_LIB_TEST_CAN_GET_SID, + BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, + BINDER_LIB_TEST_SET_MAX_THREAD_COUNT, + BINDER_LIB_TEST_LOCK_UNLOCK, + BINDER_LIB_TEST_PROCESS_LOCK, + BINDER_LIB_TEST_UNLOCK_AFTER_MS, + BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -247,13 +255,11 @@ class BinderLibTest : public ::testing::Test { { int32_t id; Parcel data, reply; - sp<IBinder> binder; EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR)); - EXPECT_FALSE(binder != nullptr); - binder = reply.readStrongBinder(); - EXPECT_TRUE(binder != nullptr); + sp<IBinder> binder = reply.readStrongBinder(); + EXPECT_NE(nullptr, binder); EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); if (idPtr) *idPtr = id; @@ -442,6 +448,12 @@ TEST_F(BinderLibTest, CannotUseBinderAfterFork) { EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork"); } +TEST_F(BinderLibTest, AddManagerToManager) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = IInterface::asBinder(sm); + EXPECT_EQ(NO_ERROR, sm->addService(String16("binderLibTest-manager"), binder)); +} + TEST_F(BinderLibTest, WasParceled) { auto binder = sp<BBinder>::make(); EXPECT_FALSE(binder->wasParceled()); @@ -1146,6 +1158,41 @@ TEST_F(BinderLibTest, VectorSent) { EXPECT_EQ(readValue, testValue); } +// see ProcessState.cpp BINDER_VM_SIZE = 1MB. +// This value is not exposed, but some code in the framework relies on being able to use +// buffers near the cap size. +constexpr size_t kSizeBytesAlmostFull = 950'000; +constexpr size_t kSizeBytesOverFull = 1'050'000; + +TEST_F(BinderLibTest, GargantuanVectorSent) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + for (size_t i = 0; i < 10; i++) { + // a slight variation in size is used to consider certain possible caching implementations + const std::vector<uint64_t> testValue((kSizeBytesAlmostFull + i) / sizeof(uint64_t), 42); + + Parcel data, reply; + data.writeUint64Vector(testValue); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR)) + << i; + std::vector<uint64_t> readValue; + EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK)); + EXPECT_EQ(readValue, testValue); + } +} + +TEST_F(BinderLibTest, LimitExceededVectorSent) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + const std::vector<uint64_t> testValue(kSizeBytesOverFull / sizeof(uint64_t), 42); + + Parcel data, reply; + data.writeUint64Vector(testValue); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), + StatusEq(FAILED_TRANSACTION)); +} + TEST_F(BinderLibTest, BufRejected) { Parcel data, reply; uint32_t buf; @@ -1221,6 +1268,53 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> { + TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {} + + // Flattenable protocol + size_t getFlattenedSize() const { + // Return a valid non-zero size here so we don't get an unintended + // BAD_VALUE from Parcel::write + return 16; + } + size_t getFdCount() const { return mFdCount; } + status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const { + for (size_t i = 0; i < count; i++) { + fds[i] = STDIN_FILENO; + } + return NO_ERROR; + } + status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/, + size_t & /*count*/) { + /* This doesn't get called */ + return NO_ERROR; + } + + size_t mFdCount; +}; + +TEST_F(BinderLibTest, TooManyFdsFlattenable) { + rlimit origNofile; + int ret = getrlimit(RLIMIT_NOFILE, &origNofile); + ASSERT_EQ(0, ret); + + // Restore the original file limits when the test finishes + base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + + rlimit testNofile = {1024, 1024}; + ret = setrlimit(RLIMIT_NOFILE, &testNofile); + ASSERT_EQ(0, ret); + + Parcel parcel; + // Try to write more file descriptors than supported by the OS + TooManyFdsFlattenable tooManyFds1(1024); + EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE)); + + // Try to write more file descriptors than the internal limit + TooManyFdsFlattenable tooManyFds2(1025); + EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE)); +} + TEST(ServiceNotifications, Unregister) { auto sm = defaultServiceManager(); using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; @@ -1234,6 +1328,79 @@ TEST(ServiceNotifications, Unregister) { EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK); } +TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + 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); + 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). + */ + std::vector<std::thread> ts; + for (size_t i = 0; i < kKernelThreads; i++) { + ts.push_back(std::thread([&] { + Parcel local_reply; + EXPECT_THAT(server->transact(BINDER_LIB_TEST_LOCK_UNLOCK, data, &local_reply), + NO_ERROR); + })); + } + + data.writeInt32(100); + // Give a chance for all threads to be used + EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); + + for (auto &t : ts) { + t.join(); + } + + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_MAX_THREAD_COUNT, data, &reply), + StatusEq(NO_ERROR)); + replyi = reply.readInt32(); + EXPECT_EQ(replyi, kKernelThreads + 1); +} + +size_t epochMillis() { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::system_clock; + return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); +} + +TEST_F(BinderLibTest, HangingServices) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + int32_t delay = 1000; // ms + data.writeInt32(delay); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR); + std::vector<std::thread> ts; + size_t epochMsBefore = epochMillis(); + 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), + NO_ERROR); + })); + } + + for (auto &t : ts) { + t.join(); + } + size_t epochMsAfter = epochMillis(); + + // deadlock occurred and threads only finished after 1s passed. + EXPECT_GE(epochMsAfter, epochMsBefore + delay); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { @@ -1640,11 +1807,42 @@ public: case BINDER_LIB_TEST_CAN_GET_SID: { return IPCThreadState::self()->getCallingSid() == nullptr ? BAD_VALUE : NO_ERROR; } + case BINDER_LIB_TEST_GET_MAX_THREAD_COUNT: { + reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount()); + return NO_ERROR; + } + case BINDER_LIB_TEST_PROCESS_LOCK: { + m_blockMutex.lock(); + return NO_ERROR; + } + case BINDER_LIB_TEST_LOCK_UNLOCK: { + std::lock_guard<std::mutex> _l(m_blockMutex); + return NO_ERROR; + } + case BINDER_LIB_TEST_UNLOCK_AFTER_MS: { + int32_t ms = data.readInt32(); + return unlockInMs(ms); + } + case BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK: { + m_blockMutex.lock(); + sp<BinderLibTestService> thisService = this; + int32_t value = data.readInt32(); + // start local thread to unlock in 1s + std::thread t([=] { thisService->unlockInMs(value); }); + t.detach(); + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; } + status_t unlockInMs(int32_t ms) { + usleep(ms * 1000); + m_blockMutex.unlock(); + return NO_ERROR; + } + private: int32_t m_id; int32_t m_nextServerId; @@ -1655,6 +1853,7 @@ private: sp<IBinder> m_strongRef; sp<IBinder> m_callback; bool m_exitOnDestroy; + std::mutex m_blockMutex; }; int run_server(int index, int readypipefd, bool usePoll) @@ -1756,6 +1955,7 @@ int run_server(int index, int readypipefd, bool usePoll) } } } else { + ProcessState::self()->setThreadPoolMaxThreadCount(kKernelThreads); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp index 21cb70be17..278dd2bf81 100644 --- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp +++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp @@ -23,6 +23,7 @@ using namespace android; #ifdef __BIONIC__ TEST(MemoryHeapBase, ForceMemfdRespected) { auto mHeap = sp<MemoryHeapBase>::make(10, MemoryHeapBase::FORCE_MEMFD, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_FALSE(ashmem_valid(fd)); @@ -33,6 +34,7 @@ TEST(MemoryHeapBase, MemfdSealed) { auto mHeap = sp<MemoryHeapBase>::make(8192, MemoryHeapBase::FORCE_MEMFD, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL); @@ -43,6 +45,7 @@ TEST(MemoryHeapBase, MemfdUnsealed) { MemoryHeapBase::FORCE_MEMFD | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0); @@ -53,6 +56,7 @@ TEST(MemoryHeapBase, MemfdSealedProtected) { MemoryHeapBase::FORCE_MEMFD | MemoryHeapBase::READ_ONLY, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE); @@ -64,6 +68,7 @@ TEST(MemoryHeapBase, MemfdUnsealedProtected) { MemoryHeapBase::READ_ONLY | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE); @@ -74,6 +79,7 @@ TEST(MemoryHeapBase, HostMemfdExpected) { auto mHeap = sp<MemoryHeapBase>::make(8192, MemoryHeapBase::READ_ONLY, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); void* ptr = mHeap->getBase(); EXPECT_NE(ptr, MAP_FAILED); @@ -87,6 +93,7 @@ TEST(MemoryHeapBase,HostMemfdException) { MemoryHeapBase::READ_ONLY | MemoryHeapBase::MEMFD_ALLOW_SEALING, "Test mapping"); + ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); void* ptr = mHeap->getBase(); EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY); diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index c2639e7c67..21b0354b60 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -14,29 +14,7 @@ * limitations under the License. */ -#include <BinderRpcTestClientInfo.h> -#include <BinderRpcTestServerInfo.h> -#include <BnBinderRpcCallback.h> -#include <BnBinderRpcSession.h> -#include <BnBinderRpcTest.h> -#include <aidl/IBinderRpcTest.h> -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android/binder_auto_utils.h> -#include <android/binder_libbinder.h> -#include <binder/Binder.h> -#include <binder/BpBinder.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <binder/RpcServer.h> -#include <binder/RpcSession.h> -#include <binder/RpcTlsTestUtils.h> -#include <binder/RpcTlsUtils.h> -#include <binder/RpcTransport.h> -#include <binder/RpcTransportRaw.h> -#include <binder/RpcTransportTls.h> +#include <android-base/stringprintf.h> #include <gtest/gtest.h> #include <chrono> @@ -45,14 +23,12 @@ #include <thread> #include <type_traits> +#include <dlfcn.h> #include <poll.h> #include <sys/prctl.h> -#include <unistd.h> +#include <sys/socket.h> -#include "../FdTrigger.h" -#include "../RpcSocketAddress.h" // for testing preconnected clients -#include "../RpcState.h" // for debugging -#include "../vm_sockets.h" // for VMADDR_* +#include "binderRpcTestCommon.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -62,60 +38,20 @@ using testing::AssertionSuccess; namespace android { +#ifdef BINDER_TEST_NO_SHARED_LIBS +constexpr bool kEnableSharedLibs = false; +#else +constexpr bool kEnableSharedLibs = true; +#endif + static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT || RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); -const char* kLocalInetAddress = "127.0.0.1"; - -enum class RpcSecurity { RAW, TLS }; - -static inline std::vector<RpcSecurity> RpcSecurityValues() { - return {RpcSecurity::RAW, RpcSecurity::TLS}; -} - -static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( - RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, - std::unique_ptr<RpcAuth> auth = nullptr) { - switch (rpcSecurity) { - case RpcSecurity::RAW: - return RpcTransportCtxFactoryRaw::make(); - case RpcSecurity::TLS: { - if (verifier == nullptr) { - verifier = std::make_shared<RpcCertificateVerifierSimple>(); - } - if (auth == nullptr) { - auth = std::make_unique<RpcAuthSelfSigned>(); - } - return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); - } - default: - LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); - } -} TEST(BinderRpcParcel, EntireParcelFormatted) { Parcel p; p.writeInt32(3); - EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); -} - -class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { -public: - static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { - return newFactory(info.param)->toCString(); - } -}; - -TEST_P(BinderRpcSimple, SetExternalServerTest) { - base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); - int sinkFd = sink.get(); - auto server = RpcServer::make(newFactory(GetParam())); - ASSERT_FALSE(server->hasServer()); - ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); - ASSERT_TRUE(server->hasServer()); - base::unique_fd retrieved = server->releaseServer(); - ASSERT_FALSE(server->hasServer()); - ASSERT_EQ(sinkFd, retrieved.get()); + EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written"); } TEST(BinderRpc, CannotUseNextWireVersion) { @@ -139,191 +75,15 @@ using android::binder::Status; EXPECT_TRUE(stat.isOk()) << stat; \ } while (false) -class MyBinderRpcSession : public BnBinderRpcSession { -public: - static std::atomic<int32_t> gNum; - - MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } - Status getName(std::string* name) override { - *name = mName; - return Status::ok(); - } - ~MyBinderRpcSession() { gNum--; } - -private: - std::string mName; -}; -std::atomic<int32_t> MyBinderRpcSession::gNum; - -class MyBinderRpcCallback : public BnBinderRpcCallback { - Status sendCallback(const std::string& value) { - std::unique_lock _l(mMutex); - mValues.push_back(value); - _l.unlock(); - mCv.notify_one(); - return Status::ok(); - } - Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } - -public: - std::mutex mMutex; - std::condition_variable mCv; - std::vector<std::string> mValues; -}; - -class MyBinderRpcTest : public BnBinderRpcTest { -public: - wp<RpcServer> server; - int port = 0; - - Status sendString(const std::string& str) override { - (void)str; - return Status::ok(); - } - Status doubleString(const std::string& str, std::string* strstr) override { - *strstr = str + str; - return Status::ok(); - } - Status getClientPort(int* out) override { - *out = port; - return Status::ok(); - } - Status countBinders(std::vector<int32_t>* out) override { - sp<RpcServer> spServer = server.promote(); - if (spServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - out->clear(); - for (auto session : spServer->listSessions()) { - size_t count = session->state()->countBinders(); - out->push_back(count); - } - return Status::ok(); - } - Status pingMe(const sp<IBinder>& binder, int32_t* out) override { - if (binder == nullptr) { - std::cout << "Received null binder!" << std::endl; - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - *out = binder->pingBinder(); - return Status::ok(); - } - Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { - *out = binder; - return Status::ok(); - } - static sp<IBinder> mHeldBinder; - Status holdBinder(const sp<IBinder>& binder) override { - mHeldBinder = binder; - return Status::ok(); - } - Status getHeldBinder(sp<IBinder>* held) override { - *held = mHeldBinder; - return Status::ok(); - } - Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { - if (count <= 0) return Status::ok(); - return binder->nestMe(this, count - 1); - } - Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { - static sp<IBinder> binder = new BBinder; - *out = binder; - return Status::ok(); - } - Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { - *out = new MyBinderRpcSession(name); - return Status::ok(); - } - Status getNumOpenSessions(int32_t* out) override { - *out = MyBinderRpcSession::gNum; - return Status::ok(); - } - - std::mutex blockMutex; - Status lock() override { - blockMutex.lock(); - return Status::ok(); +static std::string WaitStatusToString(int wstatus) { + if (WIFEXITED(wstatus)) { + return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); } - Status unlockInMsAsync(int32_t ms) override { - usleep(ms * 1000); - blockMutex.unlock(); - return Status::ok(); + if (WIFSIGNALED(wstatus)) { + return base::StringPrintf("term signal %d", WTERMSIG(wstatus)); } - Status lockUnlock() override { - std::lock_guard<std::mutex> _l(blockMutex); - return Status::ok(); - } - - Status sleepMs(int32_t ms) override { - usleep(ms * 1000); - return Status::ok(); - } - - Status sleepMsAsync(int32_t ms) override { - // In-process binder calls are asynchronous, but the call to this method - // is synchronous wrt its client. This in/out-process threading model - // diffentiation is a classic binder leaky abstraction (for better or - // worse) and is preserved here the way binder sockets plugs itself - // into BpBinder, as nothing is changed at the higher levels - // (IInterface) which result in this behavior. - return sleepMs(ms); - } - - Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - if (callback == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - - if (delayed) { - std::thread([=]() { - ALOGE("Executing delayed callback: '%s'", value.c_str()); - Status status = doCallback(callback, oneway, false, value); - ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); - }).detach(); - return Status::ok(); - } - - if (oneway) { - return callback->sendOnewayCallback(value); - } - - return callback->sendCallback(value); - } - - Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, - const std::string& value) override { - return doCallback(callback, oneway, delayed, value); - } - - Status die(bool cleanup) override { - if (cleanup) { - exit(1); - } else { - _exit(1); - } - } - - Status scheduleShutdown() override { - sp<RpcServer> strongServer = server.promote(); - if (strongServer == nullptr) { - return Status::fromExceptionCode(Status::EX_NULL_POINTER); - } - std::thread([=] { - LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); - }).detach(); - return Status::ok(); - } - - Status useKernelBinderCallingId() override { - // this is WRONG! It does not make sense when using RPC binder, and - // because it is SO wrong, and so much code calls this, it should abort! - - (void)IPCThreadState::self()->getCallingPid(); - return Status::ok(); - } -}; -sp<IBinder> MyBinderRpcTest::mHeldBinder; + return base::StringPrintf("unexpected state %d", wstatus); +} class Process { public: @@ -332,8 +92,8 @@ public: android::base::borrowed_fd /* readEnd */)>& f) { android::base::unique_fd childWriteEnd; android::base::unique_fd childReadEnd; - CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno); - CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno); + CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno); + CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); @@ -345,13 +105,28 @@ public: } ~Process() { if (mPid != 0) { - waitpid(mPid, nullptr, 0); + int wstatus; + waitpid(mPid, &wstatus, 0); + if (mCustomExitStatusCheck) { + mCustomExitStatusCheck(wstatus); + } else { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) + << "server process failed: " << WaitStatusToString(wstatus); + } } } android::base::borrowed_fd readEnd() { return mReadEnd; } android::base::borrowed_fd writeEnd() { return mWriteEnd; } + void setCustomExitStatusCheck(std::function<void(int wstatus)> f) { + mCustomExitStatusCheck = std::move(f); + } + + // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead. + void terminate() { kill(mPid, SIGTERM); } + private: + std::function<void(int wstatus)> mCustomExitStatusCheck; pid_t mPid = 0; android::base::unique_fd mReadEnd; android::base::unique_fd mWriteEnd; @@ -366,7 +141,7 @@ static std::string allocateSocketAddress() { }; static unsigned int allocateVsockPort() { - static unsigned int vsockPort = 3456; + static unsigned int vsockPort = 34567; return vsockPort++; } @@ -419,10 +194,10 @@ struct BinderRpcTestProcessSession { BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; ~BinderRpcTestProcessSession() { - EXPECT_NE(nullptr, rootIface); - if (rootIface == nullptr) return; - if (!expectAlreadyShutdown) { + EXPECT_NE(nullptr, rootIface); + if (rootIface == nullptr) return; + std::vector<int32_t> remoteCounts; // calling over any sessions counts across all sessions EXPECT_OK(rootIface->countBinders(&remoteCounts)); @@ -443,28 +218,6 @@ struct BinderRpcTestProcessSession { } }; -enum class SocketType { - PRECONNECTED, - UNIX, - VSOCK, - INET, -}; -static inline std::string PrintToString(SocketType socketType) { - switch (socketType) { - case SocketType::PRECONNECTED: - return "preconnected_uds"; - case SocketType::UNIX: - return "unix_domain_socket"; - case SocketType::VSOCK: - return "vm_socket"; - case SocketType::INET: - return "inet_socket"; - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - return ""; - } -} - static base::unique_fd connectTo(const RpcSocketAddress& addr) { base::unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); @@ -480,115 +233,83 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } -class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { +using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd, + android::base::borrowed_fd readEnd); + +class BinderRpc : public ::testing::TestWithParam< + std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> { public: - struct Options { - size_t numThreads = 1; - size_t numSessions = 1; - size_t numIncomingConnections = 0; - size_t numOutgoingConnections = SIZE_MAX; - }; + 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()); } - static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [type, security] = info.param; - return PrintToString(type) + "_" + newFactory(security)->toCString(); + bool clientOrServerSingleThreaded() const { + return !kEnableRpcThreads || serverSingleThreaded(); } - static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { - uint64_t length = str.length(); - CHECK(android::base::WriteFully(fd, &length, sizeof(length))); - CHECK(android::base::WriteFully(fd, str.data(), str.length())); + // Whether the test params support sending FDs in parcels. + bool supportsFdTransport() const { + return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS && + (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX); } - static inline std::string readString(android::base::borrowed_fd fd) { - uint64_t length; - CHECK(android::base::ReadFully(fd, &length, sizeof(length))); - std::string ret(length, '\0'); - CHECK(android::base::ReadFully(fd, ret.data(), length)); + static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { + auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param; + auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); + if (singleThreaded) { + ret += "_single_threaded"; + } + if (noKernel) { + ret += "_no_kernel"; + } return ret; } - static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { - Parcel parcel; - CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); - writeString(fd, - std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); - } - - template <typename T> - static inline T readFromFd(android::base::borrowed_fd fd) { - std::string data = readString(fd); - Parcel parcel; - CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); - T object; - CHECK_EQ(OK, object.readFromParcel(&parcel)); - return object; - } - // This creates a new process serving an interface on a certain number of // threads. - ProcessSession createRpcTestSocketServerProcess( - const Options& options, const std::function<void(const sp<RpcServer>&)>& configure) { + ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) { CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server"; SocketType socketType = std::get<0>(GetParam()); RpcSecurity rpcSecurity = std::get<1>(GetParam()); + uint32_t clientVersion = std::get<2>(GetParam()); + uint32_t serverVersion = std::get<3>(GetParam()); + bool singleThreaded = std::get<4>(GetParam()); + bool noKernel = std::get<5>(GetParam()); - unsigned int vsockPort = allocateVsockPort(); - std::string addr = allocateSocketAddress(); + std::string path = android::base::GetExecutableDirectory(); + auto servicePath = + android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(), + singleThreaded ? "_single_threaded" : "", + noKernel ? "_no_kernel" : ""); auto ret = ProcessSession{ - .host = Process([&](android::base::borrowed_fd writeEnd, + .host = Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { - auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); - sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); - - server->setMaxThreads(options.numThreads); - - unsigned int outPort = 0; - - switch (socketType) { - case SocketType::PRECONNECTED: - [[fallthrough]]; - case SocketType::UNIX: - CHECK_EQ(OK, server->setupUnixDomainServer(addr.c_str())) << addr; - break; - case SocketType::VSOCK: - CHECK_EQ(OK, server->setupVsockServer(vsockPort)); - break; - case SocketType::INET: { - CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); - CHECK_NE(0, outPort); - break; - } - default: - LOG_ALWAYS_FATAL("Unknown socket type"); - } - - BinderRpcTestServerInfo serverInfo; - serverInfo.port = static_cast<int64_t>(outPort); - serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); - writeToFd(writeEnd, serverInfo); - auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); - - if (rpcSecurity == RpcSecurity::TLS) { - for (const auto& clientCert : clientInfo.certs) { - CHECK_EQ(OK, - certVerifier - ->addTrustedPeerCertificate(RpcCertificateFormat::PEM, - clientCert.data)); - } - } - - configure(server); - - server->join(); - - // Another thread calls shutdown. Wait for it to complete. - (void)server->shutdown(); + auto writeFd = std::to_string(writeEnd.get()); + auto readFd = std::to_string(readEnd.get()); + execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(), + NULL); }), }; + BinderRpcTestServerConfig serverConfig; + serverConfig.numThreads = options.numThreads; + serverConfig.socketType = static_cast<int32_t>(socketType); + serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity); + serverConfig.serverVersion = serverVersion; + serverConfig.vsockPort = allocateVsockPort(); + serverConfig.addr = allocateSocketAddress(); + for (auto mode : options.serverSupportedFileDescriptorTransportModes) { + serverConfig.serverSupportedFileDescriptorTransportModes.push_back( + static_cast<int32_t>(mode)); + } + writeToFd(ret.host.writeEnd(), serverConfig); + std::vector<sp<RpcSession>> sessions; auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); for (size_t i = 0; i < options.numSessions; i++) { @@ -618,20 +339,22 @@ public: status_t status; for (const auto& session : sessions) { + CHECK(session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(options.numIncomingConnections); session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); switch (socketType) { case SocketType::PRECONNECTED: status = session->setupPreconnectedClient({}, [=]() { - return connectTo(UnixSocketAddress(addr.c_str())); + return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); }); break; case SocketType::UNIX: - status = session->setupUnixDomainClient(addr.c_str()); + status = session->setupUnixDomainClient(serverConfig.addr.c_str()); break; case SocketType::VSOCK: - status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); + status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort); break; case SocketType::INET: status = session->setupInetClient("127.0.0.1", serverInfo.port); @@ -639,52 +362,22 @@ public: default: LOG_ALWAYS_FATAL("Unknown socket type"); } + if (options.allowConnectFailure && status != OK) { + ret.sessions.clear(); + break; + } CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status); ret.sessions.push_back({session, session->getRootObject()}); } return ret; } - BinderRpcTestProcessSession createRpcTestSocketServerProcess(const Options& options) { + BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) { BinderRpcTestProcessSession ret{ - .proc = createRpcTestSocketServerProcess( - options, - [&](const sp<RpcServer>& server) { - server->setPerSessionRootObject([&](const sockaddr* addr, - socklen_t len) { - sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); - switch (addr->sa_family) { - case AF_UNIX: - // nothing to save - break; - case AF_VSOCK: - CHECK_EQ(len, sizeof(sockaddr_vm)); - service->port = reinterpret_cast<const sockaddr_vm*>(addr) - ->svm_port; - break; - case AF_INET: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in*>(addr) - ->sin_port); - break; - case AF_INET6: - CHECK_EQ(len, sizeof(sockaddr_in)); - service->port = - ntohs(reinterpret_cast<const sockaddr_in6*>(addr) - ->sin6_port); - break; - default: - LOG_ALWAYS_FATAL("Unrecognized address family %d", - addr->sa_family); - } - service->server = server; - return service; - }); - }), + .proc = createRpcTestSocketServerProcessEtc(options), }; - ret.rootBinder = ret.proc.sessions.at(0).root; + ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root; ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); return ret; @@ -707,6 +400,12 @@ TEST_P(BinderRpc, GetInterfaceDescriptor) { } TEST_P(BinderRpc, MultipleSessions) { + if (serverSingleThreaded()) { + // Tests with multiple sessions require a multi-threaded service, + // but work fine on a single-threaded client + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5}); for (auto session : proc.proc.sessions) { ASSERT_NE(nullptr, session.root); @@ -715,6 +414,10 @@ TEST_P(BinderRpc, MultipleSessions) { } TEST_P(BinderRpc, SeparateRootObject) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + SocketType type = std::get<0>(GetParam()); if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) { // we can't get port numbers for unix sockets @@ -752,7 +455,7 @@ TEST_P(BinderRpc, AppendSeparateFormats) { p1.markForBinder(proc1.rootBinder); p1.writeInt32(3); - EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize())); + EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize())); EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize())); Parcel p2; @@ -791,6 +494,13 @@ TEST_P(BinderRpc, SendAndGetResultBackBig) { EXPECT_EQ(single + single, doubled); } +TEST_P(BinderRpc, InvalidNullBinderReturn) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> outBinder; + EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL); +} + TEST_P(BinderRpc, CallMeBack) { auto proc = createRpcTestSocketServerProcess({}); @@ -891,6 +601,10 @@ TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { } TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires a multi-threaded service"; + } + auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2}); sp<IBinder> outBinder; @@ -900,6 +614,11 @@ TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { } TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + auto proc = createRpcTestSocketServerProcess({}); sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager()); @@ -909,6 +628,11 @@ TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { } TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { + if (!kEnableKernelIpc || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + auto proc = createRpcTestSocketServerProcess({}); // for historical reasons, IServiceManager interface only returns the @@ -928,7 +652,13 @@ TEST_P(BinderRpc, RepeatRootObject) { } TEST_P(BinderRpc, NestedTransactions) { - auto proc = createRpcTestSocketServerProcess({}); + auto proc = createRpcTestSocketServerProcess({ + // Enable FD support because it uses more stack space and so represents + // something closer to a worst case scenario. + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); auto nastyNester = sp<MyBinderRpcTest>::make(); EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10)); @@ -1031,6 +761,10 @@ size_t epochMillis() { } TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumThreads = 10; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -1082,6 +816,10 @@ void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t num } TEST_P(BinderRpc, ThreadPoolOverSaturated) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = kNumThreads + 3; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -1089,6 +827,10 @@ TEST_P(BinderRpc, ThreadPoolOverSaturated) { } TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumThreads = 20; constexpr size_t kNumOutgoingConnections = 10; constexpr size_t kNumCalls = kNumOutgoingConnections + 3; @@ -1098,6 +840,10 @@ TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { } TEST_P(BinderRpc, ThreadingStressTest) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 100; @@ -1127,6 +873,10 @@ static void saturateThreadPool(size_t threadCount, const sp<IBinderRpcTest>& ifa } TEST_P(BinderRpc, OnewayStressTest) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClientThreads = 10; constexpr size_t kNumServerThreads = 10; constexpr size_t kNumCalls = 1000; @@ -1162,6 +912,10 @@ TEST_P(BinderRpc, OnewayCallDoesNotWait) { } TEST_P(BinderRpc, OnewayCallQueueing) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumSleeps = 10; constexpr size_t kNumExtraServerThreads = 4; constexpr size_t kSleepMs = 50; @@ -1185,12 +939,16 @@ TEST_P(BinderRpc, OnewayCallQueueing) { size_t epochMsAfter = epochMillis(); - EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); + EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps); saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface); } TEST_P(BinderRpc, OnewayCallExhaustion) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + constexpr size_t kNumClients = 2; constexpr size_t kTooLongMs = 1000; @@ -1236,8 +994,17 @@ TEST_P(BinderRpc, Callbacks) { for (bool callIsOneway : {true, false}) { for (bool callbackIsOneway : {true, false}) { for (bool delayed : {true, false}) { + if (clientOrServerSingleThreaded() && + (callIsOneway || callbackIsOneway || delayed)) { + // we have no incoming connections to receive the callback + continue; + } + + size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1; auto proc = createRpcTestSocketServerProcess( - {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + {.numThreads = 1, + .numSessions = 1, + .numIncomingConnections = numIncomingConnections}); auto cb = sp<MyBinderRpcCallback>::make(); if (callIsOneway) { @@ -1248,9 +1015,14 @@ TEST_P(BinderRpc, Callbacks) { proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString)); } - using std::literals::chrono_literals::operator""s; - std::unique_lock<std::mutex> _l(cb->mMutex); - cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + // if both transactions are synchronous and the response is sent back on the + // same thread, everything should have happened in a nested call. Otherwise, + // the callback will be processed on another thread. + if (callIsOneway || callbackIsOneway || delayed) { + using std::literals::chrono_literals::operator""s; + RpcMutexUniqueLock _l(cb->mMutex); + cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + } EXPECT_EQ(cb->mValues.size(), 1) << "callIsOneway: " << callIsOneway @@ -1269,13 +1041,128 @@ TEST_P(BinderRpc, Callbacks) { // since this session has an incoming connection w/ a threadpool, we // need to manually shut it down EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); - proc.expectAlreadyShutdown = true; } } } } +TEST_P(BinderRpc, SingleDeathRecipient) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); + } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + std::unique_lock<std::mutex> lock(dr->mMtx); + ASSERT_TRUE(dr->mCv.wait_for(lock, 1000ms, [&]() { return dr->dead; })); + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, SingleDeathRecipientOnShutdown) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + dead = true; + mCv.notify_one(); + } + std::mutex mMtx; + std::condition_variable mCv; + bool dead = false; + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + + // Explicitly calling shutDownAndWait will cause the death recipients + // to be called. + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + + std::unique_lock<std::mutex> lock(dr->mMtx); + if (!dr->dead) { + EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms)); + } + EXPECT_TRUE(dr->dead) << "Failed to receive the death notification."; + + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, DeathRecipientFatalWithoutIncoming) { + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override {} + }; + + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 0}); + + auto dr = sp<MyDeathRec>::make(); + EXPECT_DEATH(proc.rootBinder->linkToDeath(dr, (void*)1, 0), + "Cannot register a DeathRecipient without any incoming connections."); +} + +TEST_P(BinderRpc, UnlinkDeathRecipient) { + if (clientOrServerSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + class MyDeathRec : public IBinder::DeathRecipient { + public: + void binderDied(const wp<IBinder>& /* who */) override { + GTEST_FAIL() << "This should not be called after unlinkToDeath"; + } + }; + + // Death recipient needs to have an incoming connection to be called + auto proc = createRpcTestSocketServerProcess( + {.numThreads = 1, .numSessions = 1, .numIncomingConnections = 1}); + + auto dr = sp<MyDeathRec>::make(); + ASSERT_EQ(OK, proc.rootBinder->linkToDeath(dr, (void*)1, 0)); + ASSERT_EQ(OK, proc.rootBinder->unlinkToDeath(dr, (void*)1, 0, nullptr)); + + if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) { + EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status; + } + + // need to wait for the session to shutdown so we don't "Leak session" + EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + proc.expectAlreadyShutdown = true; +} + TEST_P(BinderRpc, OnewayCallbackWithNoThread) { auto proc = createRpcTestSocketServerProcess({}); auto cb = sp<MyBinderRpcCallback>::make(); @@ -1299,33 +1186,185 @@ TEST_P(BinderRpc, Die) { EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) << "Do death cleanup: " << doDeathCleanup; + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } } TEST_P(BinderRpc, UseKernelBinderCallingId) { - bool okToFork = ProcessState::selfOrNull() == nullptr; + // This test only works if the current process shared the internal state of + // ProcessState with the service across the call to fork(). Both the static + // libraries and libbinder.so have their own separate copies of all the + // globals, so the test only works when the test client and service both use + // libbinder.so (when using static libraries, even a client and service + // using the same kind of static library should have separate copies of the + // variables). + if (!kEnableSharedLibs || serverSingleThreaded() || noKernel()) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } auto proc = createRpcTestSocketServerProcess({}); - // If this process has used ProcessState already, then the forked process - // cannot use it at all. If this process hasn't used it (depending on the - // order tests are run), then the forked process can use it, and we'll only - // catch the invalid usage the second time. Such is the burden of global - // state! - if (okToFork) { - // we can't allocate IPCThreadState so actually the first time should - // succeed :( - EXPECT_OK(proc.rootIface->useKernelBinderCallingId()); - } + // we can't allocate IPCThreadState so actually the first time should + // succeed :( + EXPECT_OK(proc.rootIface->useKernelBinderCallingId()); // second time! we catch the error :) EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError()); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } +TEST_P(BinderRpc, FileDescriptorTransportRejectNone) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE}, + .allowConnectFailure = true, + }); + EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed"; + proc.proc.host.terminate(); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM) + << "server process failed incorrectly: " << WaitStatusToString(wstatus); + }); + proc.expectAlreadyShutdown = true; +} + +TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::NONE, + RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + EXPECT_EQ(status.transactionError(), FDS_NOT_ALLOWED) << status; +} + +TEST_P(BinderRpc, ReceiveFile) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->echoAsFile("hello", &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "hello"); +} + +TEST_P(BinderRpc, SendFiles) { + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("123"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("b"))); + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("cd"))); + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + if (!supportsFdTransport()) { + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; + return; + } + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, "123abcd"); +} + +TEST_P(BinderRpc, SendMaxFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 253; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + ASSERT_TRUE(status.isOk()) << status; + + std::string result; + CHECK(android::base::ReadFdToString(out.get(), &result)); + EXPECT_EQ(result, std::string(253, 'a')); +} + +TEST_P(BinderRpc, SendTooManyFiles) { + if (!supportsFdTransport()) { + GTEST_SKIP() << "Would fail trivially (which is tested by BinderRpc::SendFiles)"; + } + + auto proc = createRpcTestSocketServerProcess({ + .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, + .serverSupportedFileDescriptorTransportModes = + {RpcSession::FileDescriptorTransportMode::UNIX}, + }); + + std::vector<android::os::ParcelFileDescriptor> files; + for (int i = 0; i < 254; i++) { + files.emplace_back(android::os::ParcelFileDescriptor(mockFileDescriptor("a"))); + } + + android::os::ParcelFileDescriptor out; + auto status = proc.rootIface->concatFiles(files, &out); + EXPECT_EQ(status.transactionError(), BAD_VALUE) << status; +} + TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1335,6 +1374,10 @@ TEST_P(BinderRpc, WorksWithLibbinderNdkPing) { } TEST_P(BinderRpc, WorksWithLibbinderNdkUserTransaction) { + if constexpr (!kEnableSharedLibs) { + GTEST_SKIP() << "Test disabled because Binder was built as a static library"; + } + auto proc = createRpcTestSocketServerProcess({}); ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(proc.rootBinder)); @@ -1360,6 +1403,10 @@ ssize_t countFds() { } TEST_P(BinderRpc, Fds) { + if (serverSingleThreaded()) { + GTEST_SKIP() << "This test requires multiple threads"; + } + ssize_t beforeFds = countFds(); ASSERT_GE(beforeFds, 0); { @@ -1382,20 +1429,90 @@ TEST_P(BinderRpc, AidlDelegatorTest) { static bool testSupportVsockLoopback() { // We don't need to enable TLS to know if vsock is supported. unsigned int vsockPort = allocateVsockPort(); - sp<RpcServer> server = RpcServer::make(RpcTransportCtxFactoryRaw::make()); - if (status_t status = server->setupVsockServer(vsockPort); status != OK) { - if (status == -EAFNOSUPPORT) { - return false; + + android::base::unique_fd serverFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno)); + + sockaddr_vm serverAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_ANY, + }; + int ret = TEMP_FAILURE_RETRY( + bind(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr))); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not bind socket to port %u: %s", vsockPort, + strerror(errno)); + + ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/)); + LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", vsockPort, + strerror(errno)); + + // Try to connect to the server using the VMADDR_CID_LOCAL cid + // to see if the kernel supports it. It's safe to use a blocking + // connect because vsock sockets have a 2 second connection timeout, + // and they return ETIMEDOUT after that. + android::base::unique_fd connectFd( + TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))); + LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort, + strerror(errno)); + + bool success = false; + sockaddr_vm connectAddr{ + .svm_family = AF_VSOCK, + .svm_port = vsockPort, + .svm_cid = VMADDR_CID_LOCAL, + }; + ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr), + sizeof(connectAddr))); + if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) { + android::base::unique_fd acceptFd; + while (true) { + pollfd pfd[]{ + {.fd = serverFd.get(), .events = POLLIN, .revents = 0}, + {.fd = connectFd.get(), .events = POLLOUT, .revents = 0}, + }; + ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Error polling: %s", strerror(errno)); + + if (pfd[0].revents & POLLIN) { + sockaddr_vm acceptAddr; + socklen_t acceptAddrLen = sizeof(acceptAddr); + ret = TEMP_FAILURE_RETRY(accept4(serverFd.get(), + reinterpret_cast<sockaddr*>(&acceptAddr), + &acceptAddrLen, SOCK_CLOEXEC)); + LOG_ALWAYS_FATAL_IF(ret < 0, "Could not accept4 socket: %s", strerror(errno)); + LOG_ALWAYS_FATAL_IF(acceptAddrLen != static_cast<socklen_t>(sizeof(acceptAddr)), + "Truncated address"); + + // Store the fd in acceptFd so we keep the connection alive + // while polling connectFd + acceptFd.reset(ret); + } + + if (pfd[1].revents & POLLOUT) { + // Connect either succeeded or timed out + int connectErrno; + socklen_t connectErrnoLen = sizeof(connectErrno); + int ret = getsockopt(connectFd.get(), SOL_SOCKET, SO_ERROR, &connectErrno, + &connectErrnoLen); + LOG_ALWAYS_FATAL_IF(ret == -1, + "Could not getsockopt() after connect() " + "on non-blocking socket: %s.", + strerror(errno)); + + // We're done, this is all we wanted + success = connectErrno == 0; + break; + } } - LOG_ALWAYS_FATAL("Could not setup vsock server: %s", statusToString(status).c_str()); + } else { + success = ret == 0; } - server->start(); - sp<RpcSession> session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); - status_t status = session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort); - while (!server->shutdown()) usleep(10000); - ALOGE("Detected vsock loopback supported: %s", statusToString(status).c_str()); - return status == OK; + ALOGE("Detected vsock loopback supported: %s", success ? "yes" : "no"); + + return success; } static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { @@ -1412,9 +1529,22 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } +static std::vector<uint32_t> testVersions() { + std::vector<uint32_t> versions; + for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) { + versions.push_back(i); + } + versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); + return versions; +} + INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::Combine(::testing::ValuesIn(testSocketTypes()), - ::testing::ValuesIn(RpcSecurityValues())), + ::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions()), + ::testing::ValuesIn(testVersions()), + ::testing::Values(false, true), + ::testing::Values(false, true)), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1468,37 +1598,17 @@ private: bool mValue = false; }; -TEST_P(BinderRpcSimple, Shutdown) { - auto addr = allocateSocketAddress(); - auto server = RpcServer::make(newFactory(GetParam())); - ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); - auto joinEnds = std::make_shared<OneOffSignal>(); - - // If things are broken and the thread never stops, don't block other tests. Because the thread - // may run after the test finishes, it must not access the stack memory of the test. Hence, - // shared pointers are passed. - std::thread([server, joinEnds] { - server->join(); - joinEnds->notify(); - }).detach(); - - bool shutdown = false; - for (int i = 0; i < 10 && !shutdown; i++) { - usleep(300 * 1000); // 300ms; total 3s - if (server->shutdown()) shutdown = true; - } - ASSERT_TRUE(shutdown) << "server->shutdown() never returns true"; - - ASSERT_TRUE(joinEnds->wait(2s)) - << "After server->shutdown() returns true, join() did not stop after 2s"; -} - TEST(BinderRpc, Java) { #if !defined(__ANDROID__) GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on" "createRpcDelegateServiceManager() with a device attached, such test belongs " "to binderHostDeviceTest. Hence, just disable this test on host."; #endif // !__ANDROID__ + if constexpr (!kEnableKernelIpc) { + GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled " + "at build time."; + } + sp<IServiceManager> sm = defaultServiceManager(); ASSERT_NE(nullptr, sm); // Any Java service with non-empty getInterfaceDescriptor() would do. @@ -1540,12 +1650,68 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } -INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), - BinderRpcSimple::PrintTestParam); +class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { +public: + static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { + return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + + std::to_string(std::get<1>(info.param)); + } +}; + +TEST_P(BinderRpcServerOnly, SetExternalServerTest) { + base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); + int sinkFd = sink.get(); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_FALSE(server->hasServer()); + ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); + ASSERT_TRUE(server->hasServer()); + base::unique_fd retrieved = server->releaseServer(); + ASSERT_FALSE(server->hasServer()); + ASSERT_EQ(sinkFd, retrieved.get()); +} + +TEST_P(BinderRpcServerOnly, Shutdown) { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + + auto addr = allocateSocketAddress(); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); + ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); + auto joinEnds = std::make_shared<OneOffSignal>(); + + // If things are broken and the thread never stops, don't block other tests. Because the thread + // may run after the test finishes, it must not access the stack memory of the test. Hence, + // shared pointers are passed. + std::thread([server, joinEnds] { + server->join(); + joinEnds->notify(); + }).detach(); + + bool shutdown = false; + for (int i = 0; i < 10 && !shutdown; i++) { + usleep(300 * 1000); // 300ms; total 3s + if (server->shutdown()) shutdown = true; + } + ASSERT_TRUE(shutdown) << "server->shutdown() never returns true"; + + ASSERT_TRUE(joinEnds->wait(2s)) + << "After server->shutdown() returns true, join() did not stop after 2s"; +} + +INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, + ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions())), + BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: - using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; + // Only parameterized only server version because `RpcSession` is bypassed + // in the client half of the tests. + using Param = + std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. @@ -1557,8 +1723,9 @@ public: [[nodiscard]] AssertionResult setUp( const Param& param, std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); + rpcServer->setProtocolVersion(serverVersion); switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; @@ -1606,7 +1773,7 @@ public: } } mFd = rpcServer->releaseServer(); - if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd"; + if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd"; mCtx = newFactory(rpcSecurity, mCertVerifier, std::move(auth))->newServerCtx(); if (mCtx == nullptr) return AssertionFailure() << "newServerCtx"; mSetup = true; @@ -1627,7 +1794,7 @@ public: std::vector<std::thread> threads; while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) { base::unique_fd acceptedFd( - TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/, + TEMP_FAILURE_RETRY(accept4(mFd.fd.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC | SOCK_NONBLOCK))); threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd)); } @@ -1636,7 +1803,8 @@ public: } void handleOne(android::base::unique_fd acceptedFd) { ASSERT_TRUE(acceptedFd.ok()); - auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get()); + RpcTransportFd transportFd(std::move(acceptedFd)); + auto serverTransport = mCtx->newTransport(std::move(transportFd), mFdTrigger.get()); if (serverTransport == nullptr) return; // handshake failed ASSERT_TRUE(mPostConnect(serverTransport.get(), mFdTrigger.get())); } @@ -1655,7 +1823,7 @@ public: std::unique_ptr<std::thread> mThread; ConnectToServer mConnectToServer; std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); - base::unique_fd mFd; + RpcTransportFd mFd; std::unique_ptr<RpcTransportCtx> mCtx; std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = std::make_shared<RpcCertificateVerifierSimple>(); @@ -1676,7 +1844,8 @@ public: FdTrigger* fdTrigger) { std::string message(kMessage); iovec messageIov{message.data(), message.size()}; - auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {}); + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); return AssertionSuccess(); } @@ -1687,7 +1856,8 @@ public: explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} Client(Client&&) = default; [[nodiscard]] AssertionResult setUp(const Param& param) { - auto [socketType, rpcSecurity, certificateFormat] = param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; + (void)serverVersion; mFdTrigger = FdTrigger::make(); mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; @@ -1700,7 +1870,7 @@ public: // connect() and do handshake bool setUpTransport() { mFd = mConnectToServer(); - if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server"; + if (!mFd.fd.ok()) return AssertionFailure() << "Cannot connect to server"; mClientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get()); return mClientTransport != nullptr; } @@ -1708,8 +1878,9 @@ public: LOG_ALWAYS_FATAL_IF(mClientTransport == nullptr, "setUpTransport not called or failed"); std::string readMessage(expectedMessage.size(), '\0'); iovec readMessageIov{readMessage.data(), readMessage.size()}; - status_t readStatus = mClientTransport->interruptableReadFully(mFdTrigger.get(), - &readMessageIov, 1, {}); + status_t readStatus = + mClientTransport->interruptableReadFully(mFdTrigger.get(), &readMessageIov, 1, + std::nullopt, nullptr); if (readStatus != OK) { return AssertionFailure() << statusToString(readStatus); } @@ -1728,9 +1899,11 @@ public: ASSERT_EQ(readOk, readMessage()); } + bool isTransportWaiting() { return mClientTransport->isWaiting(); } + private: ConnectToServer mConnectToServer; - base::unique_fd mFd; + RpcTransportFd mFd; std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); std::unique_ptr<RpcTransportCtx> mCtx; std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = @@ -1757,23 +1930,28 @@ public: using Server = RpcTransportTestUtils::Server; using Client = RpcTransportTestUtils::Client; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, rpcSecurity, certificateFormat] = info.param; + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param; auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString(); if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat); + ret += "_serverV" + std::to_string(serverVersion); return ret; } static std::vector<ParamType> getRpcTranportTestParams() { std::vector<ParamType> ret; - for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { - for (auto rpcSecurity : RpcSecurityValues()) { - switch (rpcSecurity) { - case RpcSecurity::RAW: { - ret.emplace_back(socketType, rpcSecurity, std::nullopt); - } break; - case RpcSecurity::TLS: { - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM); - ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER); - } break; + for (auto serverVersion : testVersions()) { + for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { + for (auto rpcSecurity : RpcSecurityValues()) { + switch (rpcSecurity) { + case RpcSecurity::RAW: { + ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion); + } break; + case RpcSecurity::TLS: { + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM, + serverVersion); + ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER, + serverVersion); + } break; + } } } } @@ -1781,9 +1959,15 @@ public: } template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b); } + void SetUp() override { + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + } }; TEST_P(RpcTransportTest, GoodCertificate) { @@ -1817,7 +2001,8 @@ TEST_P(RpcTransportTest, MultipleClients) { } TEST_P(RpcTransportTest, UntrustedServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; auto untrustedServer = std::make_unique<Server>(); ASSERT_TRUE(untrustedServer->setUp(GetParam())); @@ -1835,7 +2020,9 @@ TEST_P(RpcTransportTest, UntrustedServer) { client.run(handshakeOk); } TEST_P(RpcTransportTest, MaliciousServer) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto validServer = std::make_unique<Server>(); ASSERT_TRUE(validServer->setUp(GetParam())); @@ -1858,7 +2045,9 @@ TEST_P(RpcTransportTest, MaliciousServer) { } TEST_P(RpcTransportTest, UntrustedClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1877,7 +2066,9 @@ TEST_P(RpcTransportTest, UntrustedClient) { } TEST_P(RpcTransportTest, MaliciousClient) { - auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); + (void)serverVersion; + auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); @@ -1904,7 +2095,8 @@ TEST_P(RpcTransportTest, Trigger) { auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) { std::string message(RpcTransportTestUtils::kMessage); iovec messageIov{message.data(), message.size()}; - auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, {}); + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); if (status != OK) return AssertionFailure() << statusToString(status); { @@ -1915,7 +2107,8 @@ TEST_P(RpcTransportTest, Trigger) { } iovec msg2Iov{msg2.data(), msg2.size()}; - status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, {}); + status = serverTransport->interruptableWriteFully(fdTrigger, &msg2Iov, 1, std::nullopt, + nullptr); if (status != DEAD_OBJECT) return AssertionFailure() << "When FdTrigger is shut down, interruptableWriteFully " "should return DEAD_OBJECT, but it is " @@ -1957,28 +2150,83 @@ TEST_P(RpcTransportTest, Trigger) { ASSERT_FALSE(client.readMessage(msg2)); } +TEST_P(RpcTransportTest, CheckWaitingForRead) { + std::mutex readMutex; + std::condition_variable readCv; + bool shouldContinueReading = false; + // Server will write data on transport once its started + auto serverPostConnect = [&](RpcTransport* serverTransport, FdTrigger* fdTrigger) { + std::string message(RpcTransportTestUtils::kMessage); + iovec messageIov{message.data(), message.size()}; + auto status = serverTransport->interruptableWriteFully(fdTrigger, &messageIov, 1, + std::nullopt, nullptr); + if (status != OK) return AssertionFailure() << statusToString(status); + + { + std::unique_lock<std::mutex> lock(readMutex); + shouldContinueReading = true; + lock.unlock(); + readCv.notify_all(); + } + return AssertionSuccess(); + }; + + // Setup Server and client + auto server = std::make_unique<Server>(); + ASSERT_TRUE(server->setUp(GetParam())); + + Client client(server->getConnectToServerFn()); + ASSERT_TRUE(client.setUp(GetParam())); + + ASSERT_EQ(OK, trust(&client, server)); + ASSERT_EQ(OK, trust(server, &client)); + server->setPostConnect(serverPostConnect); + + server->start(); + ASSERT_TRUE(client.setUpTransport()); + { + // Wait till server writes data + std::unique_lock<std::mutex> lock(readMutex); + ASSERT_TRUE(readCv.wait_for(lock, 3s, [&] { return shouldContinueReading; })); + } + + // Since there is no read polling here, we will get polling count 0 + ASSERT_FALSE(client.isTransportWaiting()); + ASSERT_TRUE(client.readMessage(RpcTransportTestUtils::kMessage)); + // Thread should increment polling count, read and decrement polling count + // Again, polling count should be zero here + ASSERT_FALSE(client.isTransportWaiting()); + + server->shutdown(); +} + INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, ::testing::ValuesIn(RpcTransportTest::getRpcTranportTestParams()), RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest - : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { + : public testing::TestWithParam< + std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, certificateFormat, keyFormat] = info.param; - auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + - "_key_" + PrintToString(keyFormat); - return ret; + auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; + return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + if constexpr (!kEnableRpcThreads) { + GTEST_SKIP() << "Test skipped because threads were disabled at build time"; + } + + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { @@ -1993,8 +2241,8 @@ TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat); auto desCert = deserializeCertificate(certData, certificateFormat); auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert)); - auto utilsParam = - std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat)); + auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, + std::make_optional(certificateFormat), serverVersion); auto server = std::make_unique<RpcTransportTestUtils::Server>(); ASSERT_TRUE(server->setUp(utilsParam, std::move(auth))); @@ -2013,7 +2261,8 @@ INSTANTIATE_TEST_CASE_P( BinderRpc, RpcTransportTlsKeyTest, testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)), testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER), - testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)), + testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER), + testing::ValuesIn(testVersions())), RpcTransportTlsKeyTest::PrintParamInfo); } // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.cpp b/libs/binder/tests/binderRpcTestCommon.cpp new file mode 100644 index 0000000000..0d9aa952eb --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "binderRpcTestCommon.h" + +namespace android { + +std::atomic<int32_t> MyBinderRpcSession::gNum; +sp<IBinder> MyBinderRpcTest::mHeldBinder; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h new file mode 100644 index 0000000000..4513d36a65 --- /dev/null +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <BinderRpcTestClientInfo.h> +#include <BinderRpcTestServerConfig.h> +#include <BinderRpcTestServerInfo.h> +#include <BnBinderRpcCallback.h> +#include <BnBinderRpcSession.h> +#include <BnBinderRpcTest.h> +#include <aidl/IBinderRpcTest.h> +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/binder_auto_utils.h> +#include <android/binder_libbinder.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTlsTestUtils.h> +#include <binder/RpcTlsUtils.h> +#include <binder/RpcTransport.h> +#include <binder/RpcTransportRaw.h> +#include <binder/RpcTransportTls.h> +#include <unistd.h> +#include <string> +#include <vector> + +#include <signal.h> + +#include "../BuildFlags.h" +#include "../FdTrigger.h" +#include "../RpcSocketAddress.h" // for testing preconnected clients +#include "../RpcState.h" // for debugging +#include "../vm_sockets.h" // for VMADDR_* +#include "utils/Errors.h" + +namespace android { + +constexpr char kLocalInetAddress[] = "127.0.0.1"; + +enum class RpcSecurity { RAW, TLS }; + +static inline std::vector<RpcSecurity> RpcSecurityValues() { + return {RpcSecurity::RAW, RpcSecurity::TLS}; +} + +enum class SocketType { + PRECONNECTED, + UNIX, + VSOCK, + INET, +}; +static inline std::string PrintToString(SocketType socketType) { + switch (socketType) { + case SocketType::PRECONNECTED: + return "preconnected_uds"; + case SocketType::UNIX: + return "unix_domain_socket"; + case SocketType::VSOCK: + return "vm_socket"; + case SocketType::INET: + return "inet_socket"; + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + return ""; + } +} + +struct BinderRpcOptions { + size_t numThreads = 1; + size_t numSessions = 1; + size_t numIncomingConnections = 0; + size_t numOutgoingConnections = SIZE_MAX; + RpcSession::FileDescriptorTransportMode clientFileDescriptorTransportMode = + RpcSession::FileDescriptorTransportMode::NONE; + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes = { + RpcSession::FileDescriptorTransportMode::NONE}; + + // If true, connection failures will result in `ProcessSession::sessions` being empty + // instead of a fatal error. + bool allowConnectFailure = false; +}; + +static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { + uint64_t length = str.length(); + CHECK(android::base::WriteFully(fd, &length, sizeof(length))); + CHECK(android::base::WriteFully(fd, str.data(), str.length())); +} + +static inline std::string readString(android::base::borrowed_fd fd) { + uint64_t length; + CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + std::string ret(length, '\0'); + CHECK(android::base::ReadFully(fd, ret.data(), length)); + return ret; +} + +static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { + Parcel parcel; + CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); +} + +template <typename T> +static inline T readFromFd(android::base::borrowed_fd fd) { + std::string data = readString(fd); + Parcel parcel; + CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); + T object; + CHECK_EQ(OK, object.readFromParcel(&parcel)); + return object; +} + +static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( + RpcSecurity rpcSecurity, std::shared_ptr<RpcCertificateVerifier> verifier = nullptr, + std::unique_ptr<RpcAuth> auth = nullptr) { + switch (rpcSecurity) { + case RpcSecurity::RAW: + return RpcTransportCtxFactoryRaw::make(); + case RpcSecurity::TLS: { + if (verifier == nullptr) { + verifier = std::make_shared<RpcCertificateVerifierSimple>(); + } + if (auth == nullptr) { + auth = std::make_unique<RpcAuthSelfSigned>(); + } + return RpcTransportCtxFactoryTls::make(std::move(verifier), std::move(auth)); + } + default: + LOG_ALWAYS_FATAL("Unknown RpcSecurity %d", rpcSecurity); + } +} + +// Create an FD that returns `contents` when read. +static inline base::unique_fd mockFileDescriptor(std::string contents) { + android::base::unique_fd readFd, writeFd; + CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno); + RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() { + signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write + if (!WriteStringToFd(contents, writeFd)) { + int savedErrno = errno; + LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s", + strerror(savedErrno)); + } + }).detach(); + return readFd; +} + +using android::binder::Status; + +class MyBinderRpcSession : public BnBinderRpcSession { +public: + static std::atomic<int32_t> gNum; + + MyBinderRpcSession(const std::string& name) : mName(name) { gNum++; } + Status getName(std::string* name) override { + *name = mName; + return Status::ok(); + } + ~MyBinderRpcSession() { gNum--; } + +private: + std::string mName; +}; + +class MyBinderRpcCallback : public BnBinderRpcCallback { + Status sendCallback(const std::string& value) { + RpcMutexUniqueLock _l(mMutex); + mValues.push_back(value); + _l.unlock(); + mCv.notify_one(); + return Status::ok(); + } + Status sendOnewayCallback(const std::string& value) { return sendCallback(value); } + +public: + RpcMutex mMutex; + RpcConditionVariable mCv; + std::vector<std::string> mValues; +}; + +class MyBinderRpcTest : public BnBinderRpcTest { +public: + wp<RpcServer> server; + int port = 0; + + Status sendString(const std::string& str) override { + (void)str; + return Status::ok(); + } + Status doubleString(const std::string& str, std::string* strstr) override { + *strstr = str + str; + return Status::ok(); + } + Status getClientPort(int* out) override { + *out = port; + return Status::ok(); + } + Status countBinders(std::vector<int32_t>* out) override { + sp<RpcServer> spServer = server.promote(); + if (spServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + out->clear(); + for (auto session : spServer->listSessions()) { + size_t count = session->state()->countBinders(); + out->push_back(count); + } + return Status::ok(); + } + Status getNullBinder(sp<IBinder>* out) override { + out->clear(); + return Status::ok(); + } + Status pingMe(const sp<IBinder>& binder, int32_t* out) override { + if (binder == nullptr) { + std::cout << "Received null binder!" << std::endl; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + *out = binder->pingBinder(); + return Status::ok(); + } + Status repeatBinder(const sp<IBinder>& binder, sp<IBinder>* out) override { + *out = binder; + return Status::ok(); + } + static sp<IBinder> mHeldBinder; + Status holdBinder(const sp<IBinder>& binder) override { + mHeldBinder = binder; + return Status::ok(); + } + Status getHeldBinder(sp<IBinder>* held) override { + *held = mHeldBinder; + return Status::ok(); + } + Status nestMe(const sp<IBinderRpcTest>& binder, int count) override { + if (count <= 0) return Status::ok(); + return binder->nestMe(this, count - 1); + } + Status alwaysGiveMeTheSameBinder(sp<IBinder>* out) override { + static sp<IBinder> binder = new BBinder; + *out = binder; + return Status::ok(); + } + Status openSession(const std::string& name, sp<IBinderRpcSession>* out) override { + *out = new MyBinderRpcSession(name); + return Status::ok(); + } + Status getNumOpenSessions(int32_t* out) override { + *out = MyBinderRpcSession::gNum; + return Status::ok(); + } + + RpcMutex blockMutex; + Status lock() override { + blockMutex.lock(); + return Status::ok(); + } + Status unlockInMsAsync(int32_t ms) override { + usleep(ms * 1000); + blockMutex.unlock(); + return Status::ok(); + } + Status lockUnlock() override { + RpcMutexLockGuard _l(blockMutex); + return Status::ok(); + } + + Status sleepMs(int32_t ms) override { + usleep(ms * 1000); + return Status::ok(); + } + + Status sleepMsAsync(int32_t ms) override { + // In-process binder calls are asynchronous, but the call to this method + // is synchronous wrt its client. This in/out-process threading model + // diffentiation is a classic binder leaky abstraction (for better or + // worse) and is preserved here the way binder sockets plugs itself + // into BpBinder, as nothing is changed at the higher levels + // (IInterface) which result in this behavior. + return sleepMs(ms); + } + + Status doCallback(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + if (callback == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + if (delayed) { + RpcMaybeThread([=]() { + ALOGE("Executing delayed callback: '%s'", value.c_str()); + Status status = doCallback(callback, oneway, false, value); + ALOGE("Delayed callback status: '%s'", status.toString8().c_str()); + }).detach(); + return Status::ok(); + } + + if (oneway) { + return callback->sendOnewayCallback(value); + } + + return callback->sendCallback(value); + } + + Status doCallbackAsync(const sp<IBinderRpcCallback>& callback, bool oneway, bool delayed, + const std::string& value) override { + return doCallback(callback, oneway, delayed, value); + } + + Status die(bool cleanup) override { + if (cleanup) { + exit(1); + } else { + _exit(1); + } + } + + Status scheduleShutdown() override { + sp<RpcServer> strongServer = server.promote(); + if (strongServer == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + RpcMaybeThread([=] { + LOG_ALWAYS_FATAL_IF(!strongServer->shutdown(), "Could not shutdown"); + }).detach(); + return Status::ok(); + } + + Status useKernelBinderCallingId() override { + // this is WRONG! It does not make sense when using RPC binder, and + // because it is SO wrong, and so much code calls this, it should abort! + + if constexpr (kEnableKernelIpc) { + (void)IPCThreadState::self()->getCallingPid(); + } + return Status::ok(); + } + + Status echoAsFile(const std::string& content, android::os::ParcelFileDescriptor* out) override { + out->reset(mockFileDescriptor(content)); + return Status::ok(); + } + + Status concatFiles(const std::vector<android::os::ParcelFileDescriptor>& files, + android::os::ParcelFileDescriptor* out) override { + std::string acc; + for (const auto& file : files) { + std::string result; + CHECK(android::base::ReadFdToString(file.get(), &result)); + acc.append(result); + } + out->reset(mockFileDescriptor(acc)); + return Status::ok(); + } +}; + +} // namespace android diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp new file mode 100644 index 0000000000..31eb5dadf1 --- /dev/null +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "binderRpcTestCommon.h" + +using namespace android; + +int main(int argc, const char* argv[]) { + LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); + base::unique_fd writeEnd(atoi(argv[1])); + base::unique_fd readEnd(atoi(argv[2])); + + auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd); + auto socketType = static_cast<SocketType>(serverConfig.socketType); + auto rpcSecurity = static_cast<RpcSecurity>(serverConfig.rpcSecurity); + + std::vector<RpcSession::FileDescriptorTransportMode> + serverSupportedFileDescriptorTransportModes; + for (auto mode : serverConfig.serverSupportedFileDescriptorTransportModes) { + serverSupportedFileDescriptorTransportModes.push_back( + static_cast<RpcSession::FileDescriptorTransportMode>(mode)); + } + + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); + + server->setProtocolVersion(serverConfig.serverVersion); + server->setMaxThreads(serverConfig.numThreads); + server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes); + + unsigned int outPort = 0; + + switch (socketType) { + case SocketType::PRECONNECTED: + [[fallthrough]]; + case SocketType::UNIX: + CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str())) + << serverConfig.addr; + break; + case SocketType::VSOCK: + CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort)); + break; + case SocketType::INET: { + CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort)); + CHECK_NE(0, outPort); + break; + } + default: + LOG_ALWAYS_FATAL("Unknown socket type"); + } + + BinderRpcTestServerInfo serverInfo; + serverInfo.port = static_cast<int64_t>(outPort); + serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); + writeToFd(writeEnd, serverInfo); + auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); + + if (rpcSecurity == RpcSecurity::TLS) { + for (const auto& clientCert : clientInfo.certs) { + CHECK_EQ(OK, + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); + } + } + + server->setPerSessionRootObject([&](const void* addrPtr, size_t len) { + // UNIX sockets with abstract addresses return + // sizeof(sa_family_t)==2 in addrlen + CHECK_GE(len, sizeof(sa_family_t)); + const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr); + sp<MyBinderRpcTest> service = sp<MyBinderRpcTest>::make(); + switch (addr->sa_family) { + case AF_UNIX: + // nothing to save + break; + case AF_VSOCK: + CHECK_EQ(len, sizeof(sockaddr_vm)); + service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port; + break; + case AF_INET: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port); + break; + case AF_INET6: + CHECK_EQ(len, sizeof(sockaddr_in)); + service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized address family %d", addr->sa_family); + } + service->server = server; + return service; + }); + + server->join(); + + // Another thread calls shutdown. Wait for it to complete. + (void)server->shutdown(); + + return 0; +} diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 4fcf42d6e5..3dab2c748b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -233,11 +233,15 @@ const std::string kCurrentRepr = "0100000025000000|03000000|00000000|ffffffff|03000000|00000000|00000000|" "07000000020000003a0044000000000000000000|f8ffffff020000003a002f00000000000000000008000000"; +TEST(RpcWire, V0) { + checkRepr(kCurrentRepr, 0); +} + TEST(RpcWire, CurrentVersion) { checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION); } -static_assert(RPC_WIRE_PROTOCOL_VERSION == 0, +static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL, "If the binder wire protocol is updated, this test should test additional versions. " "The binder wire protocol should only be updated on upstream AOSP."); diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index 3b1faa8c2f..cfaf2a987f 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -249,12 +249,13 @@ Pipe make_worker(int num, int iterations, int worker_count, int payload_size, bo pid_t pid = fork(); if (pid) { /* parent */ - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } else { /* child */ - worker_fx(num, worker_count, iterations, payload_size, cs_pair, move(get<1>(pipe_pair))); + worker_fx(num, worker_count, iterations, payload_size, cs_pair, + std::move(get<1>(pipe_pair))); /* never get here */ - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } } diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index e5d32da4cc..0210237ed8 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -7,6 +7,22 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "binderReadParcelIface", + host_supported: true, + unstable: true, + srcs: [ + "EmptyParcelable.aidl", + "SingleDataParcelable.aidl", + "GenericDataParcelable.aidl", + ], + backend: { + java: { + enabled: false, + }, + }, +} + cc_fuzz { name: "binder_parcel_fuzzer", host_supported: true, @@ -29,6 +45,8 @@ cc_fuzz { "libcutils", "libhidlbase", "liblog", + "binderReadParcelIface-cpp", + "binderReadParcelIface-ndk", ], target: { @@ -59,6 +77,7 @@ cc_fuzz { cc_library_static { name: "libbinder_random_parcel", host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl new file mode 100644 index 0000000000..96d6223d3d --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable EmptyParcelable{ +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl new file mode 100644 index 0000000000..fc2542b36c --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable GenericDataParcelable { + int data; + float majorVersion; + float minorVersion; + IBinder binder; + ParcelFileDescriptor fileDescriptor; + int[] array; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl new file mode 100644 index 0000000000..d62891b26a --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable SingleDataParcelable{ + int data; +}
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 47ec776990..9dac2c98a7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder" #include "binder.h" +#include "EmptyParcelable.h" +#include "GenericDataParcelable.h" +#include "SingleDataParcelable.h" #include "util.h" #include <android-base/hex.h> @@ -73,20 +76,20 @@ struct BigStruct { uint8_t data[1337]; }; -#define PARCEL_READ_WITH_STATUS(T, FUN) \ - [] (const ::android::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ - T t{};\ - status_t status = p.FUN(&t);\ - FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\ +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ + T t{}; \ + status_t status = p.FUN(&t); \ + FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \ } -#define PARCEL_READ_NO_STATUS(T, FUN) \ - [] (const ::android::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ - T t = p.FUN();\ - (void) t;\ - FUZZ_LOG() << #T " done " /* << " value: " << t*/;\ +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [](const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \ + T t = p.FUN(); \ + (void)t; \ + FUZZ_LOG() << #T " done " /* << " value: " << t*/; \ } #define PARCEL_READ_OPT_STATUS(T, FUN) \ @@ -102,7 +105,9 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, dataPosition), PARCEL_READ_NO_STATUS(size_t, dataCapacity), PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail), - [] (const ::android::Parcel& p, uint8_t pos) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to setDataPosition: " << pos; p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; @@ -111,13 +116,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), - [] (const ::android::Parcel& p, uint8_t len) { - std::string interface(len, 'a'); + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + std::string interface = provider.ConsumeRandomLengthString(); FUZZ_LOG() << "about to enforceInterface: " << interface; bool b = p.enforceInterface(::android::String16(interface.c_str())); FUZZ_LOG() << "enforced interface: " << b; }, - [] (const ::android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to checkInterface"; android::sp<android::IBinder> aBinder = new android::BBinder(); bool b = p.checkInterface(aBinder.get()); @@ -125,13 +130,16 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { }, PARCEL_READ_NO_STATUS(size_t, objectsCount), PARCEL_READ_NO_STATUS(status_t, errorCheck), - [] (const ::android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + // Read at least a bit. Unbounded allocation would OOM. + size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024); FUZZ_LOG() << "about to read void*"; std::vector<uint8_t> data(len); status_t status = p.read(data.data(), len); FUZZ_LOG() << "read status: " << status; }, - [] (const ::android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + size_t len = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readInplace"; const void* r = p.readInplace(len); FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << (r ? HexString(r, len) : "null"); @@ -149,13 +157,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16), PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16), PARCEL_READ_WITH_STATUS(std::optional<std::string>, readUtf8FromUtf16), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read c-str"; const char* str = p.readCString(); FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>"); }, PARCEL_READ_OPT_STATUS(android::String8, readString8), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString8Inplace"; size_t outLen = 0; const char* str = p.readString8Inplace(&outLen); @@ -165,7 +173,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_OPT_STATUS(android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16), PARCEL_READ_WITH_STATUS(std::optional<android::String16>, readString16), - [] (const ::android::Parcel& p, uint8_t /*data*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString16Inplace"; size_t outLen = 0; const char16_t* str = p.readString16Inplace(&outLen); @@ -263,13 +271,13 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray), #undef COMMA - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read flattenable"; ExampleFlattenable f; status_t status = p.read(f); FUZZ_LOG() << "read flattenable: " << status; }, - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read lite flattenable"; ExampleLightFlattenable f; status_t status = p.read(f); @@ -284,7 +292,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<BigStruct>>, resizeOutVector), PARCEL_READ_NO_STATUS(int32_t, readExceptionCode), - [] (const android::Parcel& p, uint8_t /*len*/) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readNativeHandle"; native_handle_t* t = p.readNativeHandle(); FUZZ_LOG() << "readNativeHandle: " << t; @@ -303,15 +311,16 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector), PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector), - [] (const android::Parcel& p, uint8_t len) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { + size_t len = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readBlob"; ::android::Parcel::ReadableBlob blob; status_t status = p.readBlob(len, &blob); FUZZ_LOG() << "readBlob status: " << status; }, - [] (const android::Parcel& p, uint8_t options) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to readObject"; - bool nullMetaData = options & 0x1; + bool nullMetaData = provider.ConsumeBool(); const void* obj = static_cast<const void*>(p.readObject(nullMetaData)); FUZZ_LOG() << "readObject: " << obj; }, @@ -319,20 +328,19 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize), // additional parcelable objects defined in libbinder - [] (const ::android::Parcel& p, uint8_t data) { + [] (const ::android::Parcel& p, FuzzedDataProvider& provider) { using ::android::os::ParcelableHolder; using ::android::Parcelable; FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status"; - Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL; - if ( (data & 1) == 1 ) { - stability = Parcelable::Stability::STABILITY_VINTF; - } + Parcelable::Stability stability = provider.ConsumeBool() + ? Parcelable::Stability::STABILITY_LOCAL + : Parcelable::Stability::STABILITY_VINTF; ParcelableHolder t = ParcelableHolder(stability); status_t status = p.readParcelable(&t); FUZZ_LOG() << "ParcelableHolder status: " << status; }, PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable), - [] (const ::android::Parcel& p, uint8_t /* data */) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status"; size_t offset = p.readUint32(); size_t length = p.readUint32(); @@ -340,7 +348,7 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.hasFileDescriptorsInRange(offset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, - [] (const ::android::Parcel& p, uint8_t /* data */) { + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to call compareDataInRange() with status"; size_t thisOffset = p.readUint32(); size_t otherOffset = p.readUint32(); @@ -349,6 +357,24 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result); FUZZ_LOG() << " status: " << status << " result: " << result; }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable"; + EmptyParcelable emptyParcelable{}; + status_t status = emptyParcelable.readFromParcel(&p); + FUZZ_LOG() << " status: " << status; + }, + [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable"; + SingleDataParcelable singleDataParcelable; + status_t status = singleDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, + [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable"; + GenericDataParcelable genericDataParcelable; + status_t status = genericDataParcelable.readFromParcel(&p); + FUZZ_LOG() <<" status: " << status; + }, }; // clang-format on #pragma clang diagnostic pop diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 5aeb5cc6f2..af773a02f7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -16,6 +16,9 @@ #define FUZZ_LOG_TAG "binder_ndk" #include "binder_ndk.h" +#include "aidl/EmptyParcelable.h" +#include "aidl/GenericDataParcelable.h" +#include "aidl/SingleDataParcelable.h" #include <android/binder_parcel_utils.h> #include <android/binder_parcelable_utils.h> @@ -70,7 +73,7 @@ binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, } #define PARCEL_READ(T, FUN) \ - [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { \ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ T t{}; \ binder_status_t status = FUN(p.aParcel(), &t); \ @@ -80,32 +83,37 @@ binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, // clang-format off std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ // methods from binder_parcel.h - [](const NdkParcelAdapter& p, uint8_t pos) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to set data position to " << pos; binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos); FUZZ_LOG() << "set data position: " << status; }, - [](const NdkParcelAdapter& p, uint8_t /*data*/) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to read status header"; ndk::ScopedAStatus t; binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR()); FUZZ_LOG() << "read status header: " << status; }, - [](const NdkParcelAdapter& p, uint8_t /*data*/) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to getDataSize the parcel"; AParcel_getDataSize(p.aParcel()); FUZZ_LOG() << "getDataSize done"; }, - [](const NdkParcelAdapter& p, uint8_t data) { + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to read a ParcelableHolder"; - ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF}; + ndk::AParcelableHolder ph {provider.ConsumeBool() ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF}; binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph); FUZZ_LOG() << "read the ParcelableHolder: " << status; }, - [](const NdkParcelAdapter& p, uint8_t data) { - FUZZ_LOG() << "about to appendFrom"; + [](const NdkParcelAdapter& p, FuzzedDataProvider& provider) { + size_t offset = provider.ConsumeIntegral<size_t>(); + size_t pos = provider.ConsumeIntegral<size_t>(); + FUZZ_LOG() << "about to appendFrom " << pos; + // TODO: create random parcel AParcel* parcel = AParcel_create(); - binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data); + binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, offset, pos); AParcel_delete(parcel); FUZZ_LOG() << "appendFrom: " << status; }, @@ -172,5 +180,24 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData), PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData), #undef COMMA + + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable"; + aidl::EmptyParcelable emptyParcelable; + binder_status_t status = emptyParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable"; + aidl::SingleDataParcelable singleDataParcelable; + binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, + [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) { + FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable"; + aidl::GenericDataParcelable genericDataParcelable; + binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel()); + FUZZ_LOG() << "status: " << status; + }, }; // clang-format on diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h index cf24ab932b..d19f25bc88 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h @@ -18,29 +18,31 @@ #include <android/binder_auto_utils.h> #include <vector> +#include <android/binder_libbinder.h> #include <android/binder_parcel.h> #include "parcel_fuzzer.h" -// libbinder_ndk doesn't export this header which breaks down its API for NDK -// and APEX users, but we need access to it to fuzz. -#include "../../ndk/parcel_internal.h" - class NdkParcelAdapter { public: - NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {} + NdkParcelAdapter() : mParcel(AParcel_create()) {} const AParcel* aParcel() const { return mParcel.get(); } AParcel* aParcel() { return mParcel.get(); } - android::Parcel* parcel() { return aParcel()->get(); } + const android::Parcel* parcel() const { return AParcel_viewPlatformParcel(aParcel()); } + android::Parcel* parcel() { return AParcel_viewPlatformParcel(aParcel()); } - const uint8_t* data() const { return aParcel()->get()->data(); } - size_t dataSize() const { return aParcel()->get()->dataSize(); } - size_t dataAvail() const { return aParcel()->get()->dataAvail(); } - size_t dataPosition() const { return aParcel()->get()->dataPosition(); } - size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); } + const uint8_t* data() const { return parcel()->data(); } + size_t dataSize() const { return parcel()->dataSize(); } + size_t dataAvail() const { return parcel()->dataAvail(); } + size_t dataPosition() const { return parcel()->dataPosition(); } + size_t dataCapacity() const { return parcel()->dataCapacity(); } android::status_t setData(const uint8_t* buffer, size_t len) { - return aParcel()->get()->setData(buffer, len); + return parcel()->setData(buffer, len); + } + + android::status_t appendFrom(const NdkParcelAdapter* parcel, int32_t start, int32_t len) { + return AParcel_appendFrom(parcel->aParcel(), aParcel(), start, len); } private: diff --git a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp index ee9840f133..438e8ae9b2 100644 --- a/libs/binder/tests/parcel_fuzzer/hwbinder.cpp +++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp @@ -35,19 +35,19 @@ std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardwa #define PARCEL_READ_OPT_STATUS(T, FUN) \ PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN) -#define PARCEL_READ_NO_STATUS(T, FUN) \ - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ - T t = p.FUN();\ - FUZZ_LOG() << #T " value: " << t;\ +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status"; \ + T t = p.FUN(); \ + FUZZ_LOG() << #T " value: " << t; \ } -#define PARCEL_READ_WITH_STATUS(T, FUN) \ - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ - FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ - T t;\ - status_t status = p.FUN(&t);\ - FUZZ_LOG() << #T " status: " << status << " value: " << t;\ +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [](const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ + T t; \ + status_t status = p.FUN(&t); \ + FUZZ_LOG() << #T " status: " << status << " value: " << t; \ } // clang-format off @@ -56,27 +56,30 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI PARCEL_READ_NO_STATUS(size_t, dataAvail), PARCEL_READ_NO_STATUS(size_t, dataPosition), PARCEL_READ_NO_STATUS(size_t, dataCapacity), - [] (const ::android::hardware::Parcel& p, uint8_t pos) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + // aborts on larger values + size_t pos = provider.ConsumeIntegralInRange<size_t>(0, INT32_MAX); FUZZ_LOG() << "about to setDataPosition: " << pos; p.setDataPosition(pos); FUZZ_LOG() << "setDataPosition done"; }, - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { FUZZ_LOG() << "about to enforceInterface"; - std::string interfaceName(length, 'a'); - bool okay = p.enforceInterface(interfaceName.c_str()); + bool okay = p.enforceInterface(provider.ConsumeRandomLengthString().c_str()); FUZZ_LOG() << "enforceInterface status: " << okay; }, PARCEL_READ_NO_STATUS(size_t, objectsCount), - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + // Read at least a bit. Unbounded allocation would OOM. + size_t length = provider.ConsumeIntegralInRange<size_t>(0, 1024); FUZZ_LOG() << "about to read"; std::vector<uint8_t> data (length); status_t status = p.read(data.data(), length); FUZZ_LOG() << "read status: " << status << " data: " << HexString(data.data(), data.size()); }, - [] (const ::android::hardware::Parcel& p, uint8_t length) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t length = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to read"; - std::vector<uint8_t> data (length); const void* inplace = p.readInplace(length); FUZZ_LOG() << "read status: " << (inplace ? HexString(inplace, length) : "null"); }, @@ -91,14 +94,14 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI PARCEL_READ_OPT_STATUS(float, readFloat), PARCEL_READ_OPT_STATUS(double, readDouble), PARCEL_READ_OPT_STATUS(bool, readBool), - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readCString"; const char* str = p.readCString(); FUZZ_LOG() << "readCString " << (str ? str : "<null>"); }, PARCEL_READ_OPT_STATUS(::android::String16, readString16), PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16), - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readString16Inplace"; size_t outSize = 0; const char16_t* str = p.readString16Inplace(&outSize); @@ -106,7 +109,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI }, PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder), PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder), - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readBuffer"; size_t handle = 0; const void* data = nullptr; @@ -116,7 +120,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readNullableBuffer"; size_t handle = 0; const void* data = nullptr; @@ -126,7 +131,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readEmbeddedBuffer"; size_t handle = 0; size_t parent_buffer_handle = 0; @@ -138,7 +144,8 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t size) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& provider) { + size_t size = provider.ConsumeIntegral<size_t>(); FUZZ_LOG() << "about to readNullableEmbeddedBuffer"; size_t handle = 0; size_t parent_buffer_handle = 0; @@ -150,7 +157,7 @@ std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTI // should be null since we don't create any IPC objects CHECK(data == nullptr) << data; }, - [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + [] (const ::android::hardware::Parcel& p, FuzzedDataProvider& /*provider*/) { FUZZ_LOG() << "about to readNativeHandleNoDup"; const native_handle_t* handle = nullptr; status_t status = p.readNativeHandleNoDup(&handle); diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 843b6e3011..6ea970825b 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h @@ -19,10 +19,14 @@ #include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> +#include <vector> + namespace android { // always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -base::unique_fd getRandomFd(FuzzedDataProvider* provider); +// +// may return multiple FDs (e.g. pipe), but will always return at least one +std::vector<base::unique_fd> getRandomFds(FuzzedDataProvider* provider); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 459fb127c2..27587a9162 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -33,10 +33,13 @@ struct RandomParcelOptions { /** * Fill parcel data, including some random binder objects and FDs * + * May insert additional FDs/binders if they own data related to the Parcel (e.g. the other + * end of a pipe). + * * p - the Parcel to fill * provider - takes ownership and completely consumes provider * writeHeader - optional function to write a specific header once the format of the parcel is * picked (for instance, to write an interface header) */ -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index d5aa353af0..25f609674e 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -17,6 +17,9 @@ #include <fuzzbinder/random_parcel.h> +#include <android-base/logging.h> +#include <binder/ProcessState.h> + namespace android { void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { @@ -44,7 +47,7 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); - fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options); Parcel reply; (void)target->transact(code, data, &reply, flags); @@ -60,6 +63,15 @@ void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); } } + + // invariants + + auto ps = ProcessState::selfOrNull(); + if (ps) { + CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount()) + << "Binder threadpool should not be started by fuzzer because coverage can only " + "cover in-process calls."; + } } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index f435dae659..bef4ab6e99 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -35,17 +35,22 @@ #include <sys/time.h> using android::fillRandomParcel; +using android::RandomParcelOptions; using android::sp; using android::base::HexString; -void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) { +void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // TODO: functionality to create random parcels for libhwbinder parcels + (void)options; + std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>(); p->setData(input.data(), input.size()); } -static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) { +static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { // fill underlying parcel using functions to fill random libbinder parcel - fillRandomParcel(p->parcel(), std::move(provider)); + fillRandomParcel(p->parcel(), std::move(provider), options); } template <typename P, typename B> @@ -55,9 +60,11 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider FUZZ_LOG() << "backend: " << backend; + RandomParcelOptions options; + P reply; P data; - fillRandomParcel(&data, std::move(provider)); + fillRandomParcel(&data, std::move(provider), &options); (void)binder->transact(code, data, &reply, flag); } @@ -73,8 +80,10 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, maxInstructions)); + RandomParcelOptions options; + P p; - fillRandomParcel(&p, std::move(provider)); + fillRandomParcel(&p, std::move(provider), &options); // since we are only using a byte to index CHECK(reads.size() <= 255) << reads.size(); @@ -83,22 +92,39 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize()); FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size()); - for (size_t i = 0; i + 1 < instructions.size(); i += 2) { - uint8_t a = instructions[i]; - uint8_t readIdx = a % reads.size(); + FuzzedDataProvider instructionsProvider(instructions.data(), instructions.size()); + while (instructionsProvider.remaining_bytes() > 0) { + uint8_t idx = instructionsProvider.ConsumeIntegralInRange<uint8_t>(0, reads.size() - 1); - uint8_t b = instructions[i + 1]; + FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail() + << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity(); - FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2 - << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx) - << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize() - << " avail: " << p.dataAvail() << " pos: " << p.dataPosition() - << " cap: " << p.dataCapacity(); - - reads[readIdx](p, b); + reads[idx](p, instructionsProvider); } } +// Append two random parcels. +template <typename P> +void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) { + int32_t start = provider.ConsumeIntegral<int32_t>(); + int32_t len = provider.ConsumeIntegral<int32_t>(); + + std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>( + provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + + // same options so that FDs and binders could be shared in both Parcels + RandomParcelOptions options; + + P p0, p1; + fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options); + fillRandomParcel(&p1, std::move(provider), &options); + + FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "start: " << start << " len: " << len; + + p0.appendFrom(&p1, start, len); +} + void* NothingClass_onCreate(void* args) { return args; } @@ -148,6 +174,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { doReadFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS, std::move(provider)); }, + [](FuzzedDataProvider&& provider) { + doAppendFuzz<::android::Parcel>("binder", std::move(provider)); + }, + [](FuzzedDataProvider&& provider) { + doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider)); + }, }; provider.PickValueInArray(fuzzBackend)(std::move(provider)); diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h index b68a8a91e6..765a93e8c9 100644 --- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h +++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h @@ -15,5 +15,9 @@ */ #pragma once +#include <fuzzer/FuzzedDataProvider.h> + +#include <functional> + template <typename P> -using ParcelRead = std::function<void(const P& p, uint8_t data)>; +using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>; diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index ab0b7e306f..e4dbb2d955 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,50 @@ namespace android { -base::unique_fd getRandomFd(FuzzedDataProvider* provider) { - int fd = provider->PickValueInArray<std::function<int()>>({ - []() { return ashmem_create_region("binder test region", 1024); }, - []() { return open("/dev/null", O_RDWR); }, +using base::unique_fd; + +std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) { + const char* fdType; + + std::vector<unique_fd> fds = provider->PickValueInArray< + std::function<std::vector<unique_fd>()>>({ + [&]() { + fdType = "ashmem"; + std::vector<unique_fd> ret; + ret.push_back(unique_fd( + ashmem_create_region("binder test region", + provider->ConsumeIntegralInRange<size_t>(0, 4096)))); + return ret; + }, + [&]() { + fdType = "/dev/null"; + std::vector<unique_fd> ret; + ret.push_back(unique_fd(open("/dev/null", O_RDWR))); + return ret; + }, + [&]() { + fdType = "pipefd"; + + int pipefds[2]; + + int flags = O_CLOEXEC; + if (provider->ConsumeBool()) flags |= O_DIRECT; + if (provider->ConsumeBool()) flags |= O_NONBLOCK; + + CHECK_EQ(0, pipe2(pipefds, flags)) << flags; + + if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]); + + std::vector<unique_fd> ret; + ret.push_back(unique_fd(pipefds[0])); + ret.push_back(unique_fd(pipefds[1])); + return ret; + }, })(); - CHECK(fd >= 0); - return base::unique_fd(fd); + + for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType; + + return fds; } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 0204f5ed61..51cb768d3d 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -39,23 +39,24 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { CHECK(OK == p->write(data.data(), data.size())); } -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, - const RandomParcelOptions& options) { +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { + CHECK_NE(options, nullptr); + if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } fillRandomParcelData(p, std::move(provider)); return; } - if (options.writeHeader) { - options.writeHeader(p, provider); + if (options->writeHeader) { + options->writeHeader(p, provider); } while (provider.remaining_bytes() > 0) { @@ -69,15 +70,21 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, }, // write FD [&]() { - if (options.extraFds.size() > 0 && provider.ConsumeBool()) { - const base::unique_fd& fd = options.extraFds.at( + if (options->extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange<size_t>(0, - options.extraFds.size() - + options->extraFds.size() - 1)); CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/)); } else { - base::unique_fd fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/)); + std::vector<base::unique_fd> fds = getRandomFds(&provider); + CHECK(OK == + p->writeFileDescriptor(fds.begin()->release(), + true /*takeOwnership*/)); + + options->extraFds.insert(options->extraFds.end(), + std::make_move_iterator(fds.begin() + 1), + std::make_move_iterator(fds.end())); } }, // write binder @@ -98,10 +105,10 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, return IInterface::asBinder(defaultServiceManager()); }, [&]() -> sp<IBinder> { - if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { - return options.extraBinders.at( + if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { + return options->extraBinders.at( provider.ConsumeIntegralInRange< - size_t>(0, options.extraBinders.size() - 1)); + size_t>(0, options->extraBinders.size() - 1)); } else { return nullptr; } diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp index a8713a24d6..f68a56144d 100644 --- a/libs/binder/tests/rpc_fuzzer/main.cpp +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -157,8 +157,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } - usleep(10000); - if (hangupBeforeShutdown) { connections.clear(); while (!server->listSessions().empty() || server->numUninitializedSessions()) { @@ -167,6 +165,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } } + while (server->hasActiveRequests()) { + usleep(10); + } + while (!server->shutdown()) usleep(1); serverThread.join(); diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index 56d958c6be..0035e4ee5a 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -398,14 +398,13 @@ Pipe make_process(int num, int iterations, int no_process, int payload_size) { pid_t pid = fork(); if (pid) { // parent - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } else { // child thread_dump(is_client(num) ? "client" : "server"); - worker_fx(num, no_process, iterations, payload_size, - move(get<1>(pipe_pair))); + worker_fx(num, no_process, iterations, payload_size, std::move(get<1>(pipe_pair))); // never get here - return move(get<0>(pipe_pair)); + return std::move(get<0>(pipe_pair)); } } diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index e77c55c669..910c9dc25c 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp @@ -49,6 +49,7 @@ 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); diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp new file mode 100644 index 0000000000..46346bb3e9 --- /dev/null +++ b/libs/binder/trusty/OS.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(TRUSTY_USERSPACE) +#include <openssl/rand.h> +#else +#include <lib/rand/rand.h> +#endif + +#include <binder/RpcTransportTipcTrusty.h> + +#include "../OS.h" + +using android::base::Result; + +namespace android { + +Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) { + // Trusty IPC syscalls are all non-blocking by default. + return {}; +} + +status_t getRandomBytes(uint8_t* data, size_t size) { +#if defined(TRUSTY_USERSPACE) + int res = RAND_bytes(data, size); + return res == 1 ? OK : UNKNOWN_ERROR; +#else + int res = rand_get_bytes(data, size); + return res == 0 ? OK : UNKNOWN_ERROR; +#endif // TRUSTY_USERSPACE +} + +status_t dupFileDescriptor(int /*oldFd*/, int* /*newFd*/) { + // TODO: implement separately + return INVALID_OPERATION; +} + +std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() { + return RpcTransportCtxFactoryTipcTrusty::make(); +} + +} // namespace android diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md new file mode 100644 index 0000000000..8a60af8b23 --- /dev/null +++ b/libs/binder/trusty/README.md @@ -0,0 +1,45 @@ +# Binder for Trusty + +This is the Trusty port of the libbinder library. +To build it, first you will need a checkout of the Trusty tree: +```shell +$ mkdir /path/to/trusty +$ cd /path/to/trusty +$ repo init -u https://android.googlesource.com/trusty/manifest -b master +$ repo sync -j$(nproc) -c --no-tags +``` + +After the checkout is complete, you can use the `build.py` script for both +building and testing Trusty. For a quick build without any tests, run: +```shell +$ ./trusty/vendor/google/aosp/scripts/build.py generic-arm64-test-debug +``` +This will build the smaller `generic-arm64-test-debug` project which +does not run any tests. + +The qemu-generic-arm64-test-debug` project includes the QEMU emulator and +a full Trusty test suite, including a set of libbinder tests. +To run the latter, use the command: +```shell +$ ./trusty/vendor/google/aosp/scripts/build.py \ + --test "boot-test:com.android.trusty.binder.test" \ + qemu-generic-arm64-test-debug +``` + +## Building AIDL files on Trusty +To compile AIDL interfaces into Trusty libraries, include the `make/aidl.mk` +in your `rules.mk` file, e.g.: +``` +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_AIDLS := \ + $(LOCAL_DIR)/IFoo.aidl \ + +include make/aidl.mk +``` + +## Examples +The Trusty tree contains some sample test apps at +`trusty/user/app/sample/binder-test`. diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp new file mode 100644 index 0000000000..18ce316d67 --- /dev/null +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcServerTrusty" + +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcServerTrusty.h> +#include <binder/RpcThreads.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +using android::base::unexpected; + +namespace android { + +android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) { + // Default is without TLS. + if (rpcTransportCtxFactory == nullptr) + rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make(); + auto ctx = rpcTransportCtxFactory->newServerCtx(); + if (ctx == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl), + msgMaxSize); + if (srv == nullptr) { + return unexpected(ERR_NO_MEMORY); + } + + int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps); + if (rc != NO_ERROR) { + return unexpected(rc); + } + return srv; +} + +RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize) + : mRpcServer(sp<RpcServer>::make(std::move(ctx))), + mPortName(std::move(portName)), + mPortAcl(std::move(portAcl)) { + mTipcPort.name = mPortName.c_str(); + mTipcPort.msg_max_size = msgMaxSize; + mTipcPort.msg_queue_len = 6; // Three each way + mTipcPort.priv = this; + + if (mPortAcl) { + // Initialize the array of pointers to uuids. + // The pointers in mUuidPtrs should stay valid across moves of + // RpcServerTrusty (the addresses of a std::vector's elements + // shouldn't change when the vector is moved), but would be invalidated + // by a copy which is why we disable the copy constructor and assignment + // operator for RpcServerTrusty. + auto numUuids = mPortAcl->uuids.size(); + mUuidPtrs.resize(numUuids); + for (size_t i = 0; i < numUuids; i++) { + mUuidPtrs[i] = &mPortAcl->uuids[i]; + } + + // Copy the contents of portAcl into the tipc_port_acl structure that we + // pass to tipc_add_service + mTipcPortAcl.flags = mPortAcl->flags; + mTipcPortAcl.uuid_num = numUuids; + mTipcPortAcl.uuids = mUuidPtrs.data(); + mTipcPortAcl.extra_data = mPortAcl->extraData; + + mTipcPort.acl = &mTipcPortAcl; + } else { + mTipcPort.acl = nullptr; + } +} + +int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, + void** ctx_p) { + auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv)); + server->mRpcServer->mShutdownTrigger = FdTrigger::make(); + server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread(); + + int rc = NO_ERROR; + auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) { + if (result.status != OK) { + rc = statusToTrusty(result.status); + return; + } + + /* Save the session and connection for the other callbacks */ + auto* channelContext = new (std::nothrow) ChannelContext; + if (channelContext == nullptr) { + rc = ERR_NO_MEMORY; + return; + } + + channelContext->session = std::move(session); + channelContext->connection = std::move(result.connection); + + *ctx_p = channelContext; + }; + + base::unique_fd clientFd(chan); + android::RpcTransportFd transportFd(std::move(clientFd)); + + std::array<uint8_t, RpcServer::kRpcAddressSize> addr; + constexpr size_t addrLen = sizeof(*peer); + memcpy(addr.data(), peer, addrLen); + RpcServer::establishConnection(sp(server->mRpcServer), std::move(transportFd), addr, addrLen, + joinFn); + + return rc; +} + +int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) { + auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); + LOG_ALWAYS_FATAL_IF(channelContext == nullptr, + "bad state: message received on uninitialized channel"); + + auto& session = channelContext->session; + auto& connection = channelContext->connection; + status_t status = + session->state()->drainCommands(connection, session, RpcState::CommandType::ANY); + if (status != OK) { + LOG_RPC_DETAIL("Binder connection thread closing w/ status %s", + statusToString(status).c_str()); + } + + return NO_ERROR; +} + +void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/, + void* /*ctx*/) {} + +void RpcServerTrusty::handleChannelCleanup(void* ctx) { + auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); + if (channelContext == nullptr) { + return; + } + + auto& session = channelContext->session; + auto& connection = channelContext->connection; + LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection), + "bad state: connection object guaranteed to be in list"); + + delete channelContext; +} + +} // namespace android diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp new file mode 100644 index 0000000000..0b67b9fb12 --- /dev/null +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcTransportTipcTrusty" + +#include <trusty_ipc.h> + +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcTrusty.h> +#include <log/log.h> + +#include "../FdTrigger.h" +#include "../RpcState.h" +#include "TrustyStatus.h" + +namespace android { + +namespace { + +// RpcTransport for Trusty. +class RpcTransportTipcTrusty : public RpcTransport { +public: + explicit RpcTransportTipcTrusty(android::RpcTransportFd socket) : mSocket(std::move(socket)) {} + ~RpcTransportTipcTrusty() { releaseMessage(); } + + status_t pollRead() override { + auto status = ensureMessage(false); + if (status != OK) { + return status; + } + return mHaveMessage ? OK : WOULD_BLOCK; + } + + status_t interruptableWriteFully( + FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) + override { + if (niovs < 0) { + return BAD_VALUE; + } + + size_t size = 0; + for (int i = 0; i < niovs; i++) { + size += iovs[i].iov_len; + } + + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = 0, // TODO: add ancillaryFds + .handles = nullptr, + }; + ssize_t rc = send_msg(mSocket.fd.get(), &msg); + if (rc == ERR_NOT_ENOUGH_BUFFER) { + // Peer is blocked, wait until it unblocks. + // TODO: when tipc supports a send-unblocked handler, + // save the message here in a queue and retry it asynchronously + // when the handler gets called by the library + uevent uevt; + do { + rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME); + if (rc < 0) { + return statusFromTrusty(rc); + } + if (uevt.event & IPC_HANDLE_POLL_HUP) { + return DEAD_OBJECT; + } + } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED)); + + // Retry the send, it should go through this time because + // sending is now unblocked + rc = send_msg(mSocket.fd.get(), &msg); + } + if (rc < 0) { + return statusFromTrusty(rc); + } + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size, + "Sent the wrong number of bytes %zd!=%zu", rc, size); + + return OK; + } + + status_t interruptableReadFully( + FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) + override { + if (niovs < 0) { + return BAD_VALUE; + } + + // If iovs has one or more empty vectors at the end and + // we somehow advance past all the preceding vectors and + // pass some or all of the empty ones to sendmsg/recvmsg, + // the call will return processSize == 0. In that case + // we should be returning OK but instead return DEAD_OBJECT. + // To avoid this problem, we make sure here that the last + // vector at iovs[niovs - 1] has a non-zero length. + while (niovs > 0 && iovs[niovs - 1].iov_len == 0) { + niovs--; + } + if (niovs == 0) { + // The vectors are all empty, so we have nothing to read. + return OK; + } + + while (true) { + auto status = ensureMessage(true); + if (status != OK) { + return status; + } + + ipc_msg_t msg{ + .num_iov = static_cast<uint32_t>(niovs), + .iov = iovs, + .num_handles = 0, // TODO: support ancillaryFds + .handles = nullptr, + }; + ssize_t rc = read_msg(mSocket.fd.get(), mMessageInfo.id, mMessageOffset, &msg); + if (rc < 0) { + return statusFromTrusty(rc); + } + + size_t processSize = static_cast<size_t>(rc); + mMessageOffset += processSize; + LOG_ALWAYS_FATAL_IF(mMessageOffset > mMessageInfo.len, + "Message offset exceeds length %zu/%zu", mMessageOffset, + mMessageInfo.len); + + // Release the message if all of it has been read + if (mMessageOffset == mMessageInfo.len) { + releaseMessage(); + } + + while (processSize > 0 && niovs > 0) { + auto& iov = iovs[0]; + if (processSize < iov.iov_len) { + // Advance the base of the current iovec + iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize; + iov.iov_len -= processSize; + break; + } + + // The current iovec was fully written + processSize -= iov.iov_len; + iovs++; + niovs--; + } + if (niovs == 0) { + LOG_ALWAYS_FATAL_IF(processSize > 0, + "Reached the end of iovecs " + "with %zd bytes remaining", + processSize); + return OK; + } + } + } + + bool isWaiting() override { return mSocket.isInPollingState(); } + +private: + status_t ensureMessage(bool wait) { + int rc; + if (mHaveMessage) { + LOG_ALWAYS_FATAL_IF(mMessageOffset >= mMessageInfo.len, "No data left in message"); + return OK; + } + + /* TODO: interruptible wait, maybe with a timeout??? */ + uevent uevt; + rc = ::wait(mSocket.fd.get(), &uevt, wait ? INFINITE_TIME : 0); + if (rc < 0) { + if (rc == ERR_TIMED_OUT && !wait) { + // If we timed out with wait==false, then there's no message + return OK; + } + return statusFromTrusty(rc); + } + if (!(uevt.event & IPC_HANDLE_POLL_MSG)) { + /* No message, terminate here and leave mHaveMessage false */ + return OK; + } + + rc = get_msg(mSocket.fd.get(), &mMessageInfo); + if (rc < 0) { + return statusFromTrusty(rc); + } + + mHaveMessage = true; + mMessageOffset = 0; + return OK; + } + + void releaseMessage() { + if (mHaveMessage) { + put_msg(mSocket.fd.get(), mMessageInfo.id); + mHaveMessage = false; + } + } + + android::RpcTransportFd mSocket; + + bool mHaveMessage = false; + ipc_msg_info mMessageInfo; + size_t mMessageOffset; +}; + +// RpcTransportCtx for Trusty. +class RpcTransportCtxTipcTrusty : public RpcTransportCtx { +public: + std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, + FdTrigger*) const override { + return std::make_unique<RpcTransportTipcTrusty>(std::move(socket)); + } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } +}; + +} // namespace + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newClientCtx() const { + return std::make_unique<RpcTransportCtxTipcTrusty>(); +} + +const char* RpcTransportCtxFactoryTipcTrusty::toCString() const { + return "trusty"; +} + +std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcTrusty::make() { + return std::unique_ptr<RpcTransportCtxFactoryTipcTrusty>( + new RpcTransportCtxFactoryTipcTrusty()); +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.cpp b/libs/binder/trusty/TrustyStatus.cpp new file mode 100644 index 0000000000..b1caf612bc --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TrustyStatus.h" +#include "../RpcState.h" + +namespace android { + +status_t statusFromTrusty(int rc) { + LOG_RPC_DETAIL("Trusty error: %d", rc); + switch (rc) { + case NO_ERROR: + return OK; + case ERR_NOT_FOUND: + return NAME_NOT_FOUND; + case ERR_NOT_READY: + // We get this error if we try to perform an IPC operation when the + // channel is not ready + return INVALID_OPERATION; + case ERR_NO_MSG: + return WOULD_BLOCK; + case ERR_NO_MEMORY: + return NO_MEMORY; + case ERR_INVALID_ARGS: + return BAD_VALUE; + case ERR_NOT_ENOUGH_BUFFER: + return WOULD_BLOCK; + case ERR_TIMED_OUT: + return TIMED_OUT; + case ERR_ALREADY_EXISTS: + return ALREADY_EXISTS; + case ERR_CHANNEL_CLOSED: + return DEAD_OBJECT; + case ERR_NOT_ALLOWED: + return INVALID_OPERATION; + case ERR_NOT_SUPPORTED: + return INVALID_OPERATION; + case ERR_TOO_BIG: + return BAD_INDEX; + case ERR_CMD_UNKNOWN: + return UNKNOWN_TRANSACTION; + case ERR_BAD_STATE: + return INVALID_OPERATION; + case ERR_BAD_LEN: + return NOT_ENOUGH_DATA; + case ERR_BAD_HANDLE: + return BAD_VALUE; + case ERR_ACCESS_DENIED: + return PERMISSION_DENIED; + default: + return UNKNOWN_ERROR; + } +} + +int statusToTrusty(status_t status) { + switch (status) { + case OK: + return NO_ERROR; + case NO_MEMORY: + return ERR_NO_MEMORY; + case INVALID_OPERATION: + case BAD_VALUE: + case BAD_TYPE: + return ERR_NOT_VALID; + case NAME_NOT_FOUND: + return ERR_NOT_FOUND; + case PERMISSION_DENIED: + return ERR_ACCESS_DENIED; + case NO_INIT: + return ERR_NOT_CONFIGURED; + case ALREADY_EXISTS: + return ERR_ALREADY_EXISTS; + case DEAD_OBJECT: + return ERR_CHANNEL_CLOSED; + case BAD_INDEX: + return ERR_TOO_BIG; + case NOT_ENOUGH_DATA: + return ERR_BAD_LEN; + case WOULD_BLOCK: + return ERR_NO_MSG; + case TIMED_OUT: + return ERR_TIMED_OUT; + case UNKNOWN_TRANSACTION: + return ERR_CMD_UNKNOWN; + case FDS_NOT_ALLOWED: + return ERR_NOT_SUPPORTED; + case UNEXPECTED_NULL: + return ERR_NOT_VALID; + default: + return ERR_GENERIC; + } +} + +} // namespace android diff --git a/libs/binder/trusty/TrustyStatus.h b/libs/binder/trusty/TrustyStatus.h new file mode 100644 index 0000000000..fcb43f8451 --- /dev/null +++ b/libs/binder/trusty/TrustyStatus.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <uapi/err.h> +#include <utils/Errors.h> + +namespace android { + +status_t statusFromTrusty(int rc); +int statusToTrusty(status_t status); + +} // namespace android diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h new file mode 100644 index 0000000000..cc31c95da1 --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <android-base/macros.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransport.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <map> +#include <vector> + +#include <lib/tipc/tipc_srv.h> + +namespace android { + +/** + * This is the Trusty-specific RPC server code. + */ +class RpcServerTrusty final : public virtual RefBase { +public: + // C++ equivalent to tipc_port_acl that uses safe data structures instead of + // raw pointers, except for |extraData| which doesn't have a good + // equivalent. + struct PortAcl { + uint32_t flags; + std::vector<const uuid> uuids; + const void* extraData; + }; + + /** + * Creates an RPC server listening on the given port and adds it to the + * Trusty handle set at |handleSet|. + * + * The caller is responsible for calling tipc_run_event_loop() to start + * the TIPC event loop after creating one or more services here. + */ + static android::base::expected<sp<RpcServerTrusty>, int> make( + tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, + size_t msgMaxSize, + std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr); + + void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); } + void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); } + void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); } + void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) { + mRpcServer->setPerSessionRootObject(std::move(object)); + } + sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); } + +private: + // Both this class and RpcServer have multiple non-copyable fields, + // including mPortAcl below which can't be copied because mUuidPtrs + // holds pointers into it + DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty); + + friend sp<RpcServerTrusty>; + explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, + std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize); + + // The Rpc-specific context maintained for every open TIPC channel. + struct ChannelContext { + sp<RpcSession> session; + sp<RpcSession::RpcConnection> connection; + }; + + static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p); + static int handleMessage(const tipc_port* port, handle_t chan, void* ctx); + static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx); + static void handleChannelCleanup(void* ctx); + + static constexpr tipc_srv_ops kTipcOps = { + .on_connect = &handleConnect, + .on_message = &handleMessage, + .on_disconnect = &handleDisconnect, + .on_channel_cleanup = &handleChannelCleanup, + }; + + sp<RpcServer> mRpcServer; + std::string mPortName; + std::shared_ptr<const PortAcl> mPortAcl; + std::vector<const uuid*> mUuidPtrs; + tipc_port_acl mTipcPortAcl; + tipc_port mTipcPort; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h new file mode 100644 index 0000000000..8eae8c2ec1 --- /dev/null +++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Wraps the transport layer of RPC. Implementation uses plain sockets. +// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx. + +#pragma once + +#include <memory> + +#include <binder/RpcTransport.h> + +namespace android { + +// RpcTransportCtxFactory with TLS disabled. +class RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory { +public: + static std::unique_ptr<RpcTransportCtxFactory> make(); + + std::unique_ptr<RpcTransportCtx> newServerCtx() const override; + std::unique_ptr<RpcTransportCtx> newClientCtx() const override; + const char* toCString() const override; + +private: + RpcTransportCtxFactoryTipcTrusty() = default; +}; + +} // namespace android diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h new file mode 100644 index 0000000000..bf877a3179 --- /dev/null +++ b/libs/binder/trusty/include/log/log.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define BINDER_LOG_LEVEL_NONE 0 +#define BINDER_LOG_LEVEL_NORMAL 1 +#define BINDER_LOG_LEVEL_VERBOSE 2 + +#ifndef BINDER_LOG_LEVEL +#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL +#endif // BINDER_LOG_LEVEL + +#ifndef TLOG_TAG +#ifdef LOG_TAG +#define TLOG_TAG "libbinder-" LOG_TAG +#else // LOG_TAG +#define TLOG_TAG "libbinder" +#endif // LOG_TAG +#endif // TLOG_TAG + +#include <stdlib.h> +#include <trusty_log.h> + +static inline void __ignore_va_args__(...) {} + +#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL +#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__) +#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) +#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__) +#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__) +#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL +#define ALOGD(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGI(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGW(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#define ALOGE(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL + +#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE +#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO) +#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__) +#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE +#define IF_ALOGV() if (false) +#define ALOGV(fmt, ...) \ + while (0) { \ + __ignore_va_args__(__VA_ARGS__); \ + } +#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE + +#define ALOGI_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGI(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define ALOGE_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGE(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define ALOGW_IF(cond, ...) \ + do { \ + if (cond) { \ + ALOGW(#cond ": " __VA_ARGS__); \ + } \ + } while (0) + +#define LOG_ALWAYS_FATAL(fmt, ...) \ + do { \ + TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ + } while (0) +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + do { \ + if (cond) { \ + LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \ + } \ + } while (0) +#define LOG_FATAL(fmt, ...) \ + do { \ + TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \ + abort(); \ + } while (0) +#define LOG_FATAL_IF(cond, ...) \ + do { \ + if (cond) { \ + LOG_FATAL(#cond ": " __VA_ARGS__); \ + } \ + } while (0) + +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) + +#define android_errorWriteLog(tag, subTag) \ + do { \ + TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \ + } while (0) diff --git a/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h new file mode 100644 index 0000000000..2747314508 --- /dev/null +++ b/libs/binder/trusty/include_mock/lib/tipc/tipc_srv.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stddef.h> +#include <trusty_ipc.h> +#include <uapi/trusty_uuid.h> + +struct tipc_port_acl { + uint32_t flags; + uint32_t uuid_num; + const struct uuid** uuids; + const void* extra_data; +}; + +struct tipc_port { + const char* name; + uint32_t msg_max_size; + uint32_t msg_queue_len; + const struct tipc_port_acl* acl; + const void* priv; +}; + +struct tipc_srv_ops { + int (*on_connect)(const struct tipc_port* port, handle_t chan, const struct uuid* peer, + void** ctx_p); + + int (*on_message)(const struct tipc_port* port, handle_t chan, void* ctx); + + void (*on_disconnect)(const struct tipc_port* port, handle_t chan, void* ctx); + + void (*on_channel_cleanup)(void* ctx); +}; + +static inline int tipc_add_service(struct tipc_hset*, const struct tipc_port*, uint32_t, uint32_t, + const struct tipc_srv_ops*) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/openssl/rand.h b/libs/binder/trusty/include_mock/openssl/rand.h new file mode 100644 index 0000000000..07dcc1cb6d --- /dev/null +++ b/libs/binder/trusty/include_mock/openssl/rand.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +static inline int RAND_bytes(unsigned char*, int) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h new file mode 100644 index 0000000000..a2170cea64 --- /dev/null +++ b/libs/binder/trusty/include_mock/trusty_ipc.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <uapi/trusty_uuid.h> + +#define INFINITE_TIME 1 +#define IPC_MAX_MSG_HANDLES 8 + +#define IPC_HANDLE_POLL_HUP 0x1 +#define IPC_HANDLE_POLL_MSG 0x2 +#define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4 + +typedef int handle_t; + +typedef struct ipc_msg { + uint32_t num_iov; + iovec* iov; + uint32_t num_handles; + handle_t* handles; +} ipc_msg_t; + +typedef struct ipc_msg_info { + size_t len; + uint32_t id; + uint32_t num_handles; +} ipc_msg_info_t; + +typedef struct uevent { + uint32_t event; +} uevent_t; + +static inline handle_t port_create(const char*, uint32_t, uint32_t, uint32_t) { + return 0; +} +static inline handle_t connect(const char*, uint32_t) { + return 0; +} +static inline handle_t accept(handle_t, uuid_t*) { + return 0; +} +static inline int set_cookie(handle_t, void*) { + return 0; +} +static inline handle_t handle_set_create(void) { + return 0; +} +static inline int handle_set_ctrl(handle_t, uint32_t, struct uevent*) { + return 0; +} +static inline int wait(handle_t, uevent_t*, uint32_t) { + return 0; +} +static inline int wait_any(uevent_t*, uint32_t) { + return 0; +} +static inline int get_msg(handle_t, ipc_msg_info_t*) { + return 0; +} +static inline ssize_t read_msg(handle_t, uint32_t, uint32_t, ipc_msg_t*) { + return 0; +} +static inline int put_msg(handle_t, uint32_t) { + return 0; +} +static inline ssize_t send_msg(handle_t, ipc_msg_t*) { + return 0; +} diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h new file mode 100644 index 0000000000..d51e75280c --- /dev/null +++ b/libs/binder/trusty/include_mock/trusty_log.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stdio.h> + +// Mock definitions for the Trusty logging macros. These are not +// meant to be run, just compiled successfully. +#define TLOGD(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGI(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__) diff --git a/libs/binder/trusty/include_mock/uapi/err.h b/libs/binder/trusty/include_mock/uapi/err.h new file mode 100644 index 0000000000..c7e117e419 --- /dev/null +++ b/libs/binder/trusty/include_mock/uapi/err.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +enum { + NO_ERROR, + ERR_ACCESS_DENIED, + ERR_ALREADY_EXISTS, + ERR_BAD_HANDLE, + ERR_BAD_LEN, + ERR_BAD_STATE, + ERR_CHANNEL_CLOSED, + ERR_CMD_UNKNOWN, + ERR_GENERIC, + ERR_INVALID_ARGS, + ERR_NO_MEMORY, + ERR_NO_MSG, + ERR_NOT_ALLOWED, + ERR_NOT_CONFIGURED, + ERR_NOT_ENOUGH_BUFFER, + ERR_NOT_FOUND, + ERR_NOT_READY, + ERR_NOT_SUPPORTED, + ERR_NOT_VALID, + ERR_TIMED_OUT, + ERR_TOO_BIG, +}; diff --git a/libs/binder/trusty/include_mock/uapi/trusty_uuid.h b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h new file mode 100644 index 0000000000..f636826bec --- /dev/null +++ b/libs/binder/trusty/include_mock/uapi/trusty_uuid.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +typedef struct uuid { + int placeholder; +} uuid_t; diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp new file mode 100644 index 0000000000..b4243af891 --- /dev/null +++ b/libs/binder/trusty/logging.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "libbinder" + +#include "android-base/logging.h" + +#include <trusty_log.h> +#include <iostream> +#include <string> + +#include <android-base/macros.h> +#include <android-base/strings.h> + +namespace android { +namespace base { + +static const char* GetFileBasename(const char* file) { + const char* last_slash = strrchr(file, '/'); + if (last_slash != nullptr) { + return last_slash + 1; + } + return file; +} + +// This splits the message up line by line, by calling log_function with a pointer to the start of +// each line and the size up to the newline character. It sends size = -1 for the final line. +template <typename F, typename... Args> +static void SplitByLines(const char* msg, const F& log_function, Args&&... args) { + const char* newline; + while ((newline = strchr(msg, '\n')) != nullptr) { + log_function(msg, newline - msg, args...); + msg = newline + 1; + } + + log_function(msg, -1, args...); +} + +void DefaultAborter(const char* abort_message) { + TLOGC("aborting: %s\n", abort_message); + abort(); +} + +static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity, + const char* tag) { + switch (severity) { + case VERBOSE: + case DEBUG: + TLOGD("%s: %s\n", tag, msg); + break; + case INFO: + TLOGI("%s: %s\n", tag, msg); + break; + case WARNING: + TLOGW("%s: %s\n", tag, msg); + break; + case ERROR: + TLOGE("%s: %s\n", tag, msg); + break; + case FATAL_WITHOUT_ABORT: + case FATAL: + TLOGC("%s: %s\n", tag, msg); + break; + } +} + +void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, + const char*, unsigned int, const char* full_message) { + SplitByLines(full_message, TrustyLogLine, severity, tag); +} + +// This indirection greatly reduces the stack impact of having lots of +// checks/logging in a function. +class LogMessageData { +public: + LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag, + int error) + : file_(GetFileBasename(file)), + line_number_(line), + severity_(severity), + tag_(tag), + error_(error) {} + + const char* GetFile() const { return file_; } + + unsigned int GetLineNumber() const { return line_number_; } + + LogSeverity GetSeverity() const { return severity_; } + + const char* GetTag() const { return tag_; } + + int GetError() const { return error_; } + + std::ostream& GetBuffer() { return buffer_; } + + std::string ToString() const { return buffer_.str(); } + +private: + std::ostringstream buffer_; + const char* const file_; + const unsigned int line_number_; + const LogSeverity severity_; + const char* const tag_; + const int error_; + + DISALLOW_COPY_AND_ASSIGN(LogMessageData); +}; + +LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity, + const char* tag, int error) + : LogMessage(file, line, severity, tag, error) {} + +LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag, + int error) + : data_(new LogMessageData(file, line, severity, tag, error)) {} + +LogMessage::~LogMessage() { + // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM. + if (!WOULD_LOG(data_->GetSeverity())) { + return; + } + + // Finish constructing the message. + if (data_->GetError() != -1) { + data_->GetBuffer() << ": " << strerror(data_->GetError()); + } + std::string msg(data_->ToString()); + + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), + msg.c_str()); + + // Abort if necessary. + if (data_->GetSeverity() == FATAL) { + DefaultAborter(msg.c_str()); + } +} + +std::ostream& LogMessage::stream() { + return data_->GetBuffer(); +} + +void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag, + const char* message) { + TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message); +} + +bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) { + // This is controlled by Trusty's log level. + return true; +} + +} // namespace base +} // namespace android diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk new file mode 100644 index 0000000000..4e5cd18726 --- /dev/null +++ b/libs/binder/trusty/rules.mk @@ -0,0 +1,87 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +LIBBINDER_DIR := frameworks/native/libs/binder +LIBBASE_DIR := system/libbase +LIBCUTILS_DIR := system/core/libcutils +LIBUTILS_DIR := system/core/libutils +FMTLIB_DIR := external/fmtlib + +MODULE_SRCS := \ + $(LOCAL_DIR)/logging.cpp \ + $(LOCAL_DIR)/OS.cpp \ + $(LOCAL_DIR)/RpcServerTrusty.cpp \ + $(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \ + $(LOCAL_DIR)/TrustyStatus.cpp \ + $(LOCAL_DIR)/socket.cpp \ + $(LIBBINDER_DIR)/Binder.cpp \ + $(LIBBINDER_DIR)/BpBinder.cpp \ + $(LIBBINDER_DIR)/FdTrigger.cpp \ + $(LIBBINDER_DIR)/IInterface.cpp \ + $(LIBBINDER_DIR)/IResultReceiver.cpp \ + $(LIBBINDER_DIR)/Parcel.cpp \ + $(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \ + $(LIBBINDER_DIR)/RpcServer.cpp \ + $(LIBBINDER_DIR)/RpcSession.cpp \ + $(LIBBINDER_DIR)/RpcState.cpp \ + $(LIBBINDER_DIR)/Stability.cpp \ + $(LIBBINDER_DIR)/Status.cpp \ + $(LIBBINDER_DIR)/Utils.cpp \ + $(LIBBASE_DIR)/hex.cpp \ + $(LIBBASE_DIR)/stringprintf.cpp \ + $(LIBUTILS_DIR)/Errors.cpp \ + $(LIBUTILS_DIR)/misc.cpp \ + $(LIBUTILS_DIR)/RefBase.cpp \ + $(LIBUTILS_DIR)/StrongPointer.cpp \ + $(LIBUTILS_DIR)/Unicode.cpp \ + +# TODO: remove the following when libbinder supports std::string +# instead of String16 and String8 for Status and descriptors +MODULE_SRCS += \ + $(LIBUTILS_DIR)/SharedBuffer.cpp \ + $(LIBUTILS_DIR)/String16.cpp \ + $(LIBUTILS_DIR)/String8.cpp \ + +# TODO: disable dump() transactions to get rid of Vector +MODULE_SRCS += \ + $(LIBUTILS_DIR)/VectorImpl.cpp \ + +MODULE_EXPORT_INCLUDES += \ + $(LOCAL_DIR)/include \ + $(LIBBINDER_DIR)/include \ + $(LIBBASE_DIR)/include \ + $(LIBCUTILS_DIR)/include \ + $(LIBUTILS_DIR)/include \ + $(FMTLIB_DIR)/include \ + +# The android/binder_to_string.h header is shared between libbinder and +# libbinder_ndk and included by auto-generated AIDL C++ files +MODULE_EXPORT_INCLUDES += \ + $(LIBBINDER_DIR)/ndk/include_cpp \ + +MODULE_EXPORT_COMPILEFLAGS += \ + -DBINDER_RPC_SINGLE_THREADED \ + -D__ANDROID_VNDK__ \ + +MODULE_LIBRARY_DEPS += \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/tipc \ + external/boringssl \ + +include make/library.mk diff --git a/libs/binder/trusty/socket.cpp b/libs/binder/trusty/socket.cpp new file mode 100644 index 0000000000..02df8afb92 --- /dev/null +++ b/libs/binder/trusty/socket.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <log/log.h> + +// On some versions of clang, RpcServer.cpp refuses to link without accept4 +__attribute__((weak)) extern "C" int accept4(int, void*, void*, int) { + LOG_ALWAYS_FATAL("accept4 called on Trusty"); + return 0; +} + +// Placeholder for poll used by FdTrigger +__attribute__((weak)) extern "C" int poll(void*, long, int) { + LOG_ALWAYS_FATAL("poll called on Trusty"); + return 0; +} diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp index 44e2fd19b1..2f731377f0 100644 --- a/libs/binderthreadstate/test.cpp +++ b/libs/binderthreadstate/test.cpp @@ -68,8 +68,13 @@ static void callHidl(size_t id, int32_t idx) { static void callAidl(size_t id, int32_t idx) { sp<IAidlStuff> stuff; - CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); - CHECK(stuff->call(idx).isOk()); + CHECK_EQ(OK, android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); + auto ret = stuff->call(idx); + CHECK(ret.isOk()) << ret; +} + +static inline std::ostream& operator<<(std::ostream& o, const BinderCallType& s) { + return o << static_cast<std::underlying_type_t<BinderCallType>>(s); } class HidlServer : public IHidlStuff { @@ -79,13 +84,13 @@ public: size_t otherId; Return<void> callLocal() { - CHECK(BinderCallType::NONE == getCurrentServingCall()); + CHECK_EQ(BinderCallType::NONE, getCurrentServingCall()); return android::hardware::Status::ok(); } Return<void> call(int32_t idx) { LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx << " with tid: " << gettid(); - CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall()); if (idx > 0) { if (thisId == kP1Id && idx % 4 < 2) { callHidl(otherId, idx - 1); @@ -93,7 +98,7 @@ public: callAidl(otherId, idx - 1); } } - CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::HWBINDER, getCurrentServingCall()); return android::hardware::Status::ok(); } }; @@ -104,13 +109,13 @@ public: size_t otherId; Status callLocal() { - CHECK(BinderCallType::NONE == getCurrentServingCall()); + CHECK_EQ(BinderCallType::NONE, getCurrentServingCall()); return Status::ok(); } Status call(int32_t idx) { LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx << " with tid: " << gettid(); - CHECK(BinderCallType::BINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall()); if (idx > 0) { if (thisId == kP2Id && idx % 4 < 2) { callHidl(otherId, idx - 1); @@ -118,7 +123,7 @@ public: callAidl(otherId, idx - 1); } } - CHECK(BinderCallType::BINDER == getCurrentServingCall()); + CHECK_EQ(BinderCallType::BINDER, getCurrentServingCall()); return Status::ok(); } }; @@ -161,13 +166,14 @@ int server(size_t thisId, size_t otherId) { // AIDL android::ProcessState::self()->setThreadPoolMaxThreadCount(1); sp<AidlServer> aidlServer = new AidlServer(thisId, otherId); - CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); + CHECK_EQ(OK, + defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); android::ProcessState::self()->startThreadPool(); // HIDL android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); - CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str())); + CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str())); android::hardware::joinRpcThreadpool(); return EXIT_FAILURE; diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 7e9bb7d468..706704ad34 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -131,24 +131,24 @@ static bool initGlobals() { } gTisTotalMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_total_time_in_state_map")}; if (gTisTotalMapFd < 0) return false; - gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; gConcurrentMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; if (gConcurrentMapFd < 0) return false; gUidLastUpdateMapFd = - unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + unique_fd{bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_last_update_map")}; if (gUidLastUpdateMapFd < 0) return false; - gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_time_in_state_pid_time_in_state_map")}; + gPidTisMapFd = unique_fd{mapRetrieveRO(BPF_FS_PATH "map_timeInState_pid_time_in_state_map")}; if (gPidTisMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; gInitialized = true; @@ -156,7 +156,7 @@ static bool initGlobals() { } static int retrieveProgramFd(const std::string &eventType, const std::string &eventName) { - std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", + std::string path = StringPrintf(BPF_FS_PATH "prog_timeInState_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); return retrieveProgram(path.c_str()); } @@ -200,7 +200,7 @@ bool startTrackingUidTimes() { if (!initGlobals()) return false; if (gTracking) return true; - unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + unique_fd cpuPolicyFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_policy_map")); if (cpuPolicyFd < 0) return false; for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { @@ -209,7 +209,7 @@ bool startTrackingUidTimes() { } } - unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + unique_fd freqToIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_freq_to_idx_map")); if (freqToIdxFd < 0) return false; freq_idx_key_t key; for (uint32_t i = 0; i < gNPolicies; ++i) { @@ -224,23 +224,23 @@ bool startTrackingUidTimes() { } } - unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_cpu_last_update_map")); + unique_fd cpuLastUpdateFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_cpu_last_update_map")); if (cpuLastUpdateFd < 0) return false; std::vector<uint64_t> zeros(get_nprocs_conf(), 0); uint32_t zero = 0; if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false; - unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_nr_active_map")); + unique_fd nrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_nr_active_map")); if (nrActiveFd < 0) return false; if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false; - unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_nr_active_map")); + unique_fd policyNrActiveFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_nr_active_map")); if (policyNrActiveFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false; } - unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map")); + unique_fd policyFreqIdxFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_policy_freq_idx_map")); if (policyFreqIdxFd < 0) return false; for (uint32_t i = 0; i < gNPolicies; ++i) { auto freqIdx = getPolicyFreqIdx(i); @@ -300,11 +300,11 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui } std::vector<tis_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { - key.bucket = i; + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gTisMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gTisMapFd, &tmpKey)) return {}; continue; } @@ -412,10 +412,11 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); std::vector<concurrent_val_t> vals(gNCpus); - time_key_t key = {.uid = uid}; - for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + for (uint32_t i = 0; i <= (gNCpus - 1) / CPUS_PER_ENTRY; ++i) { + const time_key_t key = {.uid = uid, .bucket = i}; if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { - if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {}; + time_key_t tmpKey; + if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &tmpKey)) return {}; continue; } auto offset = key.bucket * CPUS_PER_ENTRY; @@ -559,10 +560,10 @@ bool startTrackingProcessCpuTimes(pid_t pid) { if (!gInitialized && !initGlobals()) return false; unique_fd trackedPidHashMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_hash_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_hash_map")); if (trackedPidHashMapFd < 0) return false; - unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_tracked_map")); + unique_fd trackedPidMapFd(mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_tracked_map")); if (trackedPidMapFd < 0) return false; for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) { @@ -589,7 +590,7 @@ bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { if (!gInitialized && !initGlobals()) return false; unique_fd taskAggregationMapFd( - mapRetrieveWO(BPF_FS_PATH "map_time_in_state_pid_task_aggregation_map")); + mapRetrieveWO(BPF_FS_PATH "map_timeInState_pid_task_aggregation_map")); if (taskAggregationMapFd < 0) return false; return writeToMapEntry(taskAggregationMapFd, &pid, &aggregationKey, BPF_ANY) == 0; diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp new file mode 100644 index 0000000000..2399acd156 --- /dev/null +++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp @@ -0,0 +1,41 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "cputimeinstate_fuzzer", + srcs: [ + "cputimeinstate_fuzzer.cpp", + ], + static_libs: [ + "libtimeinstate", + ], + shared_libs: [ + "libbpf_bcc", + "libbase", + "libbpf_minimal", + ], +} diff --git a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp new file mode 100644 index 0000000000..f835997187 --- /dev/null +++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/cputimeinstate_fuzzer.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ + +#include <fuzzer/FuzzedDataProvider.h> +#include <android-base/unique_fd.h> +#include <cputimeinstate.h> + +using namespace android::bpf; + +static const uint16_t MAX_VEC_SIZE = 500; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + uint32_t uid = fdp.ConsumeIntegral<uint32_t>(); + uint64_t lastUpdate = fdp.ConsumeIntegral<uint64_t>(); + uint16_t aggregationKey = fdp.ConsumeIntegral<uint16_t>(); + pid_t pid = fdp.ConsumeIntegral<pid_t>(); + std::vector<uint16_t> aggregationKeys; + uint16_t aggregationKeysSize = fdp.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE); + for (uint16_t i = 0; i < aggregationKeysSize; i++) { + aggregationKeys.push_back(fdp.ConsumeIntegral<uint16_t>()); + } + + // To randomize the API calls + while (fdp.remaining_bytes() > 0) { + auto func = fdp.PickValueInArray<const std::function<void()>>({ + [&]() { getUidCpuFreqTimes(uid); }, + [&]() { getUidsUpdatedCpuFreqTimes(&lastUpdate); }, + [&]() { getUidConcurrentTimes(uid);}, + [&]() { getUidsUpdatedConcurrentTimes(&lastUpdate); }, + [&]() { startAggregatingTaskCpuTimes(pid, aggregationKey); }, + [&]() { getAggregatedTaskCpuFreqTimes(pid, aggregationKeys); }, + }); + + func(); + } + + return 0; +} diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 45a6d47bb4..6ccc6cafb6 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -462,7 +462,7 @@ TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { ++uid; } android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; ASSERT_GE(fd, 0); uint32_t nCpus = get_nprocs_conf(); uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY; @@ -504,7 +504,7 @@ TEST_F(TimeInStateTest, RemoveUid) { { // Add a map entry for our fake UID by copying a real map entry android::base::unique_fd fd{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_time_in_state_map")}; ASSERT_GE(fd, 0); time_key_t k; ASSERT_FALSE(getFirstMapKey(fd, &k)); @@ -515,7 +515,7 @@ TEST_F(TimeInStateTest, RemoveUid) { ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); android::base::unique_fd fd2{ - bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + bpf_obj_get(BPF_FS_PATH "map_timeInState_uid_concurrent_times_map")}; k.uid = copiedUid; k.bucket = 0; std::vector<concurrent_val_t> cvals(get_nprocs_conf()); diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 3551a8fd0a..a6585c5ad2 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -85,8 +85,20 @@ static const char* hidl_hal_interfaces_to_dump[] { /* list of hal interface to dump containing process during native dumps */ static const std::vector<std::string> aidl_interfaces_to_dump { + "android.hardware.automotive.audiocontrol.IAudioControl", + "android.hardware.automotive.evs.IEvsEnumerator", + "android.hardware.biometrics.face.IBiometricsFace", + "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", "android.hardware.camera.provider.ICameraProvider", + "android.hardware.drm.IDrmFactory", + "android.hardware.graphics.allocator.IAllocator", + "android.hardware.graphics.composer3.IComposer", + "android.hardware.health.IHealth", "android.hardware.input.processor.IInputProcessor", + "android.hardware.neuralnetworks.IDevice", + "android.hardware.power.IPower", + "android.hardware.power.stats.IPowerStats", + "android.hardware.sensors.ISensors", }; /* list of extra hal interfaces to dump containing process during native dumps */ diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 47c0657bbd..29924ff500 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -28,6 +28,7 @@ cc_defaults { cc_library { name: "libfakeservicemanager", defaults: ["fakeservicemanager_defaults"], + export_include_dirs: ["include/fakeservicemanager"], } cc_test_host { @@ -37,4 +38,5 @@ cc_test_host { "test_sm.cpp", ], static_libs: ["libgmock"], + local_include_dirs: ["include/fakeservicemanager"], } diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h index e0af5d4ba8..e0af5d4ba8 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS index 8c284647f2..347c4e0db1 100644 --- a/libs/graphicsenv/OWNERS +++ b/libs/graphicsenv/OWNERS @@ -1,4 +1,10 @@ +abdolrashidi@google.com +cclao@google.com chrisforbes@google.com cnorthrop@google.com +ianelliott@google.com +lfy@google.com lpy@google.com -timvp@google.com +romanl@google.com +vantablack@google.com +yuxinhu@google.com diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index d634c58c53..56b17aecb2 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -170,7 +170,8 @@ cc_library_shared { ], srcs: [ - ":framework_native_aidl", + ":framework_native_aidl_binder", + ":framework_native_aidl_gui", ":inputconstants_aidl", ":libgui_bufferqueue_sources", @@ -316,7 +317,6 @@ filegroup { cc_defaults { name: "libgui_bufferqueue-defaults", - clang: true, cflags: [ "-Wall", "-Werror", @@ -337,7 +337,7 @@ cc_defaults { }, whole_static_libs: [ - "LibGuiProperties", + "libLibGuiProperties", ], shared_libs: [ diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 45c958ebe4..05b55337ab 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -2,9 +2,9 @@ adyabr@google.com alecmouri@google.com chaviw@google.com chrisforbes@google.com -jessehall@google.com +jreck@google.com lpy@google.com -mathias@google.com +pdwilliams@google.com racarr@google.com vishnun@google.com diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 54b6d6a549..6b544b2b96 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1943,6 +1943,7 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { mReqHeight = 0; mReqUsage = 0; mCrop.clear(); + mDataSpace = Dataspace::UNKNOWN; mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mTransform = 0; mStickyTransform = 0; diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index e58543a245..fc68ad27c5 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -15,7 +15,6 @@ cc_test { name: "libgui_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -75,7 +74,6 @@ cc_test { name: "libgui_multilib_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -102,7 +100,6 @@ cc_test { name: "SurfaceParcelable_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -131,7 +128,6 @@ cc_test { cc_test { name: "SamplingDemo", - clang: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 1335e4dfd0..b2fec7917b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -58,8 +58,6 @@ cc_library { "VirtualKeyMap.cpp", ], - clang: true, - header_libs: ["jni_headers"], export_header_lib_headers: ["jni_headers"], diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 155cb040fb..2b7483d27d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -949,6 +949,8 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", actionButton=" << std::to_string(event.getActionButton()); } const size_t pointerCount = event.getPointerCount(); + LOG_ALWAYS_FATAL_IF(pointerCount > MAX_POINTERS, "Too many pointers : pointerCount = %zu", + pointerCount); for (size_t i = 0; i < pointerCount; i++) { out << ", id[" << i << "]=" << event.getPointerId(i); float x = event.getX(i); diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 170e748ca6..d6b4579a94 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -25,8 +25,10 @@ #include <utils/Errors.h> #include <utils/Timers.h> #include <utils/Tokenizer.h> +#if defined(__ANDROID__) #include <vintf/RuntimeInfo.h> #include <vintf/VintfObject.h> +#endif #include <cstdlib> #include <string_view> @@ -79,6 +81,7 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_ sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()}; bool kernelConfigsArePresent(const std::set<std::string>& configs) { +#if defined(__ANDROID__) std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo = android::vintf::VintfObject::GetInstance()->getRuntimeInfo( vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); @@ -99,6 +102,10 @@ bool kernelConfigsArePresent(const std::set<std::string>& configs) { } } return true; +#else + (void)configs; // Suppress 'unused variable' warning + return true; +#endif } } // namespace @@ -199,78 +206,68 @@ base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor( const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); + auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + return &it->second; } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); + auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + return &it->second; } } return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey( - int32_t keyCode, std::vector<int32_t>* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; i<N; i++) { - if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->push_back(mKeysByScanCode.keyAt(i)); +std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { + std::vector<int32_t> scanCodes; + for (const auto& [scanCode, key] : mKeysByScanCode) { + if (keyCode == key.keyCode) { + scanCodes.push_back(scanCode); } } - return NO_ERROR; + return scanCodes; } -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { +std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const { + auto it = mAxes.find(scanCode); + if (it == mAxes.end()) { ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); - return NAME_NOT_FOUND; + return std::nullopt; } - *outAxisInfo = mAxes.valueAt(index); - + const AxisInfo& axisInfo = it->second; ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " "splitValue=%d, flatOverride=%d.", - scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); - - return NO_ERROR; + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + return axisInfo; } -status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { - const size_t N = mLedsByScanCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { - *outScanCode = mLedsByScanCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, - *outScanCode); - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const { + for (const auto& [scanCode, led] : mLedsByScanCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode); + return scanCode; } } - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } -status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { - const size_t N = mLedsByUsageCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { - *outUsageCode = mLedsByUsageCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode); - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const { + for (const auto& [usageCode, led] : mLedsByUsageCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode); + return usageCode; } } ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); - - return NAME_NOT_FOUND; + return std::nullopt; } - // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : @@ -345,8 +342,9 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Key>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -387,7 +385,7 @@ status_t KeyLayoutMap::Parser::parseKey() { Key key; key.keyCode = keyCode; key.flags = flags; - map.add(code, key); + map.insert({code, key}); return NO_ERROR; } @@ -400,7 +398,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { scanCodeToken.string()); return BAD_VALUE; } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; @@ -486,8 +484,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { "splitValue=%d, flatOverride=%d.", scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, axisInfo.flatOverride); - - mMap->mAxes.add(scanCode, axisInfo); + mMap->mAxes.insert({scanCode, axisInfo}); return NO_ERROR; } @@ -507,8 +504,9 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } - KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Led>& map = + mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -528,7 +526,7 @@ status_t KeyLayoutMap::Parser::parseLed() { Led led; led.ledCode = ledCode; - map.add(code, led); + map.insert({code, led}); return NO_ERROR; } diff --git a/libs/math/OWNERS b/libs/math/OWNERS index 72d33bc09e..82ae422893 100644 --- a/libs/math/OWNERS +++ b/libs/math/OWNERS @@ -1,3 +1,5 @@ mathias@google.com randolphs@google.com romainguy@google.com +sumir@google.com +jreck@google.com diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index ed728dcb45..4659b96b11 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -33,7 +33,7 @@ license { cc_library_headers { name: "libnativedisplay_headers", - export_include_dirs: ["include",], + export_include_dirs: ["include"], } cc_library_shared { @@ -43,8 +43,6 @@ cc_library_shared { "include-private", ], - clang: true, - cflags: [ "-Wall", "-Werror", diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index d30efa1851..dd07319791 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -77,8 +77,6 @@ cc_library { "include-private", ], - clang: true, - cflags: [ "-Wall", "-Werror", diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 988132cd1c..da42a96df7 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,10 +2,10 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_createFromHandle; # llndk # apex + AHardwareBuffer_createFromHandle; # llndk # systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 - AHardwareBuffer_getNativeHandle; # llndk # apex + AHardwareBuffer_getNativeHandle; # llndk # systemapi AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -23,18 +23,18 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; - ANativeWindow_getLastDequeueDuration; # apex # introduced=30 - ANativeWindow_getLastDequeueStartTime; # apex # introduced=30 - ANativeWindow_getLastQueueDuration; # apex # introduced=30 + ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 + ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30 + ANativeWindow_getLastQueueDuration; # systemapi # introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # llndk ANativeWindow_queryf; # llndk ANativeWindow_queueBuffer; # llndk - ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30 - ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30 - ANativeWindow_setPerformInterceptor; # apex # introduced=30 - ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30 + ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setPerformInterceptor; # systemapi # introduced=30 + ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30 ANativeWindow_release; ANativeWindow_setAutoPrerotation; # llndk ANativeWindow_setAutoRefresh; # llndk @@ -45,7 +45,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setDequeueTimeout; # apex # introduced=30 + ANativeWindow_setDequeueTimeout; # systemapi # introduced=30 ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index cb92df388b..f6f57dde7d 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -111,7 +111,6 @@ cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], double_loadable: true, - clang: true, cflags: [ "-fvisibility=hidden", "-Werror=format", diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index edd453a936..2b93c6e85e 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -24,7 +24,6 @@ package { cc_library_shared { name: "libsensor", - clang: true, cflags: [ "-Wall", "-Werror", @@ -53,5 +52,9 @@ cc_library_shared { export_include_dirs: ["include"], - export_shared_lib_headers: ["libbinder", "libpermission", "libhardware"], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], } diff --git a/libs/sensor/fuzz/bittube_fuzzer/Android.bp b/libs/sensor/fuzz/bittube_fuzzer/Android.bp new file mode 100644 index 0000000000..7af22cc7c1 --- /dev/null +++ b/libs/sensor/fuzz/bittube_fuzzer/Android.bp @@ -0,0 +1,51 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "bittube_fuzzer", + srcs: [ + "bittube_fuzzer.cpp", + ], + static_libs: [ + ], + shared_libs: [ + "libsensor", + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + "libpermission", + ], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], + header_libs: [ + ], +} diff --git a/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp new file mode 100644 index 0000000000..6f10a67ebd --- /dev/null +++ b/libs/sensor/fuzz/bittube_fuzzer/bittube_fuzzer.cpp @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +#include <fuzzer/FuzzedDataProvider.h> + +#include <sensor/BitTube.h> +#include <binder/Parcel.h> +using namespace android; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + BitTube bittube(size); + Parcel parcel[5]; + bittube.writeToParcel(parcel); + sp<BitTube> tube(new BitTube(size)); + bittube.sendObjects<uint8_t>(tube, data, size); + uint8_t recvData[size]; + for (int i = 0; i < size; i++) recvData[i] = fdp.ConsumeIntegral<uint8_t>(); + bittube.recvObjects<uint8_t>(tube, recvData, size); + + return 0; +} diff --git a/libs/sensor/fuzz/sensor_fuzzer/Android.bp b/libs/sensor/fuzz/sensor_fuzzer/Android.bp new file mode 100644 index 0000000000..685c54953c --- /dev/null +++ b/libs/sensor/fuzz/sensor_fuzzer/Android.bp @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "sensor_fuzzer", + srcs: [ + "sensor_fuzzer.cpp", + ], + shared_libs: [ + "libsensor", + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + "libpermission", + ], + export_shared_lib_headers: [ + "libbinder", + "libpermission", + "libhardware", + ], +} diff --git a/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp new file mode 100644 index 0000000000..0e110b7e6f --- /dev/null +++ b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************** + */ +#include <fuzzer/FuzzedDataProvider.h> + +#include <sensor/Sensor.h> +using namespace android; + +const int MAX_STR_LEN = 32; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + struct sensor_t sensor_type; + std::string name = fdp.ConsumeBytesAsString(MAX_STR_LEN); + sensor_type.name = name.c_str(); + std::string vendor = fdp.ConsumeBytesAsString(MAX_STR_LEN); + sensor_type.vendor = vendor.c_str(); + sensor_type.stringType = ""; + sensor_type.requiredPermission = ""; + sensor_type.version = fdp.ConsumeIntegral<int>(); + sensor_type.handle = fdp.ConsumeIntegral<int>(); + sensor_type.type = fdp.ConsumeIntegral<int>(); + sensor_type.maxRange = fdp.ConsumeFloatingPoint<float>(); + sensor_type.resolution = fdp.ConsumeFloatingPoint<float>(); + sensor_type.power = fdp.ConsumeFloatingPoint<float>(); + sensor_type.minDelay = fdp.ConsumeIntegral<int32_t>(); + sensor_type.fifoReservedEventCount = fdp.ConsumeIntegral<uint32_t>(); + sensor_type.fifoMaxEventCount = fdp.ConsumeIntegral<uint32_t>(); + int halVersion = fdp.ConsumeIntegral<int>(); + Sensor sensor1(&sensor_type, halVersion); + uint8_t buffer[size]; + for (int i = 0; i < size; i++) buffer[i] = data[i]; + sensor1.flatten(buffer, size); + std::vector<uint8_t> buffer1(sensor1.getFlattenedSize()); + auto ab = sensor1.unflatten(buffer1.data(), buffer1.size()); + return 0; +} + diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 8fdb003a5d..ac4be44a4a 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -24,9 +24,10 @@ package { cc_test { name: "libsensor_test", - clang: true, - - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], srcs: [ "Sensor_test.cpp", diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d138495036..0f771a9070 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -36,7 +36,6 @@ cc_library_headers { cc_defaults { name: "libui-defaults", - clang: true, cflags: [ "-Wall", "-Werror", @@ -111,7 +110,6 @@ cc_library_shared { }, double_loadable: true, - clang: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 3732fee7f2..429760ffe0 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -465,7 +465,7 @@ status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& if (flattenWordCount == 13) { usage = (uint64_t(buf[12]) << 32) | uint32_t(buf[6]); } else { - usage = uint64_t(usage_deprecated); + usage = uint64_t(ANDROID_NATIVE_UNSIGNED_CAST(usage_deprecated)); } native_handle* h = native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts)); diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index c28c303c0c..5d6070cf95 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -25,7 +25,7 @@ package { cc_defaults { name: "libui_tools_default", - clang_cflags: [ + cflags: [ "-Wall", "-Wextra", "-Werror", diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h index c6eb3d102b..760dbce149 100644 --- a/libs/vibrator/include/vibrator/ExternalVibration.h +++ b/libs/vibrator/include/vibrator/ExternalVibration.h @@ -33,7 +33,6 @@ public : ExternalVibration(int32_t uid, std::string pkg, const audio_attributes_t& attrs, sp<IExternalVibrationController> controller); virtual ~ExternalVibration() = default; - ExternalVibration(const ExternalVibration&) = default; bool operator==(const ExternalVibration&) const; diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index fa449aeb55..e72ca74ba7 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -26,7 +26,6 @@ cc_library_static { cc_test { name: "broadcast_ring_tests", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index c1f6da3b10..c95603bcf7 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -16,7 +16,6 @@ cc_library_headers { cc_library_static { name: "libpdx", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -42,7 +41,6 @@ cc_library_static { cc_test { name: "pdx_tests", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -72,7 +70,6 @@ cc_test { // Code analysis target. cc_test { name: "pdx_encoder_performance_test", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp index cc32b1822b..ac831ceda0 100644 --- a/libs/vr/libpdx/fuzz/Android.bp +++ b/libs/vr/libpdx/fuzz/Android.bp @@ -9,7 +9,6 @@ package { cc_fuzz { name: "libpdx_service_dispatcher_fuzzer", - clang: true, srcs: [ "service_dispatcher_fuzzer.cpp", ], @@ -24,13 +23,12 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } cc_fuzz { name: "libpdx_message_fuzzer", - clang: true, srcs: [ "message_fuzzer.cpp", ], @@ -45,13 +43,12 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } cc_fuzz { name: "libpdx_serialization_fuzzer", - clang: true, srcs: [ "serialization_fuzzer.cpp", ], @@ -66,6 +63,6 @@ cc_fuzz { shared_libs: [ "libutils", "liblog", - "libcutils" + "libcutils", ], } diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index 804685747e..a5758b589f 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -9,7 +9,6 @@ package { cc_defaults { name: "pdx_default_transport_compiler_defaults", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -26,7 +25,10 @@ cc_defaults { cc_defaults { name: "pdx_use_transport_servicefs", export_include_dirs: ["private/servicefs"], - whole_static_libs: ["libpdx_servicefs", "libservicefs"], + whole_static_libs: [ + "libpdx_servicefs", + "libservicefs", + ], } cc_defaults { diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp index 216ca9f236..7f88dafc81 100644 --- a/libs/vr/libpdx_uds/Android.bp +++ b/libs/vr/libpdx_uds/Android.bp @@ -9,7 +9,6 @@ package { cc_library_static { name: "libpdx_uds", - clang: true, cflags: [ "-Wall", "-Wextra", @@ -41,7 +40,6 @@ cc_library_static { cc_test { name: "libpdx_uds_tests", - clang: true, cflags: [ "-Wall", "-Wextra", diff --git a/opengl/OWNERS b/opengl/OWNERS index a47fb9a573..379f7638f0 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -6,7 +6,6 @@ ianelliott@google.com jessehall@google.com lfy@google.com lpy@google.com -timvp@google.com romanl@google.com vantablack@google.com yuxinhu@google.com diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index c9fce8ad52..c1e935aab0 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -108,7 +108,6 @@ cc_defaults { // In particular, DO NOT add libutils nor anything "above" libui "libgraphicsenv", "libnativewindow", - "libbacktrace", "libbase", ], } @@ -165,6 +164,7 @@ cc_library_shared { "libnativeloader_lazy", "libutils", "libSurfaceFlingerProp", + "libunwindstack", ], static_libs: [ "libEGL_getProcAddress", diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h index b7fdf97cbe..96437c37d9 100644 --- a/opengl/libs/EGL/CallStack.h +++ b/opengl/libs/EGL/CallStack.h @@ -16,8 +16,8 @@ #pragma once -#include <backtrace/Backtrace.h> #include <log/log.h> +#include <unwindstack/AndroidUnwinder.h> #include <memory> @@ -26,12 +26,15 @@ public: // Create a callstack with the current thread's stack trace. // Immediately dump it to logcat using the given logtag. static void log(const char* logtag) noexcept { - std::unique_ptr<Backtrace> backtrace( - Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); - if (backtrace->Unwind(2)) { - for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) { + unwindstack::AndroidLocalUnwinder unwinder; + unwindstack::AndroidUnwinderData data; + if (unwinder.Unwind(data)) { + for (size_t i = 2, c = data.frames.size(); i < c; i++) { + auto& frame = data.frames[i]; + // Trim the first two frames. + frame.num -= 2; __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s", - backtrace->FormatFrameData(i).c_str()); + unwinder.FormatFrame(frame).c_str()); } } } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index f4dbe499eb..7619a5071c 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1453,7 +1453,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri setError(EGL_BAD_SURFACE, EGL_FALSE); } int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (attribute == EGL_TIMESTAMPS_ANDROID) { @@ -1463,7 +1467,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri return EGL_TRUE; } int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (s->setSmpte2086Attribute(attribute, value)) { diff --git a/opengl/tests/gl2_cameraeye/AndroidManifest.xml b/opengl/tests/gl2_cameraeye/AndroidManifest.xml index c53f7be0b0..a4674e129d 100644 --- a/opengl/tests/gl2_cameraeye/AndroidManifest.xml +++ b/opengl/tests/gl2_cameraeye/AndroidManifest.xml @@ -26,7 +26,7 @@ <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:glEsVersion="0x00020000" /> <application android:label="@string/gl2cameraeye_name"> - <activity android:name="GL2CameraEye"> + <activity android:name="GL2CameraEye" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> diff --git a/opengl/tests/gl2_java/AndroidManifest.xml b/opengl/tests/gl2_java/AndroidManifest.xml index 8bb6840a16..500adb5c14 100644 --- a/opengl/tests/gl2_java/AndroidManifest.xml +++ b/opengl/tests/gl2_java/AndroidManifest.xml @@ -22,7 +22,8 @@ <activity android:name="GL2JavaActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/gl2_jni/AndroidManifest.xml b/opengl/tests/gl2_jni/AndroidManifest.xml index 1827e5f377..b4ce99b102 100644 --- a/opengl/tests/gl2_jni/AndroidManifest.xml +++ b/opengl/tests/gl2_jni/AndroidManifest.xml @@ -21,7 +21,8 @@ <activity android:name="GL2JNIActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/gl_jni/AndroidManifest.xml b/opengl/tests/gl_jni/AndroidManifest.xml index 5d0ec966f4..bedab56659 100644 --- a/opengl/tests/gl_jni/AndroidManifest.xml +++ b/opengl/tests/gl_jni/AndroidManifest.xml @@ -24,7 +24,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml index 6c23d422f5..d766be9ed5 100644 --- a/opengl/tests/lighting1709/AndroidManifest.xml +++ b/opengl/tests/lighting1709/AndroidManifest.xml @@ -2,7 +2,7 @@ package="com.android.lightingtest"> <application> - <activity android:name="ClearActivity" android:label="LightingTest"> + <activity android:name="ClearActivity" android:label="LightingTest" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/opengl/tests/testPauseResume/AndroidManifest.xml b/opengl/tests/testPauseResume/AndroidManifest.xml index 1879bc3217..ae82a8286a 100644 --- a/opengl/tests/testPauseResume/AndroidManifest.xml +++ b/opengl/tests/testPauseResume/AndroidManifest.xml @@ -24,7 +24,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden"> + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/rustfmt.toml b/rustfmt.toml new file mode 120000 index 0000000000..ee92d9efc5 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +../../build/soong/scripts/rustfmt.toml
\ No newline at end of file diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index 5b4ee21b42..fba64c7569 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -99,7 +99,7 @@ cc_binary { init_rc: ["gpuservice.rc"], required: [ "bpfloader", - "gpu_mem.o", + "gpuMem.o", ], srcs: [":gpuservice_binary_sources"], shared_libs: [ diff --git a/services/gpuservice/CleanSpec.mk b/services/gpuservice/CleanSpec.mk index 482fc6dfc3..c51f6aa5f0 100644 --- a/services/gpuservice/CleanSpec.mk +++ b/services/gpuservice/CleanSpec.mk @@ -44,9 +44,9 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) -# Remove gpu_mem.o +# Remove gpuMem.o $(call add-clean-step, rm -rf $(OUT_DIR)/soong/.intermediates/frameworks/native/services/gpuservice/bpf) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpu_mem.o_gpu_mem.o_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpu_mem.o) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpu_mem.o-timestamp) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/FAKE/gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/gpuMem.o_gpuMem.o_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/bpf/gpuMem.o) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/fake_packages/gpuMem.o-timestamp) diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp index 076affda5a..680b291fe3 100644 --- a/services/gpuservice/bpfprogs/Android.bp +++ b/services/gpuservice/bpfprogs/Android.bp @@ -22,8 +22,8 @@ package { } bpf { - name: "gpu_mem.o", - srcs: ["gpu_mem.c"], + name: "gpuMem.o", + srcs: ["gpuMem.c"], btf: true, cflags: [ "-Wall", diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpuMem.c index 16e1e8a1ef..16e1e8a1ef 100644 --- a/services/gpuservice/bpfprogs/gpu_mem.c +++ b/services/gpuservice/bpfprogs/gpuMem.c diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h index de691e2b75..7588b54818 100644 --- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h +++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h @@ -57,9 +57,9 @@ private: static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total"; // pinned gpu memory total bpf c program path in bpf sysfs static constexpr char kGpuMemTotalProgPath[] = - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"; + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"; // pinned gpu memory total bpf map path in bpf sysfs - static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"; + static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"; // 30 seconds timeout for trying to attach bpf program to tracepoint static constexpr int kGpuWaitTimeout = 30; }; diff --git a/services/gpuservice/gpuwork/Android.bp b/services/gpuservice/gpuwork/Android.bp index a9a59bc023..e20404044a 100644 --- a/services/gpuservice/gpuwork/Android.bp +++ b/services/gpuservice/gpuwork/Android.bp @@ -55,6 +55,6 @@ cc_library_shared { ], required: [ "bpfloader", - "gpu_work.o", + "gpuWork.o", ], } diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index 974ae38b13..fd703239e9 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -42,7 +42,7 @@ #include <unordered_set> #include <vector> -#include "gpuwork/gpu_work.h" +#include "gpuwork/gpuWork.h" #define ONE_MS_IN_NS (10000000) @@ -128,11 +128,11 @@ void GpuWork::initialize() { { std::lock_guard<std::mutex> lock(mMutex); - if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_map", &mGpuWorkMap)) { + if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_map", &mGpuWorkMap)) { return; } - if (!getBpfMap("/sys/fs/bpf/map_gpu_work_gpu_work_global_data", &mGpuWorkGlobalDataMap)) { + if (!getBpfMap("/sys/fs/bpf/map_gpuWork_gpu_work_global_data", &mGpuWorkGlobalDataMap)) { return; } @@ -140,7 +140,7 @@ void GpuWork::initialize() { } // Attach the tracepoint. - if (!attachTracepoint("/sys/fs/bpf/prog_gpu_work_tracepoint_power_gpu_work_period", "power", + if (!attachTracepoint("/sys/fs/bpf/prog_gpuWork_tracepoint_power_gpu_work_period", "power", "gpu_work_period")) { return; } diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp index b3c4eff847..fe45c98494 100644 --- a/services/gpuservice/gpuwork/bpfprogs/Android.bp +++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp @@ -17,8 +17,8 @@ package { } bpf { - name: "gpu_work.o", - srcs: ["gpu_work.c"], + name: "gpuWork.o", + srcs: ["gpuWork.c"], cflags: [ "-Wall", "-Werror", diff --git a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c index d73fff4e39..f4701896b6 100644 --- a/services/gpuservice/gpuwork/bpfprogs/gpu_work.c +++ b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "include/gpuwork/gpu_work.h" +#include "include/gpuwork/gpuWork.h" #include <linux/bpf.h> #include <stddef.h> diff --git a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h index 2affb60a0c..2affb60a0c 100644 --- a/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpu_work.h +++ b/services/gpuservice/gpuwork/bpfprogs/include/gpuwork/gpuWork.h diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index acecd569aa..cece9999c6 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -27,7 +27,7 @@ #include <functional> #include <thread> -#include "gpuwork/gpu_work.h" +#include "gpuwork/gpuWork.h" namespace android { namespace gpuwork { diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 4fb0d2e734..86f6c7febb 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -35,6 +35,7 @@ cc_test { header_libs: ["bpf_headers"], shared_libs: [ "libbase", + "libbinder", "libbpf_bcc", "libcutils", "libgfxstats", diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp index e916221c2e..8dabe4fbdb 100644 --- a/services/gpuservice/tests/unittests/GpuMemTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "gpuservice_unittest" #include <android-base/stringprintf.h> +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gmock/gmock.h> #include <gpumem/GpuMem.h> @@ -65,11 +66,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); mTestableGpuMem.setInitialized(); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } @@ -89,8 +90,8 @@ TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(), - "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total"); - EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map"); + "/sys/fs/bpf/prog_gpuMem_tracepoint_gpu_mem_gpu_mem_total"); + EXPECT_EQ(mTestableGpuMem.getGpuMemTotalMapPath(), "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map"); } TEST_F(GpuMemTest, bpfInitializationFailed) { diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp index d76f039a6d..5c042102b2 100644 --- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "gpuservice_unittest" +#define BPF_MAP_MAKE_VISIBLE_FOR_TESTING #include <bpf/BpfMap.h> #include <gpumem/GpuMem.h> #include <gtest/gtest.h> @@ -64,11 +65,11 @@ public: mTestableGpuMem = TestableGpuMem(mGpuMem.get()); errno = 0; - mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, - BPF_F_NO_PREALLOC); + mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, + TEST_MAP_SIZE, + BPF_F_NO_PREALLOC)); EXPECT_EQ(0, errno); - EXPECT_LE(0, mTestMap.getMap().get()); EXPECT_TRUE(mTestMap.isValid()); } diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp index 20c8ccf9bf..7ea22888f8 100644 --- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp +++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp @@ -18,12 +18,14 @@ #define LOG_TAG "gpuservice_unittest" #include <unistd.h> +#include <binder/ProcessState.h> #include <cutils/properties.h> #include <gmock/gmock.h> #include <gpustats/GpuStats.h> #include <gtest/gtest.h> #include <stats_pull_atom_callback.h> #include <statslog.h> +#include <utils/Looper.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -61,8 +63,9 @@ enum InputCommand : int32_t { // clang-format on class GpuStatsTest : public testing::Test { + sp<android::Looper> looper; public: - GpuStatsTest() { + GpuStatsTest() : looper(Looper::prepare(0 /* opts */)) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); @@ -72,6 +75,16 @@ public: const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + + // This is required for test due to GpuStats instance spawns binder transactions + // in its destructor. After the gtest destructor test exits immidiatelly. + // It results in binder thread not able to process above binder transactions and memory leak + // occures. Binder thread needs time to process callbacks transactions. + // It leads to GpuStats instance destructor needs to be called in advance. + mGpuStats.reset(nullptr); + // performs all pending callbacks until all data has been consumed + // gives time to process binder transactions by thread pool + looper->pollAll(1000); } std::string inputCommand(InputCommand cmd); @@ -79,6 +92,10 @@ public: void SetUp() override { mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0); mGlesVersion = property_get_int32("ro.opengles.version", 0); + + // start the thread pool + sp<ProcessState> ps(ProcessState::self()); + ps->startThreadPool(); } std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>(); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 41878e3487..18d670ae38 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -147,6 +147,7 @@ cc_defaults { srcs: [":libinputflinger_base_sources"], shared_libs: [ "libbase", + "libbinder", "libcutils", "libinput", "liblog", diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS index 82c6ee12c7..c88bfe97ca 100644 --- a/services/inputflinger/OWNERS +++ b/services/inputflinger/OWNERS @@ -1,3 +1 @@ -lzye@google.com -michaelwr@google.com -svv@google.com +include platform/frameworks/base:/INPUT_OWNERS diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index d8120fcb5e..9d1adad7ec 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3642,6 +3642,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + const bool wasEmpty = connection->outboundQueue.empty(); + for (size_t i = 0; i < cancelationEvents.size(); i++) { std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); switch (cancelationEventEntry->type) { @@ -3676,7 +3678,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget::FLAG_DISPATCH_AS_IS); } - startDispatchCycleLocked(currentTime, connection); + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty && !connection->outboundQueue.empty()) { + startDispatchCycleLocked(currentTime, connection); + } } void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( @@ -3710,6 +3715,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + const bool wasEmpty = connection->outboundQueue.empty(); + for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { switch (downEventEntry->type) { case EventEntry::Type::MOTION: { @@ -3735,8 +3742,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target, InputTarget::FLAG_DISPATCH_AS_IS); } - - startDispatchCycleLocked(currentTime, connection); + // If the outbound queue was previously empty, start the dispatch cycle going. + if (wasEmpty && !connection->outboundQueue.empty()) { + startDispatchCycleLocked(currentTime, connection); + } } std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 3bd3275bb1..01146a3c8b 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -71,7 +71,7 @@ cc_defaults { "libstatslog", "libui", "libutils", - "PlatformProperties", + "libPlatformProperties", ], static_libs: [ "libc++fs", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index d6a6bd214e..20baa42a20 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -452,8 +452,7 @@ bool EventHub::Device::hasKeycodeLocked(int keycode) const { return false; } - std::vector<int32_t> scanCodes; - keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode); const size_t N = scanCodes.size(); for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; @@ -548,10 +547,10 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } - int32_t scanCode; - if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { - *outScanCode = scanCode; + std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led); + if (scanCode.has_value()) { + if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) { + *outScanCode = *scanCode; return NO_ERROR; } } @@ -865,8 +864,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode); if (scanCodes.size() != 0) { if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { @@ -890,8 +888,8 @@ int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKey device->keyMap.keyLayoutMap == nullptr) { return AKEYCODE_UNKNOWN; } - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes); + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode); if (scanCodes.empty()) { ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input" "device %d", @@ -960,20 +958,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], - &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { - outFlags[codeIndex] = 1; - break; - } + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); + + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (device->keyBitmask.test(scanCodes[sc])) { + outFlags[codeIndex] = 1; + break; } } } @@ -1027,14 +1021,15 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } + if (device == nullptr || !device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; } - - return NAME_NOT_FOUND; + std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode); + if (!info.has_value()) { + return NAME_NOT_FOUND; + } + *outAxisInfo = *info; + return NO_ERROR; } base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId, @@ -1314,7 +1309,8 @@ static std::string generateDescriptor(InputDeviceIdentifier& identifier) { if (!identifier.uniqueId.empty()) { rawDescriptor += "uniqueId:"; rawDescriptor += identifier.uniqueId; - } else if (identifier.nonce != 0) { + } + if (identifier.nonce != 0) { rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce); } @@ -1342,16 +1338,20 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { // of Android. In practice we sometimes get devices that cannot be uniquely // identified. In this case we enforce uniqueness between connected devices. // Ideally, we also want the descriptor to be short and relatively opaque. + // Note that we explicitly do not use the path or location for external devices + // as their path or location will change as they are plugged/unplugged or moved + // to different ports. We do fallback to using name and location in the case of + // internal devices which are detected by the vendor and product being 0 in + // generateDescriptor. If two identical descriptors are detected we will fallback + // to using a 'nonce' and incrementing it until the new descriptor no longer has + // a match with any existing descriptors. identifier.nonce = 0; std::string rawDescriptor = generateDescriptor(identifier); - if (identifier.uniqueId.empty()) { - // If it didn't have a unique id check for conflicts and enforce - // uniqueness if necessary. - while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { - identifier.nonce++; - rawDescriptor = generateDescriptor(identifier); - } + // Enforce that the generated descriptor is unique. + while (hasDeviceWithDescriptorLocked(identifier.descriptor)) { + identifier.nonce++; + rawDescriptor = generateDescriptor(identifier); } ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), identifier.descriptor.c_str()); @@ -1426,13 +1426,22 @@ std::vector<int32_t> EventHub::getVibratorIds(int32_t deviceId) { return vibrators; } -EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { +/** + * Checks both mDevices and mOpeningDevices for a device with the descriptor passed. + */ +bool EventHub::hasDeviceWithDescriptorLocked(const std::string& descriptor) const { + for (const auto& device : mOpeningDevices) { + if (descriptor == device->identifier.descriptor) { + return true; + } + } + for (const auto& [id, device] : mDevices) { if (descriptor == device->identifier.descriptor) { - return device.get(); + return true; } } - return nullptr; + return false; } EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { @@ -1476,25 +1485,35 @@ EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const { } std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t batteryId) const { - std::scoped_lock _l(mLock); + std::filesystem::path batteryPath; + { + // Do not read the sysfs node to get the battery state while holding + // the EventHub lock. For some peripheral devices, reading battery state + // can be broken and take 5+ seconds. Holding the lock in this case would + // block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and read the sysfs node outside + // the lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + + const auto infos = getBatteryInfoLocked(deviceId); + auto it = infos.find(batteryId); + if (it == infos.end()) { + return std::nullopt; + } + batteryPath = it->second.path; + } // release lock - const auto infos = getBatteryInfoLocked(deviceId); - auto it = infos.find(batteryId); - if (it == infos.end()) { - return std::nullopt; - } std::string buffer; // Some devices report battery capacity as an integer through the "capacity" file - if (base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::CAPACITY), + if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY), &buffer)) { return std::stoi(base::Trim(buffer)); } // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX // These values are taken from kernel source code include/linux/power_supply.h - if (base::ReadFileToString(it->second.path / - BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL), + if (base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::CAPACITY_LEVEL), &buffer)) { // Remove any white space such as trailing new line const auto levelIt = BATTERY_LEVEL.find(base::Trim(buffer)); @@ -1507,15 +1526,27 @@ std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId, int32_t ba } std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId, int32_t batteryId) const { - std::scoped_lock _l(mLock); - const auto infos = getBatteryInfoLocked(deviceId); - auto it = infos.find(batteryId); - if (it == infos.end()) { - return std::nullopt; - } + std::filesystem::path batteryPath; + { + // Do not read the sysfs node to get the battery state while holding + // the EventHub lock. For some peripheral devices, reading battery state + // can be broken and take 5+ seconds. Holding the lock in this case would + // block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and read the sysfs node outside + // the lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + + const auto infos = getBatteryInfoLocked(deviceId); + auto it = infos.find(batteryId); + if (it == infos.end()) { + return std::nullopt; + } + batteryPath = it->second.path; + } // release lock + std::string buffer; - if (!base::ReadFileToString(it->second.path / BATTERY_NODES.at(InputBatteryClass::STATUS), + if (!base::ReadFileToString(batteryPath / BATTERY_NODES.at(InputBatteryClass::STATUS), &buffer)) { ALOGE("Failed to read sysfs battery info: %s", strerror(errno)); return std::nullopt; diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index a0119986a6..3ee0fe8de6 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -548,14 +548,6 @@ void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) { for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); }); } -std::optional<int32_t> InputDevice::getBatteryCapacity() { - return mController ? mController->getBatteryCapacity(DEFAULT_BATTERY_ID) : std::nullopt; -} - -std::optional<int32_t> InputDevice::getBatteryStatus() { - return mController ? mController->getBatteryStatus(DEFAULT_BATTERY_ID) : std::nullopt; -} - bool InputDevice::setLightColor(int32_t lightId, int32_t color) { return mController ? mController->setLightColor(lightId, color) : false; } @@ -623,6 +615,10 @@ void InputDevice::updateLedState(bool reset) { for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); }); } +std::optional<int32_t> InputDevice::getBatteryEventHubId() const { + return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt; +} + InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 9bcf463c36..905b348caa 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -38,6 +38,26 @@ using android::base::StringPrintf; namespace android { +/** + * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices + * that expose multiple input device paths such a keyboard that also has a touchpad input. + * These are separate devices with unique descriptors in EventHub, but InputReader should + * create a single InputDevice for them. + * Sub-devices are detected by the following criteria: + * 1. The vendor, product, bus, version, and unique id match + * 2. The location matches. The location is used to distinguish a single device with multiple + * inputs versus the same device plugged into multiple ports. + */ + +static bool isSubDevice(const InputDeviceIdentifier& identifier1, + const InputDeviceIdentifier& identifier2) { + return (identifier1.vendor == identifier2.vendor && + identifier1.product == identifier2.product && identifier1.bus == identifier2.bus && + identifier1.version == identifier2.version && + identifier1.uniqueId == identifier2.uniqueId && + identifier1.location == identifier2.location); +} + // --- InputReader --- InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, @@ -271,8 +291,9 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { std::shared_ptr<InputDevice> InputReader::createDeviceLocked( int32_t eventHubId, const InputDeviceIdentifier& identifier) { auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) { - return devicePair.second->getDescriptor().size() && identifier.descriptor.size() && - devicePair.second->getDescriptor() == identifier.descriptor; + const InputDeviceIdentifier identifier2 = + devicePair.second->getDeviceInfo().getIdentifier(); + return isSubDevice(identifier, identifier2); }); std::shared_ptr<InputDevice> device; @@ -685,23 +706,43 @@ void InputReader::flushSensor(int32_t deviceId, InputDeviceSensorType sensorType } std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) { - std::scoped_lock _l(mLock); + std::optional<int32_t> eventHubId; + { + // Do not query the battery state while holding the lock. For some peripheral devices, + // reading battery state can be broken and take 5+ seconds. Holding the lock in this case + // would block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and get the battery state outside the + // lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) return {}; + eventHubId = device->getBatteryEventHubId(); + } // release lock - InputDevice* device = findInputDeviceLocked(deviceId); - if (device) { - return device->getBatteryCapacity(); - } - return std::nullopt; + if (!eventHubId) return {}; + const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); + if (batteryIds.empty()) return {}; + return mEventHub->getBatteryCapacity(*eventHubId, batteryIds.front()); } std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) { - std::scoped_lock _l(mLock); + std::optional<int32_t> eventHubId; + { + // Do not query the battery state while holding the lock. For some peripheral devices, + // reading battery state can be broken and take 5+ seconds. Holding the lock in this case + // would block all other event processing during this time. For now, we assume this + // call never happens on the InputReader thread and get the battery state outside the + // lock to prevent event processing from being blocked by this call. + std::scoped_lock _l(mLock); + InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) return {}; + eventHubId = device->getBatteryEventHubId(); + } // release lock - InputDevice* device = findInputDeviceLocked(deviceId); - if (device) { - return device->getBatteryStatus(); - } - return std::nullopt; + if (!eventHubId) return {}; + const auto batteryIds = mEventHub->getRawBatteryIds(*eventHubId); + if (batteryIds.empty()) return {}; + return mEventHub->getBatteryStatus(*eventHubId, batteryIds.front()); } std::vector<InputDeviceLightInfo> InputReader::getLights(int32_t deviceId) { diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index a6934960c9..8065f57524 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -524,4 +524,8 @@ std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) { return light->getLightPlayerId(); } +int32_t PeripheralController::getEventHubId() const { + return getDeviceContext().getEventHubId(); +} + } // namespace android diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h index b1bc8c732c..ac951ebe2a 100644 --- a/services/inputflinger/reader/controller/PeripheralController.h +++ b/services/inputflinger/reader/controller/PeripheralController.h @@ -31,6 +31,7 @@ public: explicit PeripheralController(InputDeviceContext& deviceContext); ~PeripheralController() override; + int32_t getEventHubId() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; bool setLightColor(int32_t lightId, int32_t color) override; @@ -43,6 +44,7 @@ public: private: inline int32_t getDeviceId() { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; } InputDeviceContext& mDeviceContext; void configureLights(); diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h index 7688a431d1..306e36119b 100644 --- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h +++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h @@ -33,6 +33,8 @@ public: PeripheralControllerInterface() {} virtual ~PeripheralControllerInterface() {} + virtual int32_t getEventHubId() const = 0; + // Interface methods for Battery virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0; virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0; diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 130c55639b..54c6810a33 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -650,7 +650,6 @@ private: void scanDevicesLocked() REQUIRES(mLock); status_t readNotifyLocked() REQUIRES(mLock); - Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock); Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock); Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock); /** @@ -660,6 +659,9 @@ private: Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock); int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock); + + bool hasDeviceWithDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock); + void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock); void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier, ftl::Flags<InputDeviceClass> classes) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 728020eadc..b3a24af8a9 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -32,8 +32,6 @@ #include "InputReaderContext.h" namespace android { -// TODO b/180733860 support multiple battery in API and remove this. -constexpr int32_t DEFAULT_BATTERY_ID = 1; class PeripheralController; class PeripheralControllerInterface; @@ -100,8 +98,7 @@ public: void disableSensor(InputDeviceSensorType sensorType); void flushSensor(InputDeviceSensorType sensorType); - std::optional<int32_t> getBatteryCapacity(); - std::optional<int32_t> getBatteryStatus(); + std::optional<int32_t> getBatteryEventHubId() const; bool setLightColor(int32_t lightId, int32_t color); bool setLightPlayerId(int32_t lightId, int32_t playerId); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 8233682a32..f4f3ae95a1 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -69,7 +69,11 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} -CursorInputMapper::~CursorInputMapper() {} +CursorInputMapper::~CursorInputMapper() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } +} uint32_t CursorInputMapper::getSources() const { return mSource; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 428fe10156..2ddacef02d 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1017,9 +1017,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) : getInverseRotation(mViewport.orientation); // For orientation-aware devices that work in the un-rotated coordinate space, the // viewport update should be skipped if it is only a change in the orientation. - skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware && - mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight && - viewportOrientationChanged; + skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth && + mDisplayHeight == oldDisplayHeight && viewportOrientationChanged; // Apply the input device orientation for the device. mInputDeviceOrientation = @@ -1034,6 +1033,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mDisplayHeight = rawHeight; mInputDeviceOrientation = DISPLAY_ORIENTATION_0; } + // If displayId changed, do not skip viewport update. + skipViewportUpdate &= !viewportDisplayIdChanged; } // If moving between pointer modes, need to reset some state. @@ -1055,6 +1056,10 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } } else { + if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT && + !mConfig.showTouches) { + mPointerController->clearSpots(); + } mPointerController.reset(); } diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index ef68a84fdb..6ef6e4498c 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -180,6 +180,20 @@ void EventHubTest::assertNoMoreEvents() { } /** + * Ensure that two identical devices get assigned unique descriptors from EventHub. + */ +TEST_F(EventHubTest, DevicesWithMatchingUniqueIdsAreUnique) { + std::unique_ptr<UinputHomeKey> keyboard2 = createUinputDevice<UinputHomeKey>(); + int32_t deviceId2; + ASSERT_NO_FATAL_FAILURE(deviceId2 = waitForDeviceCreation()); + + ASSERT_NE(mEventHub->getDeviceIdentifier(mDeviceId).descriptor, + mEventHub->getDeviceIdentifier(deviceId2).descriptor); + keyboard2.reset(); + waitForDeviceClose(deviceId2); +} + +/** * Ensure that input_events are generated with monotonic clock. * That means input_event should receive a timestamp that is in the future of the time * before the event was sent. diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index e1befedcff..575d950c9b 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -239,7 +239,7 @@ private: mSpotsByDisplay[displayId] = newSpots; } - void clearSpots() override {} + void clearSpots() override { mSpotsByDisplay.clear(); } std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; }; @@ -1007,7 +1007,9 @@ private: return BATTERY_STATUS; } - const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; } + const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) override { + return {DEFAULT_BATTERY}; + } std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) { return std::nullopt; @@ -2157,6 +2159,8 @@ public: ~FakePeripheralController() override {} + int32_t getEventHubId() const { return getDeviceContext().getEventHubId(); } + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {} void dump(std::string& dump) override {} @@ -2190,6 +2194,7 @@ private: InputDeviceContext& mDeviceContext; inline int32_t getDeviceId() { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; } }; TEST_F(InputReaderTest, BatteryGetCapacity) { @@ -6777,7 +6782,9 @@ TEST_F(SingleTouchInputMapperTest, NotifyMotionArgs motionArgs; // Down. - processDown(mapper, 100, 200); + int32_t x = 100; + int32_t y = 200; + processDown(mapper, x, y); processSync(mapper); // We should receive a down event @@ -8780,7 +8787,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Default device will reconfigure above, need additional reconfiguration for another device. device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); // Two fingers down at default display. int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; @@ -8807,6 +8815,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID); ASSERT_TRUE(iter != fakePointerController->getSpots().end()); ASSERT_EQ(size_t(2), iter->second.size()); + + // Disable the show touches configuration and ensure the spots are cleared. + mFakePolicy->setShowTouches(false); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); + + ASSERT_TRUE(fakePointerController->getSpots().empty()); } TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp index b62be5f5d4..741495878a 100644 --- a/services/powermanager/tests/IThermalManagerTest.cpp +++ b/services/powermanager/tests/IThermalManagerTest.cpp @@ -33,26 +33,38 @@ using namespace android; using namespace android::os; using namespace std::chrono_literals; -class IThermalServiceTest : public testing::Test, - public BnThermalStatusListener{ +class IThermalServiceTestListener : public BnThermalStatusListener { + public: + virtual binder::Status onStatusChange(int status) override; + std::condition_variable mCondition; + int mListenerStatus = 0; + std::mutex mMutex; +}; + +binder::Status IThermalServiceTestListener::onStatusChange(int status) { + std::unique_lock<std::mutex> lock(mMutex); + mListenerStatus = status; + ALOGI("IThermalServiceTestListener::notifyListener %d", mListenerStatus); + mCondition.notify_all(); + return binder::Status::ok(); +} + +class IThermalServiceTest : public testing::Test { public: IThermalServiceTest(); void setThermalOverride(int level); - virtual binder::Status onStatusChange(int status) override; int getStatusFromService(); void SetUp() override; void TearDown() override; protected: sp<IThermalService> mThermalSvc; - std::condition_variable mCondition; - int mListenerStatus; int mServiceStatus; - std::mutex mMutex; + sp<IThermalServiceTestListener> mCallback; }; IThermalServiceTest::IThermalServiceTest() - : mListenerStatus(0), - mServiceStatus(0) { + : mServiceStatus(0), + mCallback(sp<IThermalServiceTestListener>::make()) { } void IThermalServiceTest::setThermalOverride(int level) { @@ -60,14 +72,6 @@ void IThermalServiceTest::setThermalOverride(int level) { system(cmdStr.c_str()); } -binder::Status IThermalServiceTest::onStatusChange(int status) { - std::unique_lock<std::mutex> lock(mMutex); - mListenerStatus = status; - ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus); - mCondition.notify_all(); - return binder::Status::ok(); -} - int IThermalServiceTest::getStatusFromService() { int status; binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status); @@ -87,19 +91,19 @@ void IThermalServiceTest::SetUp() { mThermalSvc = interface_cast<IThermalService>(binder); EXPECT_NE(mThermalSvc, nullptr); // Lock mutex for operation, so listener will only be processed after wait_for is called - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mCallback->mMutex); bool success = false; - binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success); + binder::Status ret = mThermalSvc->registerThermalStatusListener(mCallback, &success); // Check the result ASSERT_TRUE(success); ASSERT_TRUE(ret.isOk()); // Wait for listener called after registration, shouldn't timeout - EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); + EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout); } void IThermalServiceTest::TearDown() { bool success = false; - binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success); + binder::Status ret = mThermalSvc->unregisterThermalStatusListener(mCallback, &success); ASSERT_TRUE(success); ASSERT_TRUE(ret.isOk()); } @@ -114,14 +118,14 @@ class IThermalListenerTest : public IThermalServiceTest, public testing::WithPar TEST_P(IThermalListenerTest, TestListener) { int level = GetParam(); // Lock mutex for operation, so listener will only be processed after wait_for is called - std::unique_lock<std::mutex> lock(mMutex); + std::unique_lock<std::mutex> lock(mCallback->mMutex); // Set the override thermal status setThermalOverride(level); // Wait for listener called, shouldn't timeout - EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); + EXPECT_NE(mCallback->mCondition.wait_for(lock, 1s), std::cv_status::timeout); // Check the result - EXPECT_EQ(level, mListenerStatus); - ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level); + EXPECT_EQ(level, mCallback->mListenerStatus); + ALOGI("Thermal listener status %d, expecting %d", mCallback->mListenerStatus, level); } INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range( @@ -158,4 +162,4 @@ int main(int argc, char **argv) { ALOGV("Test result = %d\n", status); return status; -}
\ No newline at end of file +} diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 000a2cb0d3..cbb95f963c 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -280,7 +280,7 @@ cc_library_shared { "liblog", ], static_libs: [ - "SurfaceFlingerProperties", + "libSurfaceFlingerProperties", ], export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", @@ -288,6 +288,6 @@ cc_library_shared { "libui", ], export_static_lib_headers: [ - "SurfaceFlingerProperties", + "libSurfaceFlingerProperties", ], } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index 8e4e9af48c..24a7744542 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -150,8 +150,6 @@ public: bool hasSolidColorLayers() const; private: - CachedSet() = default; - const NonBufferHash mFingerprint; std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now(); std::vector<Layer> mLayers; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index bcc94d3146..0c4b012fe8 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1587,8 +1587,10 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; + const float parentShadowRadius = + newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - newParent->mEffectiveShadowRadius); + parentShadowRadius); } } diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 2ece51c3dc..6011d0d0f7 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -2,6 +2,7 @@ adyabr@google.com alecmouri@google.com chaviw@google.com lpy@google.com +pdwilliams@google.com racarr@google.com scroggo@google.com -vishnun@google.com
\ No newline at end of file +vishnun@google.com diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 13cd304a5f..e23945dc8a 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "VSyncReactor" //#define LOG_NDEBUG 0 +#include <assert.h> #include <cutils/properties.h> #include <log/log.h> #include <utils/Trace.h> diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index fd824dc502..4e2fbfd9b4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1960,8 +1960,16 @@ SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() { const auto now = systemTime(); const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod; const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod; - return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0] - : mPreviousPresentFences[1]; + + size_t shift = 0; + if (!expectedPresentTimeIsTheNextVsync) { + shift = static_cast<size_t>((mExpectedPresentTime - now) / vsyncPeriod); + if (shift >= mPreviousPresentFences.size()) { + shift = mPreviousPresentFences.size() - 1; + } + } + ATRACE_FORMAT("previousFrameFence shift=%zu", shift); + return mPreviousPresentFences[shift]; } bool SurfaceFlinger::previousFramePending(int graceTimeMs) { @@ -2447,7 +2455,10 @@ void SurfaceFlinger::postComposition() { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - mPreviousPresentFences[1] = mPreviousPresentFences[0]; + for (size_t i = mPreviousPresentFences.size()-1; i >= 1; i--) { + mPreviousPresentFences[i] = mPreviousPresentFences[i-1]; + } + mPreviousPresentFences[0].fence = display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE; mPreviousPresentFences[0].fenceTime = diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 83134a2ebc..8cfd60c8a5 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1224,7 +1224,8 @@ private: std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames; // Tracks layers that need to update a display's dirty region. std::vector<sp<Layer>> mLayersPendingRefresh; - std::array<FenceWithFenceTime, 2> mPreviousPresentFences; + // size should be longest sf-duration / shortest vsync period and round up + std::array<FenceWithFenceTime, 5> mPreviousPresentFences; // currently consider 166hz. // True if in the previous frame at least one layer was composed via the GPU. bool mHadClientComposition = false; // True if in the previous frame at least one layer was composed via HW Composer. diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index d33bc1080c..434c297cc9 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -52,19 +52,12 @@ const String8 SURFACE_NAME("Test Surface Name"); #pragma clang diagnostic ignored "-Wconversion" class CredentialsTest : public ::testing::Test { protected: - void SetUp() override { - // Start the tests as root. - seteuid(AID_ROOT); - - ASSERT_NO_FATAL_FAILURE(initClient()); - } + void SetUp() override { ASSERT_NO_FATAL_FAILURE(initClient()); } void TearDown() override { mComposerClient->dispose(); mBGSurfaceControl.clear(); mComposerClient.clear(); - // Finish the tests as root. - seteuid(AID_ROOT); } sp<IBinder> mDisplay; @@ -99,31 +92,6 @@ protected: } /** - * Sets UID to imitate Graphic's process. - */ - void setGraphicsUID() { - seteuid(AID_ROOT); - seteuid(AID_GRAPHICS); - } - - /** - * Sets UID to imitate System's process. - */ - void setSystemUID() { - seteuid(AID_ROOT); - seteuid(AID_SYSTEM); - } - - /** - * Sets UID to imitate a process that doesn't have any special privileges in - * our code. - */ - void setBinUID() { - seteuid(AID_ROOT); - seteuid(AID_BIN); - } - - /** * Template function the check a condition for different types of users: root * graphics, system, and non-supported user. Root, graphics, and system should * always equal privilegedValue, and non-supported user should equal unprivilegedValue. @@ -131,24 +99,34 @@ protected: template <typename T> void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) { // Check with root. - seteuid(AID_ROOT); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a Graphics user. - setGraphicsUID(); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a system user. - setSystemUID(); - ASSERT_EQ(privilegedValue, condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_EQ(privilegedValue, condition()); + } // Check as a non-supported user. - setBinUID(); - ASSERT_EQ(unprivilegedValue, condition()); + { + UIDFaker f(AID_BIN); + ASSERT_EQ(unprivilegedValue, condition()); + } // Check as shell since shell has some additional permissions - seteuid(AID_SHELL); - ASSERT_EQ(unprivilegedValue, condition()); + { + UIDFaker f(AID_SHELL); + ASSERT_EQ(privilegedValue, condition()); + } } }; @@ -157,17 +135,23 @@ TEST_F(CredentialsTest, ClientInitTest) { ASSERT_NO_FATAL_FAILURE(initClient()); // Graphics can init the client. - setGraphicsUID(); - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_NO_FATAL_FAILURE(initClient()); + } // System can init the client. - setSystemUID(); - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_NO_FATAL_FAILURE(initClient()); + } // Anyone else can init the client. - setBinUID(); - mComposerClient = new SurfaceComposerClient; - ASSERT_NO_FATAL_FAILURE(initClient()); + { + UIDFaker f(AID_BIN); + mComposerClient = new SurfaceComposerClient; + ASSERT_NO_FATAL_FAILURE(initClient()); + } } TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { @@ -181,7 +165,7 @@ TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { TEST_F(CredentialsTest, AllowedGetterMethodsTest) { // The following methods are tested with a UID that is not root, graphics, // or system, to show that anyone can access them. - setBinUID(); + UIDFaker f(AID_BIN); const auto display = SurfaceComposerClient::getInternalDisplayToken(); ASSERT_TRUE(display != nullptr); @@ -250,24 +234,34 @@ TEST_F(CredentialsTest, CreateDisplayTest) { }; // Check with root. - seteuid(AID_ROOT); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_ROOT); + ASSERT_FALSE(condition()); + } // Check as a Graphics user. - setGraphicsUID(); - ASSERT_TRUE(condition()); + { + UIDFaker f(AID_GRAPHICS); + ASSERT_TRUE(condition()); + } // Check as a system user. - setSystemUID(); - ASSERT_TRUE(condition()); + { + UIDFaker f(AID_SYSTEM); + ASSERT_TRUE(condition()); + } // Check as a non-supported user. - setBinUID(); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_BIN); + ASSERT_FALSE(condition()); + } // Check as shell since shell has some additional permissions - seteuid(AID_SHELL); - ASSERT_FALSE(condition()); + { + UIDFaker f(AID_SHELL); + ASSERT_FALSE(condition()); + } condition = [=]() { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); @@ -313,17 +307,22 @@ TEST_F(CredentialsTest, GetLayerDebugInfo) { // is called when we call dumpsys. I don't see a reason why we should change this. std::vector<LayerDebugInfo> outLayers; // Check with root. - seteuid(AID_ROOT); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_ROOT); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + } // Check as a shell. - seteuid(AID_SHELL); - ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_SHELL); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + } // Check as anyone else. - seteuid(AID_ROOT); - seteuid(AID_BIN); - ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); + { + UIDFaker f(AID_BIN); + ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); + } } TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp index fb4458a27e..916267447b 100644 --- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp +++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp @@ -80,14 +80,6 @@ void toggleOverlay(bool enabled) { } // namespace -TEST(RefreshRateOverlayTest, enableOverlay) { - toggleOverlay(true); -} - -TEST(RefreshRateOverlayTest, disableOverlay) { - toggleOverlay(false); -} - TEST(RefreshRateOverlayTest, enableAndDisableOverlay) { toggleOverlay(true); toggleOverlay(false); diff --git a/services/utils/tests/Android.bp b/services/utils/tests/Android.bp index 54cf5b7404..cfa8a08c66 100644 --- a/services/utils/tests/Android.bp +++ b/services/utils/tests/Android.bp @@ -34,5 +34,4 @@ cc_test { "libgmock", "libserviceutils", ], - clang: true, } diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp index 2002bdf628..5403baf2e7 100644 --- a/services/vibratorservice/Android.bp +++ b/services/vibratorservice/Android.bp @@ -59,6 +59,12 @@ cc_library_shared { "-Wunreachable-code", ], + // FIXME: Workaround LTO build breakage + // http://b/241699694 + lto: { + never: true, + }, + local_include_dirs: ["include"], export_include_dirs: ["include"], diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 440c5b144a..5719b5cf61 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -37,7 +37,6 @@ cc_library_shared { "vulkan_headers", ], }, - clang: true, sanitize: { misc_undefined: ["integer"], }, diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index c4b1487267..1fe8800e09 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -19,6 +19,8 @@ #include <android/hardware/graphics/common/1.0/types.h> #include <grallocusage/GrallocUsageConversion.h> #include <graphicsenv/GraphicsEnv.h> +#include <hardware/gralloc.h> +#include <hardware/gralloc1.h> #include <log/log.h> #include <sync/sync.h> #include <system/window.h> @@ -42,6 +44,26 @@ namespace driver { namespace { +static uint64_t convertGralloc1ToBufferUsage(uint64_t producerUsage, + uint64_t consumerUsage) { + static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) == + uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN), + "expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN " + "bits to match"); + uint64_t merged = producerUsage | consumerUsage; + if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) == + GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) { + merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN); + merged |= BufferUsage::CPU_READ_OFTEN; + } + if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) == + GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) { + merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN); + merged |= BufferUsage::CPU_WRITE_OFTEN; + } + return merged; +} + const VkSurfaceTransformFlagsKHR kSupportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | @@ -763,7 +785,11 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, // We must support R8G8B8A8 std::vector<VkSurfaceFormatKHR> all_formats = { {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY + {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}, + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}, + }; if (colorspace_ext) { all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -785,12 +811,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_PASS_THROUGH_EXT}); } desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT}); if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, @@ -806,6 +836,9 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_PASS_THROUGH_EXT}); if (wide_color_support) { all_formats.emplace_back( VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, @@ -939,8 +972,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, // VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and // VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR. We technically cannot // know if VK_PRESENT_MODE_SHARED_MAILBOX_KHR is supported without a - // surface, and that cannot be relied upon. - present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); + // surface, and that cannot be relied upon. Therefore, don't return it. present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } else { ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); @@ -1228,11 +1260,15 @@ VkResult CreateSwapchainKHR(VkDevice device, decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } - err = native_window_set_buffers_data_space(window, native_dataspace); - if (err != android::OK) { - ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", - native_dataspace, strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; + + /* Respect consumer default dataspace upon HAL_DATASPACE_ARBITRARY. */ + if (native_dataspace != HAL_DATASPACE_ARBITRARY) { + err = native_window_set_buffers_data_space(window, native_dataspace); + if (err != android::OK) { + ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", + native_dataspace, strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } } err = native_window_set_buffers_dimensions( @@ -1323,7 +1359,7 @@ VkResult CreateSwapchainKHR(VkDevice device, num_images = 1; } - int32_t legacy_usage = 0; + uint64_t native_usage = 0; if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); @@ -1335,10 +1371,11 @@ VkResult CreateSwapchainKHR(VkDevice device, ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } - legacy_usage = - android_convertGralloc1To0Usage(producer_usage, consumer_usage); + native_usage = + convertGralloc1ToBufferUsage(consumer_usage, producer_usage); } else if (dispatch.GetSwapchainGrallocUsageANDROID) { ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); + int32_t legacy_usage = 0; result = dispatch.GetSwapchainGrallocUsageANDROID( device, create_info->imageFormat, create_info->imageUsage, &legacy_usage); @@ -1347,8 +1384,9 @@ VkResult CreateSwapchainKHR(VkDevice device, ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result); return VK_ERROR_SURFACE_LOST_KHR; } + native_usage = static_cast<uint64_t>(legacy_usage); } - uint64_t native_usage = static_cast<uint64_t>(legacy_usage); + native_usage |= surface.consumer_usage; bool createProtectedSwapchain = false; if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) { diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index 0daad9c634..a6d540bb93 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -27,7 +27,6 @@ cc_library_shared { proprietary: true, relative_install_path: "hw", - clang: true, cflags: [ "-fvisibility=hidden", "-fstrict-aliasing", diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp index fa0258bc06..b6d3a0b45f 100644 --- a/vulkan/vkjson/Android.bp +++ b/vulkan/vkjson/Android.bp @@ -37,7 +37,6 @@ cc_library_shared { cc_library_static { name: "libvkjson_ndk", - clang: true, srcs: [ "vkjson.cc", "vkjson_instance.cc", |