diff options
186 files changed, 5250 insertions, 1680 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 4b64203387..260ee8dd4f 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -67,5 +67,66 @@ } ] } + ], + "hwasan-postsubmit": [ + { + "name": "SurfaceFlinger_test", + "options": [ + { + "include-filter": "*CredentialsTest.*" + }, + { + "include-filter": "*SurfaceFlingerStress.*" + }, + { + "include-filter": "*SurfaceInterceptorTest.*" + }, + { + "include-filter": "*LayerTransactionTest.*" + }, + { + "include-filter": "*LayerTypeTransactionTest.*" + }, + { + "include-filter": "*LayerUpdateTest.*" + }, + { + "include-filter": "*GeometryLatchingTest.*" + }, + { + "include-filter": "*CropLatchingTest.*" + }, + { + "include-filter": "*ChildLayerTest.*" + }, + { + "include-filter": "*ScreenCaptureTest.*" + }, + { + "include-filter": "*ScreenCaptureChildOnlyTest.*" + }, + { + "include-filter": "*DereferenceSurfaceControlTest.*" + }, + { + "include-filter": "*BoundlessLayerTest.*" + }, + { + "include-filter": "*MultiDisplayLayerBoundsTest.*" + }, + { + "include-filter": "*InvalidHandleTest.*" + }, + { + "include-filter": "*VirtualDisplayTest.*" + }, + { + "include-filter": "*RelativeZTest.*" + }, + { + "include-filter": "*RefreshRateOverlayTest.*" + } + ] + } ] } diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 7aee795fc4..b94f3daf03 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -1194,6 +1194,11 @@ int main(int argc, char **argv) bool traceStream = false; bool onlyUserspace = false; + fprintf(stderr, + "** Warning: atrace will end vendor support in the next Android Release. **\n" + "** Perfetto is the suggested replacement tool. It will gain vendor **\n" + "** support. See https://perfetto.dev/docs/quickstart/android-tracing **\n\n"); + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { showHelp(argv[0]); exit(0); @@ -1224,10 +1229,7 @@ int main(int argc, char **argv) if (ret < 0) { for (int i = optind; i < argc; i++) { - if (!setCategoryEnable(argv[i])) { - fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); - exit(1); - } + setCategoryEnable(argv[i]); } break; } @@ -1343,10 +1345,10 @@ int main(int argc, char **argv) // contain entries from only one CPU can cause "begin" entries without a // matching "end" entry to show up if a task gets migrated from one CPU to // another. - if (!onlyUserspace) + if (!onlyUserspace) { ok = clearTrace(); - - writeClockSyncMarker(); + writeClockSyncMarker(); + } if (ok && !async && !traceStream) { // Sleep to allow the trace to be captured. struct timespec timeLeft; diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 1c3a4f201c..a417493189 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -292,12 +292,9 @@ on late-init write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" -# Set up histogram triggers - # rss_stat_throttled (bucket size == 512KB) - chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + # allow creating event triggers chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger - write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" - write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index a2491e503f..a60972b722 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -101,6 +101,7 @@ cc_defaults { "libhidlbase", "liblog", "libutils", + "libvintf", "libbinderdebug", "packagemanager_aidl-cpp", ], diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 0e9ce897bc..942a17e79f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -88,6 +88,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 +830,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 +923,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 +1397,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) { @@ -1619,6 +1639,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"); @@ -1648,7 +1670,7 @@ static Dumpstate::RunStatus dumpstate() { DumpPacketStats(); - RunDumpsys("EBPF MAP STATS", {"netd", "trafficcontroller"}); + RunDumpsys("EBPF MAP STATS", {"connectivity", "trafficcontroller"}); DoKmsg(); @@ -2084,7 +2106,7 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { int timeout_failures = 0; bool dalvik_found = false; - const std::set<int> hal_pids = get_interesting_hal_pids(); + const std::set<int> hal_pids = get_interesting_pids(); struct dirent* d; while ((d = readdir(proc.get()))) { @@ -2418,7 +2440,7 @@ void Dumpstate::DumpstateBoard(int out_fd) { // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we // will kill the HAL and grab whatever it dumped in time. - constexpr size_t timeout_sec = 30; + constexpr size_t timeout_sec = 45; if (dumpstate_hal_handle_aidl != nullptr) { DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode, @@ -2733,8 +2755,8 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { // Duplicate the fds because the passed in fds don't outlive the binder transaction. - bugreport_fd.reset(dup(bugreport_fd_in.get())); - screenshot_fd.reset(dup(screenshot_fd_in.get())); + bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0)); + screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0)); SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested); } diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 49c1318945..f0c19b93ec 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -65,6 +65,7 @@ class ServiceManagerMock : public IServiceManager { const sp<LocalRegistrationCallback>&)); MOCK_METHOD2(unregisterForNotifications, status_t(const String16&, const sp<LocalRegistrationCallback>&)); + MOCK_METHOD0(getServiceDebugInfo, std::vector<ServiceDebugInfo>()); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index c9f680b266..0f7c48964f 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -72,8 +72,6 @@ cc_defaults { }, }, - clang: true, - tidy: true, tidy_checks: [ "-*", @@ -81,8 +79,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 +126,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", @@ -187,7 +185,6 @@ cc_binary { "-Wall", "-Werror", ], - clang: true, srcs: [ "otapreopt_chroot.cpp", diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b6ebfca2ed..ee3a67e5fd 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -41,6 +41,7 @@ #include <fstream> #include <functional> #include <regex> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> @@ -54,7 +55,8 @@ #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> -#include <log/log.h> // TODO: Move everything to base/logging. +#include <linux/quota.h> +#include <log/log.h> // TODO: Move everything to base/logging. #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -81,6 +83,7 @@ // #define GRANULAR_LOCKS using android::base::ParseUint; +using android::base::Split; using android::base::StringPrintf; using std::endl; @@ -115,6 +118,12 @@ static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; static std::atomic<bool> sAppDataIsolationEnabled(false); +/** + * Flag to control if project ids are supported for internal storage + */ +static std::atomic<bool> sUsingProjectIdsFlag(false); +static std::once_flag flag; + namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -456,14 +465,41 @@ done: return res; } -static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) { - if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) { +static bool internal_storage_has_project_id() { + // The following path is populated in setFirstBoot, so if this file is present + // then project ids can be used. Using call once to cache the result of this check + // to avoid having to check the file presence again and again. + std::call_once(flag, []() { + auto using_project_ids = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + sUsingProjectIdsFlag = access(using_project_ids.c_str(), F_OK) == 0; + }); + // return sUsingProjectIdsFlag; + return false; +} + +static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, + long project_id) { + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } + if (internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } return 0; } +static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, gid_t gid, long project_id) { + auto path = StringPrintf("%s/%s", parent.c_str(), name); + int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + if (ret == 0 && internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } + return ret; +} + static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; @@ -597,9 +633,10 @@ static void chown_app_profile_dir(const std::string &packageName, int32_t appId, } } -static binder::Status createAppDataDirs(const std::string& path, - int32_t uid, int32_t* previousUid, int32_t cacheGid, - const std::string& seInfo, mode_t targetMode) { +static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid, + int32_t previousUid, int32_t cacheGid, + const std::string& seInfo, mode_t targetMode, + long projectIdApp, long projectIdCache) { struct stat st{}; bool parent_dir_exists = (stat(path.c_str(), &st) == 0); @@ -609,23 +646,17 @@ static binder::Status createAppDataDirs(const std::string& path, bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0); if (parent_dir_exists) { - if (*previousUid < 0) { - // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner - // of the directory as previousUid. This is required because it is not always possible - // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked) - *previousUid = st.st_uid; - } - if (*previousUid != uid) { - if (!chown_app_dir(path, uid, *previousUid, cacheGid)) { + if (previousUid > 0 && previousUid != uid) { + if (!chown_app_dir(path, uid, previousUid, cacheGid)) { return error("Failed to chown " + path); } } } // Prepare only the parent app directory - if (prepare_app_dir(path, targetMode, uid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { + if (prepare_app_dir(path, targetMode, uid, gid, projectIdApp) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, projectIdCache) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, projectIdCache)) { return error("Failed to prepare " + path); } @@ -666,12 +697,9 @@ binder::Status InstalldNativeService::createAppDataLocked( int32_t uid = multiuser_get_uid(userId, appId); - // If previousAppId < 0, we will use the existing app data owner as previousAppUid - // If previousAppId == 0, we use uid as previousUid (no data migration will happen) - // if previousAppId > 0, an app is upgrading and changing its app ID - int32_t previousUid = previousAppId > 0 - ? (int32_t) multiuser_get_uid(userId, previousAppId) - : (previousAppId == 0 ? uid : -1); + // If previousAppId > 0, an app is changing its app ID + int32_t previousUid = + previousAppId > 0 ? (int32_t)multiuser_get_uid(userId, previousAppId) : -1; int32_t cacheGid = multiuser_get_cache_gid(userId, appId); mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; @@ -681,10 +709,14 @@ binder::Status InstalldNativeService::createAppDataLocked( cacheGid = uid; } + long projectIdApp = get_project_id(uid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); - auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, + projectIdApp, projectIdCache); if (!status.isOk()) { return status; } @@ -709,11 +741,12 @@ binder::Status InstalldNativeService::createAppDataLocked( if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode); + auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, + projectIdApp, projectIdCache); if (!status.isOk()) { return status; } - if (previousUid != uid) { + if (previousUid > 0 && previousUid != uid) { chown_app_profile_dir(packageName, appId, userId); } @@ -722,38 +755,38 @@ binder::Status InstalldNativeService::createAppDataLocked( } } - // TODO(b/220095381): Due to boot time regression, we have omitted call to - // createSdkSandboxDataDirectory from here temporarily (unless it's for testing) - if (uuid_ != nullptr && strcmp(uuid_, "TEST") == 0) { - auto status = createSdkSandboxDataDirectory(uuid, packageName, userId, appId, previousAppId, - seInfo, flags); - if (!status.isOk()) { - return status; + if (flags & FLAG_STORAGE_SDK) { + // Safe to ignore status since we can retry creating this by calling reconcileSdkData + auto ignore = createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); + if (!ignore.isOk()) { + PLOG(WARNING) << "Failed to create sdk data package directory for " << packageName; } + + } else { + // Package does not need sdk storage. Remove it. + destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); } return ok(); } /** - * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<app-name> directory and other + * Responsible for creating /data/misc_{ce|de}/user/0/sdksandbox/<package-name> directory and other * app level sub directories, such as ./shared */ -binder::Status InstalldNativeService::createSdkSandboxDataDirectory( +binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, - int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) { + int32_t appId, int32_t flags) { int32_t sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); if (sdkSandboxUid == -1) { // There no valid sdk sandbox process for this app. Skip creation of data directory return ok(); } - // TODO(b/211763739): what if uuid is not nullptr or TEST? const char* uuid_ = uuid ? uuid->c_str() : nullptr; constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; - for (int i = 0; i < 2; i++) { - int currentFlag = storageFlags[i]; + for (int currentFlag : storageFlags) { if ((flags & currentFlag) == 0) { continue; } @@ -762,33 +795,16 @@ binder::Status InstalldNativeService::createSdkSandboxDataDirectory( // /data/misc_{ce,de}/<user-id>/sdksandbox directory gets created by vold // during user creation - // Prepare the app directory - auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, - packageName.c_str()); - if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) { - return error("Failed to prepare " + appPath); - } - - // Now prepare the shared directory which will be accessible by all codes - auto sharedPath = create_data_misc_sdk_sandbox_shared_path(uuid_, isCeData, userId, - packageName.c_str()); + // Prepare the package directory + auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, + packageName.c_str()); +#if SDK_DEBUG + LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath; +#endif - int32_t previousSdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId); - int32_t cacheGid = multiuser_get_cache_gid(userId, appId); - if (cacheGid == -1) { - return exception(binder::Status::EX_ILLEGAL_STATE, - StringPrintf("cacheGid cannot be -1 for sdksandbox data")); - } - auto status = createAppDataDirs(sharedPath, sdkSandboxUid, &previousSdkSandboxUid, cacheGid, - seInfo, 0700); - if (!status.isOk()) { - return status; + if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM, 0)) { + return error("Failed to prepare " + packagePath); } - - // TODO(b/211763739): We also need to handle art profile creations - - // TODO(b/211763739): And return the CE inode of the sdksandbox root directory and - // app directory under it so we can clear contents while CE storage is locked } return ok(); @@ -837,6 +853,111 @@ binder::Status InstalldNativeService::createAppDataBatched( return ok(); } +binder::Status InstalldNativeService::reconcileSdkData( + const android::os::ReconcileSdkDataArgs& args) { + // Locking is performed depeer in the callstack. + + return reconcileSdkData(args.uuid, args.packageName, args.subDirNames, args.userId, args.appId, + args.previousAppId, args.seInfo, args.flags); +} + +/** + * Reconciles per-sdk directory under app-level sdk data directory. + + * E.g. `/data/misc_ce/0/sdksandbox/<package-name>/<sdkPackageName>-<randomSuffix> + * + * - If the sdk data package directory is missing, we create it first. + * - If sdkPackageNames is empty, we delete sdk package directory since it's not needed anymore. + * - If a sdk level directory we need to prepare already exist, we skip creating it again. This + * is to avoid having same per-sdk directory with different suffix. + * - If a sdk level directory exist which is absent from sdkPackageNames, we remove it. + */ +binder::Status InstalldNativeService::reconcileSdkData(const std::optional<std::string>& uuid, + const std::string& packageName, + const std::vector<std::string>& subDirNames, + int userId, int appId, int previousAppId, + const std::string& seInfo, int flags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE_USER(); + +#if SDK_DEBUG + LOG(DEBUG) << "Creating per sdk data directory for: " << packageName; +#endif + + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + + // Prepare the sdk package directory in case it's missing + const auto status = + createSdkSandboxDataPackageDirectory(uuid, packageName, userId, appId, flags); + if (!status.isOk()) { + return status; + } + + auto res = ok(); + // We have to create sdk data for CE and DE storage + const int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + const bool isCeData = (currentFlag == FLAG_STORAGE_CE); + + const auto packagePath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, + packageName.c_str()); + + // Remove existing sub-directories not referred in subDirNames + const std::unordered_set<std::string> expectedSubDirNames(subDirNames.begin(), + subDirNames.end()); + const auto subDirHandler = [&packagePath, &expectedSubDirNames, + &res](const std::string& subDirName) { + // Remove the per-sdk directory if it is not referred in + // expectedSubDirNames + if (expectedSubDirNames.find(subDirName) == expectedSubDirNames.end()) { + auto path = packagePath + "/" + subDirName; + if (delete_dir_contents_and_dir(path) != 0) { + res = error("Failed to delete " + path); + return; + } + } + }; + const int ec = foreach_subdir(packagePath, subDirHandler); + if (ec != 0) { + res = error("Failed to process subdirs for " + packagePath); + continue; + } + + // Now create the subDirNames + for (const auto& subDirName : subDirNames) { + const std::string path = + create_data_misc_sdk_sandbox_sdk_path(uuid_, isCeData, userId, + packageName.c_str(), subDirName.c_str()); + + // Create the directory along with cache and code_cache + const int32_t cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid == -1) { + return exception(binder::Status::EX_ILLEGAL_STATE, + StringPrintf("cacheGid cannot be -1 for sdk data")); + } + const int32_t sandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + int32_t previousSandboxUid = multiuser_get_sdk_sandbox_uid(userId, previousAppId); + int32_t appUid = multiuser_get_uid(userId, appId); + long projectIdApp = get_project_id(appUid, PROJECT_ID_APP_START); + long projectIdCache = get_project_id(appUid, PROJECT_ID_APP_CACHE_START); + auto status = + createAppDataDirs(path, sandboxUid, AID_NOBODY, previousSandboxUid, cacheGid, + seInfo, 0700 | S_ISGID, projectIdApp, projectIdCache); + if (!status.isOk()) { + res = status; + continue; + } + } + } + + return res; +} + binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); @@ -982,6 +1103,47 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri } } } + auto status = clearSdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); + if (!status.isOk()) { + res = status; + } + return res; +} + +binder::Status InstalldNativeService::clearSdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags) { + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgname = packageName.c_str(); + + binder::Status res = ok(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int i = 0; i < 2; i++) { + int currentFlag = storageFlags[i]; + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + std::string suffix; + if (flags & FLAG_CLEAR_CACHE_ONLY) { + suffix = CACHE_DIR_POSTFIX; + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + suffix = CODE_CACHE_DIR_POSTFIX; + } + + auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname); + if (access(appPath.c_str(), F_OK) != 0) continue; + const auto subDirHandler = [&appPath, &res, &suffix](const std::string& filename) { + auto filepath = appPath + "/" + filename + suffix; + if (delete_dir_contents(filepath, true) != 0) { + res = error("Failed to clear contents of " + filepath); + } + }; + const int ec = foreach_subdir(appPath, subDirHandler); + if (ec != 0) { + res = error("Failed to process subdirs for " + appPath); + } + } return res; } @@ -1078,6 +1240,32 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st } } } + auto status = destroySdkSandboxDataPackageDirectory(uuid, packageName, userId, flags); + if (!status.isOk()) { + res = status; + } + return res; +} + +binder::Status InstalldNativeService::destroySdkSandboxDataPackageDirectory( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags) { + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgname = packageName.c_str(); + + binder::Status res = ok(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int i = 0; i < 2; i++) { + int currentFlag = storageFlags[i]; + if ((flags & currentFlag) == 0) { + continue; + } + bool isCeData = (currentFlag == FLAG_STORAGE_CE); + auto appPath = create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgname); + if (rename_delete_dir_contents_and_dir(appPath) != 0) { + res = error("Failed to delete " + appPath); + } + } return res; } @@ -1563,6 +1751,36 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } + // Copy sdk data for all known users + for (auto userId : users) { + LOCK_USER(); + + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + const bool isCeData = currentFlag == FLAG_STORAGE_CE; + + const auto from = create_data_misc_sdk_sandbox_package_path(from_uuid, isCeData, userId, + package_name); + if (access(from.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from; + continue; + } + const auto to = create_data_misc_sdk_sandbox_path(to_uuid, isCeData, userId); + + const int rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + goto fail; + } + } + + if (!restoreconSdkDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, seInfo) + .isOk()) { + res = error("Failed to restorecon"); + goto fail; + } + } // We let the framework scan the new location and persist that before // deleting the data in the old location; this ordering ensures that // we can recover from things like battery pulls. @@ -1590,6 +1808,18 @@ fail: } } } + for (auto userId : users) { + LOCK_USER(); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + const bool isCeData = currentFlag == FLAG_STORAGE_CE; + const auto to = create_data_misc_sdk_sandbox_package_path(to_uuid, isCeData, userId, + package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to rollback " << to; + } + } + } return res; } @@ -1621,8 +1851,14 @@ 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); + if (delete_dir_contents_and_dir(sdk_sandbox_de_path, true) != 0) { + res = error("Failed to delete " + sdk_sandbox_de_path); } if (uuid_ == nullptr) { path = create_data_misc_legacy_path(userId); @@ -1637,12 +1873,19 @@ 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); + if (delete_dir_contents_and_dir(sdk_sandbox_ce_path, true) != 0) { + 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; @@ -1879,69 +2122,78 @@ static std::string toString(std::vector<int64_t> values) { return res.str(); } #endif +// On devices without sdcardfs, if internal and external are on +// the same volume, a uid such as u0_a123 is used for both +// internal and external storage; therefore, subtract that +// amount from internal to make sure we don't count it double. +// This needs to happen for data, cache and OBB +static void deductDoubleSpaceIfNeeded(stats* stats, int64_t doubleSpaceToBeDeleted, uid_t uid, + const std::string& uuid) { + if (!supports_sdcardfs()) { + stats->dataSize -= doubleSpaceToBeDeleted; + long obbProjectId = get_project_id(uid, PROJECT_ID_EXT_OBB_START); + int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); + stats->dataSize -= appObbSize; + } +} static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { - int64_t space; + int64_t space, doubleSpaceToBeDeleted = 0; uid_t uid = multiuser_get_uid(userId, appId); - if (stats != nullptr) { - if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { - stats->dataSize += space; - } - - int cacheGid = multiuser_get_cache_gid(userId, appId); - if (cacheGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { - stats->cacheSize += space; - } - } - - int sharedGid = multiuser_get_shared_gid(0, appId); - if (sharedGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { - stats->codeSize += space; - } - } - } + static const bool supportsProjectId = internal_storage_has_project_id(); if (extStats != nullptr) { - static const bool supportsSdCardFs = supports_sdcardfs(); space = get_occupied_app_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for - // application dirs on both internal and external storage; - // therefore, substract that amount from internal to make sure - // we don't count it double. - stats->dataSize -= space; - } + doubleSpaceToBeDeleted += space; } space = get_occupied_app_cache_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; // cache counts for "data" extStats->cacheSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for both - // internal and external storage; therefore, substract that - // amount from internal to make sure we don't count it double. - stats->dataSize -= space; + doubleSpaceToBeDeleted += space; + } + } + + if (stats != nullptr) { + if (!supportsProjectId) { + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + stats->dataSize += space; + } + deductDoubleSpaceIfNeeded(stats, doubleSpaceToBeDeleted, uid, uuid); + int sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId); + if (sdkSandboxUid != -1) { + if ((space = GetOccupiedSpaceForUid(uuid, sdkSandboxUid)) != -1) { + stats->dataSize += space; + } + } + int cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { + stats->cacheSize += space; + } + } + } else { + long projectId = get_project_id(uid, PROJECT_ID_APP_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->dataSize += space; + } + projectId = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->cacheSize += space; + stats->dataSize += space; } } - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, the UID of OBBs on external storage - // matches the regular app UID (eg u0_a123); therefore, to avoid - // OBBs being include in stats->dataSize, compute the OBB size for - // this app, and substract it from the size reported on internal - // storage - long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; - int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); - stats->dataSize -= appObbSize; + int sharedGid = multiuser_get_shared_gid(0, appId); + if (sharedGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { + stats->codeSize += space; + } } } } @@ -1996,8 +2248,17 @@ static void collectManualStats(const std::string& path, struct stats* stats) { closedir(d); } +void collectManualStatsForSubDirectories(const std::string& path, struct stats* stats) { + const auto subDirHandler = [&path, &stats](const std::string& subDir) { + auto fullpath = path + "/" + subDir; + collectManualStats(fullpath, stats); + }; + foreach_subdir(path, subDirHandler); +} + static void collectManualStatsForUser(const std::string& path, struct stats* stats, - bool exclude_apps = false) { + bool exclude_apps = false, + bool is_sdk_sandbox_storage = false) { DIR *d; int dfd; struct dirent *de; @@ -2022,6 +2283,11 @@ static void collectManualStatsForUser(const std::string& path, struct stats* sta continue; } else if (exclude_apps && (user_uid >= AID_APP_START && user_uid <= AID_APP_END)) { continue; + } else if (is_sdk_sandbox_storage) { + // 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.) + collectManualStatsForSubDirectories(StringPrintf("%s/%s", path.c_str(), name), + stats); } else { collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats); } @@ -2064,6 +2330,11 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st fts_close(fts); } static bool ownsExternalStorage(int32_t appId) { + // if project id calculation is supported then, there is no need to + // calculate in a different way and project_id based calculation can work + if (internal_storage_has_project_id()) { + return false; + } // Fetch external storage owner appid and check if it is the same as the // current appId whose size is calculated struct stat s; @@ -2164,6 +2435,19 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string collectManualStats(dePath, &stats); ATRACE_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"); + 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(); + } + if (!uuid) { ATRACE_BEGIN("profiles"); calculate_tree_size( @@ -2400,6 +2684,13 @@ binder::Status InstalldNativeService::getUserSize(const std::optional<std::strin collectManualStatsForUser(dePath, &stats); ATRACE_END(); + ATRACE_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(); + if (!uuid) { ATRACE_BEGIN("profile"); auto userProfilePath = create_primary_cur_profile_dir_path(userId); @@ -2539,6 +2830,9 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s auto obbPath = StringPrintf("%s/Android/obb", create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); + if (!(flags & FLAG_USE_QUOTA)) { + totalSize -= obbSize; + } ATRACE_END(); } @@ -2921,6 +3215,49 @@ binder::Status InstalldNativeService::restoreconAppDataLocked( return res; } +binder::Status InstalldNativeService::restoreconSdkDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + + binder::Status res = ok(); + + // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. + unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; + const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* pkgName = packageName.c_str(); + const char* seinfo = seInfo.c_str(); + + uid_t uid = multiuser_get_sdk_sandbox_uid(userId, appId); + constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE}; + for (int currentFlag : storageFlags) { + if ((flags & currentFlag) == 0) { + continue; + } + const bool isCeData = (currentFlag == FLAG_STORAGE_CE); + const auto packagePath = + create_data_misc_sdk_sandbox_package_path(uuid_, isCeData, userId, pkgName); + if (access(packagePath.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << packagePath; + continue; + } + const auto subDirHandler = [&packagePath, &seinfo, &uid, &seflags, + &res](const std::string& subDir) { + const auto& fullpath = packagePath + "/" + subDir; + if (selinux_android_restorecon_pkgdir(fullpath.c_str(), seinfo, uid, seflags) < 0) { + res = error("restorecon failed for " + fullpath); + } + }; + const auto ec = foreach_subdir(packagePath, subDirHandler); + if (ec != 0) { + res = error("Failed to restorecon for subdirs of " + packagePath); + } + } + return res; +} + binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); @@ -3055,6 +3392,33 @@ binder::Status InstalldNativeService::hashSecondaryDexFile( return result ? ok() : error(); } +bool check_if_ioctl_feature_is_supported() { + bool result = false; + auto temp_path = StringPrintf("%smisc/installd/ioctl_check", android_data_dir.c_str()); + if (access(temp_path.c_str(), F_OK) != 0) { + open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + result = set_quota_project_id(temp_path, 0, false) == 0; + // delete the temp file + // remove the external file + remove(temp_path.c_str()); + } + return result; +} + +binder::Status InstalldNativeService::setFirstBoot() { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string uuid; + if (GetOccupiedSpaceForProjectId(uuid, 0) != -1 && check_if_ioctl_feature_is_supported()) { + auto first_boot_path = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + if (access(first_boot_path.c_str(), F_OK) != 0) { + open(first_boot_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + } + } + return ok(); +} + binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard<std::recursive_mutex> lock(mMountsLock); @@ -3141,10 +3505,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(); } @@ -3258,11 +3622,17 @@ binder::Status InstalldNativeService::cleanupInvalidPackageDirs( if (flags & FLAG_STORAGE_CE) { auto ce_path = create_data_user_ce_path(uuid_cstr, userId); cleanup_invalid_package_dirs_under_path(ce_path); + auto sdksandbox_ce_path = + create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/true, userId); + cleanup_invalid_package_dirs_under_path(sdksandbox_ce_path); } if (flags & FLAG_STORAGE_DE) { auto de_path = create_data_user_de_path(uuid_cstr, userId); cleanup_invalid_package_dirs_under_path(de_path); + auto sdksandbox_de_path = + create_data_misc_sdk_sandbox_path(uuid_cstr, /*isCeData=*/false, userId); + cleanup_invalid_package_dirs_under_path(sdksandbox_de_path); } return ok(); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index b2bad1d386..95ac51692d 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -58,12 +58,12 @@ public: const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return); + binder::Status reconcileSdkData(const android::os::ReconcileSdkDataArgs& args); + binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); - binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, - int32_t flags, int32_t appId, const std::string& seInfo); + binder::Status migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags); binder::Status clearAppData(const std::optional<std::string>& uuid, @@ -167,6 +167,7 @@ public: int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); + binder::Status setFirstBoot(); binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid, bool* _aidl_return); binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid); @@ -203,11 +204,28 @@ private: int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); - binder::Status createSdkSandboxDataDirectory(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, - int32_t appId, int32_t previousAppId, - const std::string& seInfo, int32_t flags); + binder::Status createSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t appId, + int32_t flags); + binder::Status clearSdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status destroySdkSandboxDataPackageDirectory(const std::optional<std::string>& uuid, + const std::string& packageName, + int32_t userId, int32_t flags); + binder::Status reconcileSdkData(const std::optional<std::string>& uuid, + const std::string& packageName, + const std::vector<std::string>& subDirNames, int32_t userId, + int32_t appId, int32_t previousAppId, const std::string& seInfo, + int flags); + binder::Status restoreconSdkDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); }; } // namespace installd diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 3f0fb6d2ba..fc4cfc98dc 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -30,6 +30,14 @@ }, { "name": "CtsCompilationTestCases" + }, + { + "name": "SdkSandboxStorageHostTest", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + } + ] } ] } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index f4fd9a94de..c17c6bfce3 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -20,10 +20,12 @@ package android.os; interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); - + void setFirstBoot(); android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); + void reconcileSdkData(in android.os.ReconcileSdkDataArgs args); + void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, int appId, @utf8InCpp String seInfo); void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @@ -131,6 +133,7 @@ interface IInstalld { const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; + const int FLAG_STORAGE_SDK = 0x8; const int FLAG_CLEAR_CACHE_ONLY = 0x10; const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; diff --git a/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl new file mode 100644 index 0000000000..583a36d580 --- /dev/null +++ b/cmds/installd/binder/android/os/ReconcileSdkDataArgs.aidl @@ -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. + */ + +package android.os; + +/** {@hide} */ +parcelable ReconcileSdkDataArgs { + @nullable @utf8InCpp String uuid; + @utf8InCpp String packageName; + @utf8InCpp List<String> subDirNames; + int userId; + int appId; + int previousAppId; + @utf8InCpp String seInfo; + int flags; +} diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index e390babb57..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", @@ -26,6 +25,7 @@ cc_test { "libasync_safe", "libdiskusage", "libext2_uuid", + "libgmock", "libinstalld", "liblog", ], @@ -35,7 +35,6 @@ cc_test { cc_test { name: "installd_cache_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_cache_test.cpp"], cflags: [ "-Wall", @@ -81,7 +80,6 @@ cc_test { cc_test { name: "installd_service_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_service_test.cpp"], cflags: [ "-Wall", @@ -106,6 +104,7 @@ cc_test { "libziparchive", "liblog", "liblogwrap", + "libc++fs", ], test_config: "installd_service_test.xml", @@ -128,7 +127,6 @@ cc_test { cc_test { name: "installd_dexopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_dexopt_test.cpp"], cflags: [ "-Wall", @@ -175,7 +173,6 @@ cc_test { cc_test { name: "installd_otapreopt_test", test_suites: ["device-tests"], - clang: true, srcs: ["installd_otapreopt_test.cpp"], cflags: [ "-Wall", @@ -196,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 aeba451a48..800e141d92 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -879,6 +879,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([=]() { diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 21ab5b87b1..effb4013f1 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -32,16 +32,20 @@ #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> +#include <filesystem> +#include <fstream> #include <android/content/pm/IPackageManagerNative.h> #include <binder/IServiceManager.h> #include "InstalldNativeService.h" +#include "binder/Status.h" #include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" #include "utils.h" using android::base::StringPrintf; +using std::filesystem::is_empty; namespace android { std::string get_package_name(uid_t uid) { @@ -75,12 +79,18 @@ std::string get_package_name(uid_t uid) { namespace installd { static constexpr const char* kTestUuid = "TEST"; -static constexpr const char* kTestPath = "/data/local/tmp/user/0"; +static const std::string kTestPath = "/data/local/tmp"; +static constexpr const uid_t kNobodyUid = 9999; static constexpr const uid_t kSystemUid = 1000; static constexpr const int32_t kTestUserId = 0; static constexpr const uid_t kTestAppId = 19999; +static constexpr const int FLAG_STORAGE_SDK = InstalldNativeService::FLAG_STORAGE_SDK; +static constexpr const int FLAG_CLEAR_CACHE_ONLY = InstalldNativeService::FLAG_CLEAR_CACHE_ONLY; +static constexpr const int FLAG_CLEAR_CODE_CACHE_ONLY = + InstalldNativeService::FLAG_CLEAR_CODE_CACHE_ONLY; const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId); +const gid_t kTestCacheGid = multiuser_get_cache_gid(kTestUserId, kTestAppId); const uid_t kTestSdkSandboxUid = multiuser_get_sdk_sandbox_uid(kTestUserId, kTestAppId); #define FLAG_FORCE InstalldNativeService::FLAG_FORCE @@ -103,18 +113,18 @@ bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *ins return create_cache_path_default(path, src, instruction_set); } -static std::string get_full_path(const char* path) { - return StringPrintf("%s/%s", kTestPath, path); +static std::string get_full_path(const std::string& path) { + return StringPrintf("%s/%s", kTestPath.c_str(), path.c_str()); } -static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { +static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { const std::string fullPath = get_full_path(path); EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0); EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0); EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0); } -static int create(const char* path, uid_t owner, gid_t group, mode_t mode) { +static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode) { int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode); EXPECT_NE(fd, -1); EXPECT_EQ(::fchown(fd, owner, group), 0); @@ -122,8 +132,8 @@ static int create(const char* path, uid_t owner, gid_t group, mode_t mode) { return fd; } -static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { - EXPECT_EQ(::close(create(path, owner, group, mode)), 0); +static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) { + EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0); } static int stat_gid(const char* path) { @@ -138,7 +148,7 @@ static int stat_mode(const char* path) { return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } -static bool exists(const char* path) { +static bool exists(const std::string& path) { return ::access(get_full_path(path).c_str(), F_OK) == 0; } @@ -161,8 +171,8 @@ static bool find_file(const char* path, Pred&& pred) { return result; } -static bool exists_renamed_deleted_dir() { - return find_file(kTestPath, [](const std::string& name, bool is_dir) { +static bool exists_renamed_deleted_dir(const std::string& rootDirectory) { + return find_file((kTestPath + rootDirectory).c_str(), [](const std::string& name, bool is_dir) { return is_dir && is_renamed_deleted_dir(name); }); } @@ -179,197 +189,205 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; system("rm -rf /data/local/tmp/user"); + system("rm -rf /data/local/tmp/misc_ce"); + system("rm -rf /data/local/tmp/misc_de"); system("mkdir -p /data/local/tmp/user/0"); - + system("mkdir -p /data/local/tmp/misc_ce/0/sdksandbox"); + system("mkdir -p /data/local/tmp/misc_de/0/sdksandbox"); init_globals_from_data_and_root(); } virtual void TearDown() { delete service; system("rm -rf /data/local/tmp/user"); + system("rm -rf /data/local/tmp/misc_ce"); + system("rm -rf /data/local/tmp/misc_de"); } }; TEST_F(ServiceTest, FixupAppData_Upgrade) { LOG(INFO) << "FixupAppData_Upgrade"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/normal", 10000, 10000, 0700); - mkdir("com.example/cache", 10000, 10000, 0700); - touch("com.example/cache/file", 10000, 10000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/normal", 10000, 10000, 0700); + mkdir("user/0/com.example/cache", 10000, 10000, 0700); + touch("user/0/com.example/cache/file", 10000, 10000, 0700); service->fixupAppData(testUuid, 0); - EXPECT_EQ(10000, stat_gid("com.example/normal")); - EXPECT_EQ(20000, stat_gid("com.example/cache")); - EXPECT_EQ(20000, stat_gid("com.example/cache/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/normal")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/cache")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/cache/file")); - EXPECT_EQ(0700, stat_mode("com.example/normal")); - EXPECT_EQ(02771, stat_mode("com.example/cache")); - EXPECT_EQ(0700, stat_mode("com.example/cache/file")); + EXPECT_EQ(0700, stat_mode("user/0/com.example/normal")); + EXPECT_EQ(02771, stat_mode("user/0/com.example/cache")); + EXPECT_EQ(0700, stat_mode("user/0/com.example/cache/file")); } TEST_F(ServiceTest, FixupAppData_Moved) { LOG(INFO) << "FixupAppData_Moved"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); - mkdir("com.example/bar", 10000, 20000, 0700); - touch("com.example/bar/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); service->fixupAppData(testUuid, 0); - EXPECT_EQ(10000, stat_gid("com.example/foo")); - EXPECT_EQ(20000, stat_gid("com.example/foo/file")); - EXPECT_EQ(10000, stat_gid("com.example/bar")); - EXPECT_EQ(10000, stat_gid("com.example/bar/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo")); + EXPECT_EQ(20000, stat_gid("user/0/com.example/foo/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file")); service->fixupAppData(testUuid, FLAG_FORCE); - EXPECT_EQ(10000, stat_gid("com.example/foo")); - EXPECT_EQ(10000, stat_gid("com.example/foo/file")); - EXPECT_EQ(10000, stat_gid("com.example/bar")); - EXPECT_EQ(10000, stat_gid("com.example/bar/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/foo/file")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar")); + EXPECT_EQ(10000, stat_gid("user/0/com.example/bar/file")); } TEST_F(ServiceTest, DestroyUserData) { LOG(INFO) << "DestroyUserData"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); - mkdir("com.example/bar", 10000, 20000, 0700); - touch("com.example/bar/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); - EXPECT_TRUE(exists("com.example/foo")); - EXPECT_TRUE(exists("com.example/foo/file")); - EXPECT_TRUE(exists("com.example/bar")); - EXPECT_TRUE(exists("com.example/bar/file")); + EXPECT_TRUE(exists("user/0/com.example/foo")); + EXPECT_TRUE(exists("user/0/com.example/foo/file")); + EXPECT_TRUE(exists("user/0/com.example/bar")); + EXPECT_TRUE(exists("user/0/com.example/bar/file")); service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE); - EXPECT_FALSE(exists("com.example/foo")); - EXPECT_FALSE(exists("com.example/foo/file")); - EXPECT_FALSE(exists("com.example/bar")); - EXPECT_FALSE(exists("com.example/bar/file")); + EXPECT_FALSE(exists("user/0/com.example/foo")); + EXPECT_FALSE(exists("user/0/com.example/foo/file")); + EXPECT_FALSE(exists("user/0/com.example/bar")); + EXPECT_FALSE(exists("user/0/com.example/bar/file")); - EXPECT_FALSE(exists_renamed_deleted_dir()); + EXPECT_FALSE(exists_renamed_deleted_dir("/user/0")); } TEST_F(ServiceTest, DestroyAppData) { LOG(INFO) << "DestroyAppData"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); - mkdir("com.example/bar", 10000, 20000, 0700); - touch("com.example/bar/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example/bar", 10000, 20000, 0700); + touch("user/0/com.example/bar/file", 10000, 20000, 0700); - EXPECT_TRUE(exists("com.example/foo")); - EXPECT_TRUE(exists("com.example/foo/file")); - EXPECT_TRUE(exists("com.example/bar")); - EXPECT_TRUE(exists("com.example/bar/file")); + EXPECT_TRUE(exists("user/0/com.example/foo")); + EXPECT_TRUE(exists("user/0/com.example/foo/file")); + EXPECT_TRUE(exists("user/0/com.example/bar")); + EXPECT_TRUE(exists("user/0/com.example/bar/file")); service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0); - EXPECT_FALSE(exists("com.example/foo")); - EXPECT_FALSE(exists("com.example/foo/file")); - EXPECT_FALSE(exists("com.example/bar")); - EXPECT_FALSE(exists("com.example/bar/file")); + EXPECT_FALSE(exists("user/0/com.example/foo")); + EXPECT_FALSE(exists("user/0/com.example/foo/file")); + EXPECT_FALSE(exists("user/0/com.example/bar")); + EXPECT_FALSE(exists("user/0/com.example/bar/file")); - EXPECT_FALSE(exists_renamed_deleted_dir()); + EXPECT_FALSE(exists_renamed_deleted_dir("/user/0")); } TEST_F(ServiceTest, CleanupInvalidPackageDirs) { LOG(INFO) << "CleanupInvalidPackageDirs"; - mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700); - mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700); - touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700); - mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700); - touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700); - - auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700); - - mkdir("b14b6458a44NOTdeleted", 10000, 10000, 0700); - mkdir("b14b6458a44NOTdeleted/foo", 10000, 10000, 0700); - touch("b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700); - mkdir("b14b6458a44NOTdeleted/bar", 10000, 20000, 0700); - touch("b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700); - - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); - mkdir("com.example/bar", 10000, 20000, 0700); - touch("com.example/bar/file", 10000, 20000, 0700); - - mkdir("==deleted==", 10000, 10000, 0700); - mkdir("==deleted==/foo", 10000, 10000, 0700); - touch("==deleted==/foo/file", 10000, 20000, 0700); - mkdir("==deleted==/bar", 10000, 20000, 0700); - touch("==deleted==/bar/file", 10000, 20000, 0700); - - EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo")); - EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file")); - EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar")); - EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file")); - EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file")); - - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file")); - - EXPECT_TRUE(exists("com.example/foo")); - EXPECT_TRUE(exists("com.example/foo/file")); - EXPECT_TRUE(exists("com.example/bar")); - EXPECT_TRUE(exists("com.example/bar/file")); - - EXPECT_TRUE(exists("==deleted==/foo")); - EXPECT_TRUE(exists("==deleted==/foo/file")); - EXPECT_TRUE(exists("==deleted==/bar")); - EXPECT_TRUE(exists("==deleted==/bar/file")); - - EXPECT_TRUE(exists_renamed_deleted_dir()); - - service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE); - - EXPECT_EQ(::close(fd), 0); - - EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo")); - EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file")); - EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar")); - EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file")); - EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file")); - - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/foo/file")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar")); - EXPECT_TRUE(exists("b14b6458a44NOTdeleted/bar/file")); - - EXPECT_TRUE(exists("com.example/foo")); - EXPECT_TRUE(exists("com.example/foo/file")); - EXPECT_TRUE(exists("com.example/bar")); - EXPECT_TRUE(exists("com.example/bar/file")); - - EXPECT_FALSE(exists("==deleted==/foo")); - EXPECT_FALSE(exists("==deleted==/foo/file")); - EXPECT_FALSE(exists("==deleted==/bar")); - EXPECT_FALSE(exists("==deleted==/bar/file")); - - EXPECT_FALSE(exists_renamed_deleted_dir()); + std::string rootDirectoryPrefix[] = {"user/0", "misc_ce/0/sdksandbox", "misc_de/0/sdksandbox"}; + for (auto& prefix : rootDirectoryPrefix) { + mkdir(prefix + "/5b14b6458a44==deleted==", 10000, 10000, 0700); + mkdir(prefix + "/5b14b6458a44==deleted==/foo", 10000, 10000, 0700); + touch(prefix + "/5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/5b14b6458a44==deleted==/bar", 10000, 20000, 0700); + touch(prefix + "/5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700); + + auto fd = create(prefix + "/5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700); + + mkdir(prefix + "/b14b6458a44NOTdeleted", 10000, 10000, 0700); + mkdir(prefix + "/b14b6458a44NOTdeleted/foo", 10000, 10000, 0700); + touch(prefix + "/b14b6458a44NOTdeleted/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/b14b6458a44NOTdeleted/bar", 10000, 20000, 0700); + touch(prefix + "/b14b6458a44NOTdeleted/bar/file", 10000, 20000, 0700); + + mkdir(prefix + "/com.example", 10000, 10000, 0700); + mkdir(prefix + "/com.example/foo", 10000, 10000, 0700); + touch(prefix + "/com.example/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/com.example/bar", 10000, 20000, 0700); + touch(prefix + "/com.example/bar/file", 10000, 20000, 0700); + + mkdir(prefix + "/==deleted==", 10000, 10000, 0700); + mkdir(prefix + "/==deleted==/foo", 10000, 10000, 0700); + touch(prefix + "/==deleted==/foo/file", 10000, 20000, 0700); + mkdir(prefix + "/==deleted==/bar", 10000, 20000, 0700); + touch(prefix + "/==deleted==/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/foo/file")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/file")); + EXPECT_TRUE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file")); + + EXPECT_TRUE(exists(prefix + "/com.example/foo")); + EXPECT_TRUE(exists(prefix + "/com.example/foo/file")); + EXPECT_TRUE(exists(prefix + "/com.example/bar")); + EXPECT_TRUE(exists(prefix + "/com.example/bar/file")); + + EXPECT_TRUE(exists(prefix + "/==deleted==/foo")); + EXPECT_TRUE(exists(prefix + "/==deleted==/foo/file")); + EXPECT_TRUE(exists(prefix + "/==deleted==/bar")); + EXPECT_TRUE(exists(prefix + "/==deleted==/bar/file")); + + EXPECT_TRUE(exists_renamed_deleted_dir("/" + prefix)); + + service->cleanupInvalidPackageDirs(testUuid, 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE); + + EXPECT_EQ(::close(fd), 0); + + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/foo/file")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/file")); + EXPECT_FALSE(exists(prefix + "/5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/foo/file")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar")); + EXPECT_TRUE(exists(prefix + "/b14b6458a44NOTdeleted/bar/file")); + + EXPECT_TRUE(exists(prefix + "/com.example/foo")); + EXPECT_TRUE(exists(prefix + "/com.example/foo/file")); + EXPECT_TRUE(exists(prefix + "/com.example/bar")); + EXPECT_TRUE(exists(prefix + "/com.example/bar/file")); + + EXPECT_FALSE(exists(prefix + "/==deleted==/foo")); + EXPECT_FALSE(exists(prefix + "/==deleted==/foo/file")); + EXPECT_FALSE(exists(prefix + "/==deleted==/bar")); + EXPECT_FALSE(exists(prefix + "/==deleted==/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir(prefix)); + } } TEST_F(ServiceTest, HashSecondaryDex) { LOG(INFO) << "HashSecondaryDex"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -389,7 +407,7 @@ TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { LOG(INFO) << "HashSecondaryDex_NoSuch"; std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -399,12 +417,12 @@ TEST_F(ServiceTest, HashSecondaryDex_NoSuch) { TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { LOG(INFO) << "HashSecondaryDex_Unreadable"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0300); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0300); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); @@ -414,12 +432,12 @@ TEST_F(ServiceTest, HashSecondaryDex_Unreadable) { TEST_F(ServiceTest, HashSecondaryDex_WrongApp) { LOG(INFO) << "HashSecondaryDex_WrongApp"; - mkdir("com.example", 10000, 10000, 0700); - mkdir("com.example/foo", 10000, 10000, 0700); - touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("user/0/com.example", 10000, 10000, 0700); + mkdir("user/0/com.example/foo", 10000, 10000, 0700); + touch("user/0/com.example/foo/file", 10000, 20000, 0700); std::vector<uint8_t> result; - std::string dexPath = get_full_path("com.example/foo/file"); + std::string dexPath = get_full_path("user/0/com.example/foo/file"); EXPECT_BINDER_FAIL(service->hashSecondaryDexFile( dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result)); } @@ -447,7 +465,7 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } -TEST_F(ServiceTest, GetAppSize) { +TEST_F(ServiceTest, GetAppSizeManualForMedia) { struct stat s; std::string externalPicDir = @@ -491,6 +509,100 @@ TEST_F(ServiceTest, GetAppSize) { system(removeCommand.c_str()); } } +// TEST_F(ServiceTest, GetAppSizeProjectID_UID) { +// struct stat s; +// std::string externalPicDir = +// StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); +// if (stat(externalPicDir.c_str(), &s) == 0) { +// // fetch the appId from the uid of the external storage owning app +// int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid); +// // Fetch Package Name for the external storage owning app uid +// std::string pkg = get_package_name(s.st_uid); +// +// std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingCacheFile; +// std::vector<int64_t> ceDataInodes; +// +// std::vector<std::string> codePaths; +// std::vector<std::string> packageNames; +// // set up parameters +// packageNames.push_back(pkg); +// ceDataInodes.push_back(0); +// // initialise the mounts +// service->invalidateMounts(); +// auto using_project_ids = +// StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); +// bool usingProjectIds = access(using_project_ids.c_str(), F_OK) == 0; +// if (!usingProjectIds) { +// service->setFirstBoot(); +// } +// +// if (access(using_project_ids.c_str(), F_OK) != 0) { +// // projectids is not used, so check that ioctl features should be absent +// auto temp_path = StringPrintf("%smisc/installd/ioctl_check", +// android_data_dir.c_str()); +// +// if (access(temp_path.c_str(), F_OK) != 0) { +// open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); +// bool result = set_quota_project_id(temp_path, 0, false) == 0; +// // delete the temp file +// // remove the external file +// remove(temp_path.c_str()); +// // since using_project_ids file is not present, so ioctl settings should be +// absent +// // that is denoted by the result of setting project id flag as false +// ASSERT_FALSE(result); +// } +// } +// // call the getAppSize to get the current size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize); +// // add a file with 20MB size to the external storage +// std::string externalStorageCacheDir = +// StringPrintf("%s/%s/cache", create_data_user_ce_path(nullptr, 0).c_str(), +// pkg.c_str()); +// std::string cacheFileLocation = +// StringPrintf("%s/%s", externalStorageCacheDir.c_str(), "External.jpg"); +// std::string externalFileContentCommand = +// StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", cacheFileLocation.c_str()); +// system(externalFileContentCommand.c_str()); +// // call the getAppSize again to get the new size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, +// &externalStorageSizeAfterAddingCacheFile); +// // check that the size of cache and data increases when cache file is added +// int64_t sizeDiffData = externalStorageSizeAfterAddingCacheFile[1] - +// externalStorageSize[1]; int64_t sizeDiffCache = +// externalStorageSizeAfterAddingCacheFile[2] - externalStorageSize[2]; +// ASSERT_TRUE(sizeDiffData == sizeDiffCache); +// // remove the external file +// std::string removeCommand = StringPrintf("rm -f %s", cacheFileLocation.c_str()); +// system(removeCommand.c_str()); +// // remove the setFirstBoot setting +// std::string removeCommand2 = "rm -f /data/misc/installd/using_project_ids"; +// system(removeCommand2.c_str()); +// // Do now without project id +// std::vector<int64_t> sizeWithUID, sizeWithUIDAfterAddingCacheFile; +// // call the getAppSize to get the current size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, &sizeWithUID); +// // add a file with 20MB size to the external storage +// system(externalFileContentCommand.c_str()); +// // call the getAppSize again to get the new size of the external storage owning app +// service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, +// externalStorageAppId, ceDataInodes, codePaths, +// &sizeWithUIDAfterAddingCacheFile); +// // check that the size of cache and data increases when cache file is added +// sizeDiffData = sizeWithUIDAfterAddingCacheFile[1] - sizeWithUID[1]; +// sizeDiffCache = sizeWithUIDAfterAddingCacheFile[2] - sizeWithUID[2]; +// ASSERT_TRUE(sizeDiffData == sizeDiffCache); +// // remove the external file +// system(removeCommand.c_str()); +// // reset the using_project_id if it was initially set +// if (usingProjectIds) { +// service->setFirstBoot(); +// } +// } +// } TEST_F(ServiceTest, GetAppSizeWrongSizes) { int32_t externalStorageAppId = -1; std::vector<int64_t> externalStorageSize; @@ -951,26 +1063,42 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { class SdkSandboxDataTest : public testing::Test { public: - void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) { + void CheckFileAccess(const std::string& path, uid_t uid, gid_t gid, mode_t mode) { const auto fullPath = "/data/local/tmp/" + path; ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath; struct stat st; ASSERT_EQ(0, stat(fullPath.c_str(), &st)); ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath; - ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath; + ASSERT_EQ(gid, st.st_gid) << "For path: " << fullPath; ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath; } bool exists(const char* path) { return ::access(path, F_OK) == 0; } // Creates a default CreateAppDataArgs object - android::os::CreateAppDataArgs createAppDataArgs() { + android::os::CreateAppDataArgs createAppDataArgs(const std::string& packageName) { android::os::CreateAppDataArgs args; args.uuid = kTestUuid; - args.packageName = "com.foo"; + args.packageName = packageName; args.userId = kTestUserId; args.appId = kTestAppId; args.seInfo = "default"; + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE | FLAG_STORAGE_SDK; + return args; + } + + android::os::ReconcileSdkDataArgs reconcileSdkDataArgs( + const std::string& packageName, const std::vector<std::string>& subDirNames) { + android::os::ReconcileSdkDataArgs args; + args.uuid = kTestUuid; + args.packageName = packageName; + for (const auto& subDirName : subDirNames) { + args.subDirNames.push_back(subDirName); + } + args.userId = kTestUserId; + args.appId = kTestAppId; + args.previousAppId = -1; + args.seInfo = "default"; args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; return args; } @@ -999,58 +1127,72 @@ protected: private: void clearAppData() { + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user", true)); ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true)); ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true)); ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true)); - ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true)); } }; -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + + const std::string fooCePath = "misc_ce/0/sdksandbox/com.foo"; + CheckFileAccess(fooCePath, kSystemUid, kSystemUid, S_IFDIR | 0751); + + const std::string fooDePath = "misc_de/0/sdksandbox/com.foo"; + CheckFileAccess(fooDePath, kSystemUid, kSystemUid, S_IFDIR | 0751); +} + +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); - CheckFileAccess("misc_ce/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} - CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared", kTestSdkSandboxUid, S_IFDIR | 0700); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); - CheckFileAccess("misc_de/0/sdksandbox/com.foo/shared/code_cache", kTestSdkSandboxUid, - S_IFDIR | S_ISGID | 0771); +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutSdkFlagDeletesExisting) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); + + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutDeFlag) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; - args.flags = FLAG_STORAGE_CE; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_SDK; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); // Only CE paths should exist - CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); + CheckFileAccess("misc_ce/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751); // DE paths should not exist ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); } -TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) { +TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSdkPackageData_WithoutCeFlag) { android::os::CreateAppDataResult result; - android::os::CreateAppDataArgs args = createAppDataArgs(); - args.packageName = "com.foo"; - args.flags = FLAG_STORAGE_DE; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.flags = FLAG_STORAGE_DE | FLAG_STORAGE_SDK; // Create the app user data. ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); @@ -1059,7 +1201,223 @@ TEST_F(SdkSandboxDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFla ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); // Only DE paths should exist - CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, S_IFDIR | 0751); + CheckFileAccess("misc_de/0/sdksandbox/com.foo", kSystemUid, kSystemUid, S_IFDIR | 0751); +} + +TEST_F(SdkSandboxDataTest, ReconcileSdkData) { + android::os::ReconcileSdkDataArgs args = + reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"}); + + // Create the sdk data. + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + const std::string barCePath = "misc_ce/0/sdksandbox/com.foo/bar@random1"; + CheckFileAccess(barCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(barCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(barCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string bazCePath = "misc_ce/0/sdksandbox/com.foo/baz@random2"; + CheckFileAccess(bazCePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(bazCePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(bazCePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string barDePath = "misc_de/0/sdksandbox/com.foo/bar@random1"; + CheckFileAccess(barDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(barDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(barDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + + const std::string bazDePath = "misc_de/0/sdksandbox/com.foo/baz@random2"; + CheckFileAccess(bazDePath, kTestSdkSandboxUid, kNobodyUid, S_IFDIR | S_ISGID | 0700); + CheckFileAccess(bazDePath + "/cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); + CheckFileAccess(bazDePath + "/code_cache", kTestSdkSandboxUid, kTestCacheGid, + S_IFDIR | S_ISGID | 0771); +} + +TEST_F(SdkSandboxDataTest, ReconcileSdkData_ExtraCodeDirectoriesAreDeleted) { + android::os::ReconcileSdkDataArgs args = + reconcileSdkDataArgs("com.foo", {"bar@random1", "baz@random2"}); + + // Create the sdksandbox data. + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + // Retry with different package name + args.subDirNames[0] = "bar.diff@random1"; + + // Create the sdksandbox data again + ASSERT_BINDER_SUCCESS(service->reconcileSdkData(args)); + + // New directoris should exist + CheckFileAccess("misc_ce/0/sdksandbox/com.foo/bar.diff@random1", kTestSdkSandboxUid, kNobodyUid, + S_IFDIR | S_ISGID | 0700); + CheckFileAccess("misc_ce/0/sdksandbox/com.foo/baz@random2", kTestSdkSandboxUid, kNobodyUid, + S_IFDIR | S_ISGID | 0700); + // Directory for old unreferred sdksandbox package name should be removed + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo/bar@random1")); +} + +class DestroyAppDataTest : public SdkSandboxDataTest {}; + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithCeAndDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + args.flags, result.ceDataInode)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + FLAG_STORAGE_CE, result.ceDataInode)); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); +} + +TEST_F(DestroyAppDataTest, DestroySdkSandboxDataDirectories_WithoutCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy the app user data. + ASSERT_BINDER_SUCCESS(service->destroyAppData(args.uuid, args.packageName, args.userId, + FLAG_STORAGE_DE, result.ceDataInode)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox/com.foo")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox/com.foo")); +} + +class ClearAppDataTest : public SdkSandboxDataTest { +public: + void createTestSdkData(const std::string& packageName, std::vector<std::string> sdkNames) { + const auto& cePackagePath = "/data/local/tmp/misc_ce/0/sdksandbox/" + packageName; + const auto& dePackagePath = "/data/local/tmp/misc_de/0/sdksandbox/" + packageName; + ASSERT_TRUE(mkdirs(cePackagePath, 0700)); + ASSERT_TRUE(mkdirs(dePackagePath, 0700)); + const std::vector<std::string> packagePaths = {cePackagePath, dePackagePath}; + for (const auto& packagePath : packagePaths) { + for (auto sdkName : sdkNames) { + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/cache", 0700)); + ASSERT_TRUE(mkdirs(packagePath + "/" + sdkName + "/code_cache", 0700)); + std::ofstream{packagePath + "/" + sdkName + "/cache/cachedTestData.txt"}; + std::ofstream{packagePath + "/" + sdkName + "/code_cache/cachedTestData.txt"}; + } + } + } +}; + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_CE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data + ASSERT_BINDER_SUCCESS( + service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | (InstalldNativeService::FLAG_CLEAR_CACHE_ONLY), + -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndClearCodeCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, + FLAG_STORAGE_DE | FLAG_CLEAR_CODE_CACHE_ONLY, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1/code_cache")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2/code_cache")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithCeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_CE, -1)); + + const std::string packagePath = kTestPath + "/misc_ce/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2")); +} + +TEST_F(ClearAppDataTest, ClearSdkSandboxDataDirectories_WithDeAndWithoutAnyCacheFlag) { + createTestSdkData("com.foo", {"shared", "sdk1", "sdk2"}); + // Clear the app user data. + ASSERT_BINDER_SUCCESS(service->clearAppData(kTestUuid, "com.foo", 0, FLAG_STORAGE_DE, -1)); + + const std::string packagePath = kTestPath + "/misc_de/0/sdksandbox/com.foo"; + ASSERT_TRUE(is_empty(packagePath + "/shared")); + ASSERT_TRUE(is_empty(packagePath + "/sdk1")); + ASSERT_TRUE(is_empty(packagePath + "/sdk2")); +} + +class DestroyUserDataTest : public SdkSandboxDataTest {}; + +TEST_F(DestroyUserDataTest, DestroySdkData_WithCeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy user data + ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_CE)); + ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/sdksandbox")); + ASSERT_TRUE(exists("/data/local/tmp/misc_de/0/sdksandbox")); +} + +TEST_F(DestroyUserDataTest, DestroySdkData_WithDeFlag) { + android::os::CreateAppDataResult result; + android::os::CreateAppDataArgs args = createAppDataArgs("com.foo"); + args.packageName = "com.foo"; + // Create the app user data. + ASSERT_BINDER_SUCCESS(service->createAppData(args, &result)); + // Destroy user data + ASSERT_BINDER_SUCCESS(service->destroyUserData(args.uuid, args.userId, FLAG_STORAGE_DE)); + ASSERT_TRUE(exists("/data/local/tmp/misc_ce/0/sdksandbox")); + ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/sdksandbox")); } } // namespace installd diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 17802a30e3..910cd630f3 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <android-base/scopeguard.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" @@ -47,6 +48,8 @@ namespace android { namespace installd { +using ::testing::UnorderedElementsAre; + class UtilsTest : public testing::Test { protected: virtual void SetUp() { @@ -658,6 +661,23 @@ TEST_F(UtilsTest, TestCreateDirIfNeeded) { ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } +TEST_F(UtilsTest, TestForEachSubdir) { + auto deleter = [&]() { + delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + system("mkdir -p /data/local/tmp/user/0/com.foo"); + system("mkdir -p /data/local/tmp/user/0/com.bar"); + system("touch /data/local/tmp/user/0/some-file"); + + std::vector<std::string> result; + foreach_subdir("/data/local/tmp/user/0", + [&](const std::string &filename) { result.push_back(filename); }); + + EXPECT_THAT(result, UnorderedElementsAre("com.foo", "com.bar")); +} + TEST_F(UtilsTest, TestSdkSandboxDataPaths) { // Ce data paths EXPECT_EQ("/data/misc_ce/0/sdksandbox", @@ -670,9 +690,11 @@ TEST_F(UtilsTest, TestSdkSandboxDataPaths) { create_data_misc_sdk_sandbox_package_path(nullptr, true, 10, "com.foo")); EXPECT_EQ("/data/misc_ce/0/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, true, 0, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 0, "com.foo", "shared")); EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, true, 10, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "shared")); + EXPECT_EQ("/data/misc_ce/10/sdksandbox/com.foo/bar@random", + create_data_misc_sdk_sandbox_sdk_path(nullptr, true, 10, "com.foo", "bar@random")); // De data paths EXPECT_EQ("/data/misc_de/0/sdksandbox", @@ -685,9 +707,11 @@ TEST_F(UtilsTest, TestSdkSandboxDataPaths) { create_data_misc_sdk_sandbox_package_path(nullptr, false, 10, "com.foo")); EXPECT_EQ("/data/misc_de/0/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, false, 0, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 0, "com.foo", "shared")); EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/shared", - create_data_misc_sdk_sandbox_shared_path(nullptr, false, 10, "com.foo")); + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "shared")); + EXPECT_EQ("/data/misc_de/10/sdksandbox/com.foo/bar@random", + create_data_misc_sdk_sandbox_sdk_path(nullptr, false, 10, "com.foo", "bar@random")); } TEST_F(UtilsTest, WaitChild) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 992425d56e..c7bea3f4a9 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -37,6 +37,7 @@ #include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/properties.h> +#include <linux/fs.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -212,7 +213,7 @@ std::string create_data_misc_sdk_sandbox_path(const char* uuid, bool isCeData, u /** * Create the path name where code data for all codes in a particular app will be stored. - * E.g. /data/misc_ce/0/sdksandbox/<app-name> + * E.g. /data/misc_ce/0/sdksandbox/<package-name> */ std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, userid_t user, const char* package_name) { @@ -223,15 +224,17 @@ std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, b } /** - * Create the path name where shared code data for a particular app will be stored. - * E.g. /data/misc_ce/0/sdksandbox/<app-name>/shared + * Create the path name where sdk data for a particular sdk will be stored. + * E.g. /data/misc_ce/0/sdksandbox/<package-name>/com.foo@randomstrings */ -std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData, - userid_t user, const char* package_name) { - return StringPrintf("%s/shared", +std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData, + userid_t user, const char* package_name, + const char* sub_dir_name) { + return StringPrintf("%s/%s", create_data_misc_sdk_sandbox_package_path(volume_uuid, isCeData, user, package_name) - .c_str()); + .c_str(), + sub_dir_name); } std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { @@ -421,7 +424,44 @@ std::vector<userid_t> get_known_users(const char* volume_uuid) { return users; } +long get_project_id(uid_t uid, long start_project_id_range) { + return uid - AID_APP_START + start_project_id_range; +} + +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit) { + struct fsxattr fsx; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << path << " to set project id."; + return -1; + } + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id."; + return -1; + } + + fsx.fsx_projid = project_id; + if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to set project id on " << path; + return -1; + } + if (set_inherit) { + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance."; + return -1; + } + + flags |= FS_PROJINHERIT_FL; + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance."; + return -1; + } + } + return 0; +} int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid, int32_t exclude_gid, bool exclude_apps) { FTS *fts; @@ -665,16 +705,16 @@ static int rename_delete_dir_contents(const std::string& pathname, auto temp_dir_path = base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str()); - if (::rename(pathname.c_str(), temp_dir_path.c_str())) { + auto dir_to_delete = temp_dir_path.c_str(); + if (::rename(pathname.c_str(), dir_to_delete)) { if (ignore_if_missing && (errno == ENOENT)) { return 0; } - ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(), - strerror(errno)); - return -errno; + ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), dir_to_delete, strerror(errno)); + dir_to_delete = pathname.c_str(); } - return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing); + return delete_dir_contents(dir_to_delete, 1, exclusion_predicate, ignore_if_missing); } bool is_renamed_deleted_dir(const std::string& path) { @@ -696,6 +736,34 @@ static auto open_dir(const char* dir) { return std::unique_ptr<DIR, DirCloser>(::opendir(dir)); } +// Collects filename of subdirectories of given directory and passes it to the function +int foreach_subdir(const std::string& pathname, const std::function<void(const std::string&)> fn) { + auto dir = open_dir(pathname.c_str()); + if (!dir) return -1; + + int dfd = dirfd(dir.get()); + if (dfd < 0) { + ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno)); + return -1; + } + + struct dirent* de; + while ((de = readdir(dir.get()))) { + if (de->d_type != DT_DIR) { + continue; + } + + std::string name{de->d_name}; + // always skip "." and ".." + if (name == "." || name == "..") { + continue; + } + fn(name); + } + + return 0; +} + void cleanup_invalid_package_dirs_under_path(const std::string& pathname) { auto dir = open_dir(pathname.c_str()); if (!dir) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 4b56f99de2..ecea1d2b1c 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -32,6 +32,7 @@ #define MEASURE_DEBUG 0 #define FIXUP_DEBUG 0 +#define SDK_DEBUG 1 #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 @@ -64,8 +65,9 @@ std::string create_data_misc_sdk_sandbox_path(const char* volume_uuid, bool isCe userid_t userid); std::string create_data_misc_sdk_sandbox_package_path(const char* volume_uuid, bool isCeData, userid_t userid, const char* package_name); -std::string create_data_misc_sdk_sandbox_shared_path(const char* volume_uuid, bool isCeData, - userid_t userid, const char* package_name); +std::string create_data_misc_sdk_sandbox_sdk_path(const char* volume_uuid, bool isCeData, + userid_t userid, const char* package_name, + const char* sub_dir_name); std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); @@ -130,6 +132,8 @@ int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_miss bool is_renamed_deleted_dir(const std::string& path); int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = true); +int foreach_subdir(const std::string& pathname, std::function<void(const std::string&)> fn); + void cleanup_invalid_package_dirs_under_path(const std::string& pathname); int delete_dir_contents(const char *pathname, @@ -167,6 +171,8 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta uid_t uid, gid_t gid); bool supports_sdcardfs(); +long get_project_id(uid_t uid, long start_project_id_range); +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit); int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId); int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId); diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 555be1ed7e..3cfe5297ca 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -295,28 +295,27 @@ bool isValidServiceName(const std::string& name) { Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) { auto ctx = mAccess->getCallingContext(); - // apps cannot add services if (multiuser_get_app_id(ctx.uid) >= AID_APP) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services"); } if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY); + return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial"); } if (binder == nullptr) { - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder"); } if (!isValidServiceName(name)) { LOG(ERROR) << "Invalid service name: " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name"); } #ifndef VENDORSERVICEMANAGER if (!meetsDeclarationRequirements(binder, name)) { // already logged - return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error"); } #endif // !VENDORSERVICEMANAGER @@ -324,7 +323,7 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi if (binder->remoteBinder() != nullptr && binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) { LOG(ERROR) << "Could not linkToDeath when adding " << name; - return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure"); } // Overwrite the old service if it exists diff --git a/data/etc/Android.bp b/data/etc/Android.bp index c901c5943e..34d53a52a0 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -221,6 +221,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.software.opengles.deqp.level-2022-03-01.prebuilt.xml", + src: "android.software.opengles.deqp.level-2022-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.software.sip.voip.prebuilt.xml", src: "android.software.sip.voip.xml", defaults: ["frameworks_native_data_etc_defaults"], @@ -239,6 +245,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.software.vulkan.deqp.level-2022-03-01.prebuilt.xml", + src: "android.software.vulkan.deqp.level-2022-03-01.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "aosp_excluded_hardware.prebuilt.xml", src: "aosp_excluded_hardware.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.telephony.calling.xml b/data/etc/android.hardware.telephony.calling.xml new file mode 100644 index 0000000000..f47a00bf70 --- /dev/null +++ b/data/etc/android.hardware.telephony.calling.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices to support telephony calling service related APIs --> +<permissions> + <feature name="android.hardware.telephony.calling" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.data.xml b/data/etc/android.hardware.telephony.data.xml new file mode 100644 index 0000000000..897b16bb56 --- /dev/null +++ b/data/etc/android.hardware.telephony.data.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices to support telephony data service related APIs --> +<permissions> + <feature name="android.hardware.telephony.data" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.messaging.xml b/data/etc/android.hardware.telephony.messaging.xml new file mode 100644 index 0000000000..416c74f804 --- /dev/null +++ b/data/etc/android.hardware.telephony.messaging.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices to support telephony messaging service related APIs --> +<permissions> + <feature name="android.hardware.telephony.messaging" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.radio.xml b/data/etc/android.hardware.telephony.radio.xml new file mode 100644 index 0000000000..69244b4909 --- /dev/null +++ b/data/etc/android.hardware.telephony.radio.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices to support telephony radio access related APIs --> +<permissions> + <feature name="android.hardware.telephony.radio" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.subscription.xml b/data/etc/android.hardware.telephony.subscription.xml new file mode 100644 index 0000000000..5b3a3ee3d1 --- /dev/null +++ b/data/etc/android.hardware.telephony.subscription.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices to support telephony subscription APIs --> +<permissions> + <feature name="android.hardware.telephony.subscription" /> +</permissions> diff --git a/data/etc/android.software.telecom.xml b/data/etc/android.software.telecom.xml new file mode 100644 index 0000000000..db28ba632f --- /dev/null +++ b/data/etc/android.software.telecom.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard set of features for devices that support the Telecom API. --> +<permissions> + <feature name="android.software.telecom" /> +</permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index cc0ee825a8..95b8110b6e 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -38,7 +38,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> - <feature name="android.software.connectionservice" /> + <feature name="android.software.telecom" /> <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.home_screen" /> <feature name="android.software.companion_device_setup" /> diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml index e6db4ade09..d601931680 100644 --- a/data/etc/go_handheld_core_hardware.xml +++ b/data/etc/go_handheld_core_hardware.xml @@ -37,7 +37,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> - <feature name="android.software.connectionservice" /> + <feature name="android.software.telecom" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> <feature name="android.software.input_methods" /> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 41f15144d4..68b8defc4c 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -42,7 +42,7 @@ <!-- basic system services --> <feature name="android.software.app_widgets" /> - <feature name="android.software.connectionservice" /> + <feature name="android.software.telecom" /> <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index 4c83a14728..ee392fc401 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -235,7 +235,7 @@ void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); * * Available since API level 33. */ -int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN(33); +int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) __INTRODUCED_IN(33); /* * Set the socket tag for traffic statistics on the specified socket. @@ -245,14 +245,26 @@ int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) __INTRODUCED_IN( * opened by another UID or was previously tagged by another UID. Subsequent * calls always replace any existing parameters. The socket tag is kept when the * socket is sent to another process using binder IPCs or other mechanisms such - * as UNIX socket fd passing. + * as UNIX socket fd passing. The tag is a value defined by the caller and used + * together with uid for data traffic accounting, so that the function callers + * can account different types of data usage for a uid. * * Returns 0 on success, or a negative POSIX error code (see errno.h) on * failure. * + * Some possible error codes: + * -EBADF Bad socketfd. + * -EPERM No permission. + * -EAFNOSUPPORT Socket family is neither AF_INET nor AF_INET6. + * -EPROTONOSUPPORT Socket protocol is neither IPPROTO_UDP nor IPPROTO_TCP. + * -EMFILE Too many stats entries. + * There are still other error codes that may provided by -errno of + * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by + * BPF maps read/write sys calls, which are set appropriately. + * * Available since API level 33. */ -int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33); +int android_tag_socket(int sockfd, uint32_t tag) __INTRODUCED_IN(33); /* * Untag a network socket. @@ -267,6 +279,12 @@ int android_tag_socket(int sockfd, int tag) __INTRODUCED_IN(33); * Returns 0 on success, or a negative POSIX error code (see errno.h) on * failure. * + * One of possible error code: + * -EBADF Bad socketfd. + * Other error codes are either provided by -errno of + * [getsockopt()](https://man7.org/linux/man-pages/man2/getsockopt.2.html) or by + * BPF map element deletion sys call, which are set appropriately. + * * Available since API level 33. */ int android_untag_socket(int sockfd) __INTRODUCED_IN(33); diff --git a/include/android/storage_manager.h b/include/android/storage_manager.h index 7f2ee08d62..270570e0df 100644 --- a/include/android/storage_manager.h +++ b/include/android/storage_manager.h @@ -124,6 +124,12 @@ typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int3 /** * Attempts to mount an OBB file. This is an asynchronous operation. + * + * Since API level 33, this function can only be used to mount unencrypted OBBs, + * i.e. the {@code key} parameter must be {@code null} or an empty string. Note + * that even before API level 33, mounting encrypted OBBs didn't work on many + * Android device implementations. Applications should not assume any particular + * behavior when {@code key} is nonempty. */ void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key, AStorageManager_obbCallbackFunc cb, void* data); diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7f0324a4a8..22b9faaf7a 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -295,6 +295,8 @@ enum class InputDeviceConfigurationFileType : int32_t { /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. + * The optional suffix is appended to the end of the file name (before the + * extension). * * The device identifier is used to construct several default configuration file * names to try based on the device name, vendor, product, and version. @@ -302,8 +304,8 @@ enum class InputDeviceConfigurationFileType : int32_t { * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type); + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix = ""); /* * Gets the path of an input device configuration file, if one is available. diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index b2bd535cbf..1da78aa0c1 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -20,9 +20,8 @@ #include <android-base/result.h> #include <stdint.h> #include <utils/Errors.h> -#include <utils/KeyedVector.h> -#include <utils/RefBase.h> #include <utils/Tokenizer.h> +#include <set> #include <input/InputDevice.h> @@ -65,18 +64,18 @@ struct AxisInfo { */ class KeyLayoutMap { public: - static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename); - static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename, + const char* contents = nullptr); static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename, const char* contents); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const; - status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; - status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; + std::vector<int32_t> findScanCodesForKey(int32_t keyCode) const; + std::optional<int32_t> findScanCodeForLed(int32_t ledCode) const; + std::optional<int32_t> findUsageCodeForLed(int32_t ledCode) const; - status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + std::optional<AxisInfo> mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode); @@ -84,6 +83,8 @@ public: virtual ~KeyLayoutMap(); private: + static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + struct Key { int32_t keyCode; uint32_t flags; @@ -98,12 +99,13 @@ private: int32_t sensorDataIndex; }; - KeyedVector<int32_t, Key> mKeysByScanCode; - KeyedVector<int32_t, Key> mKeysByUsageCode; - KeyedVector<int32_t, AxisInfo> mAxes; - KeyedVector<int32_t, Led> mLedsByScanCode; - KeyedVector<int32_t, Led> mLedsByUsageCode; + std::unordered_map<int32_t, Key> mKeysByScanCode; + std::unordered_map<int32_t, Key> mKeysByUsageCode; + std::unordered_map<int32_t, AxisInfo> mAxes; + std::unordered_map<int32_t, Led> mLedsByScanCode; + std::unordered_map<int32_t, Led> mLedsByUsageCode; std::unordered_map<int32_t, Sensor> mSensorsByAbsCode; + std::set<std::string> mRequiredKernelConfigs; std::string mLoadFileName; KeyLayoutMap(); @@ -124,6 +126,7 @@ private: status_t parseAxis(); status_t parseLed(); status_t parseSensor(); + status_t parseRequiredKernelConfig(); }; }; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 08ad8c6e5a..9a3e15f1cd 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -61,9 +61,7 @@ private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name); - std::string getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type); + const std::string& name); }; /** diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index bb40f5146e..3c6769fca7 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -39,9 +39,16 @@ ndk_headers { cc_library_headers { name: "libarect_headers", + vendor_available: true, + min_sdk_version: "29", // TODO(b/153609531): remove when no longer needed. native_bridge_supported: true, export_include_dirs: ["include"], + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], } cc_library_static { 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 63d87dae5a..9389bec509 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -60,11 +60,15 @@ 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", -] + +filegroup { + name: "libbinder_device_interface_sources", + srcs: [ + "IPermissionController.cpp", + "PermissionCache.cpp", + "PermissionController.cpp", + ], +} cc_library { name: "libbinder", @@ -126,19 +130,20 @@ cc_library { "TextOutput.cpp", "Utils.cpp", ":libbinder_aidl", + ":libbinder_device_interface_sources", ], target: { android: { - srcs: libbinder_device_interface_sources, - // NOT static to keep the wire protocol unfrozen static: { enabled: false, }, }, vendor: { - exclude_srcs: libbinder_device_interface_sources, + exclude_srcs: [ + ":libbinder_device_interface_sources", + ], }, darwin: { enabled: false, @@ -199,7 +204,6 @@ cc_library { "libbinder_headers", ], - clang: true, sanitize: { misc_undefined: ["integer"], }, @@ -217,6 +221,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "google-*", @@ -366,6 +371,7 @@ filegroup { cc_library { name: "libbatterystats_aidl", + host_supported: true, srcs: [ "IBatteryStats.cpp", ], @@ -378,6 +384,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 01b25d390d..e2db1a389c 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -32,9 +32,12 @@ #include <utils/misc.h> #include <inttypes.h> -#include <linux/sched.h> #include <stdio.h> +#ifdef __linux__ +#include <linux/sched.h> +#endif + #include "RpcState.h" namespace android { @@ -49,10 +52,11 @@ 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 // Log any reply transactions for which the data exceeds this size @@ -156,7 +160,7 @@ 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; } @@ -201,6 +205,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,16 +231,19 @@ 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; @@ -404,6 +412,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 +457,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 +485,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,7 +511,7 @@ 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; } @@ -521,7 +536,7 @@ 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; } @@ -539,7 +554,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,6 +597,10 @@ void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) { BBinder::~BBinder() { + if (!wasParceled() && getExtension()) { + ALOGW("Binder %p destroyed with extension attached before being parceled.", this); + } + Extras* e = mExtras.load(std::memory_order_relaxed); if (e) delete e; } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 921e57c7bf..1eb2ffd22d 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -279,7 +279,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) { diff --git a/libs/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h index fdd532aec6..57e03cb3a6 100644 --- a/libs/binder/BufferedTextOutput.h +++ b/libs/binder/BufferedTextOutput.h @@ -18,8 +18,8 @@ #define ANDROID_BUFFEREDTEXTOUTPUT_H #include <binder/TextOutput.h> -#include <utils/threads.h> #include <sys/uio.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- namespace android { diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 9c7ff97a48..c6b0cb7b01 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -31,7 +31,7 @@ #include <binder/Parcel.h> #include <log/log.h> -#include <utils/threads.h> +#include <utils/Mutex.h> #include <map> diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index f79075d260..d53621946a 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -27,7 +27,6 @@ #include <utils/CallStack.h> #include <utils/Log.h> #include <utils/SystemClock.h> -#include <utils/threads.h> #include <atomic> #include <errno.h> @@ -639,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; @@ -667,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) @@ -678,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; } @@ -990,6 +1001,7 @@ finish: if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; + logExtendedError(); } return err; @@ -1444,6 +1456,23 @@ status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { return ret; } +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(Parcel* parcel, const uint8_t* data, size_t /*dataSize*/, const binder_size_t* /*objects*/, diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index ea2f8d2274..fd47783acd 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -99,6 +99,8 @@ public: status_t unregisterForNotifications(const String16& service, const sp<AidlRegistrationCallback>& cb) override; + + std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; // for legacy ABI const String16& getInterfaceDescriptor() const override { return mTheRealServiceManager->getInterfaceDescriptor(); @@ -165,7 +167,7 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) { } } -#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) +#if !defined(__ANDROID_VNDK__) // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) @@ -543,6 +545,23 @@ status_t ServiceManagerShim::unregisterForNotifications(const String16& name, return OK; } +std::vector<IServiceManager::ServiceDebugInfo> ServiceManagerShim::getServiceDebugInfo() { + std::vector<os::ServiceDebugInfo> serviceDebugInfos; + std::vector<IServiceManager::ServiceDebugInfo> ret; + if (Status status = mTheRealServiceManager->getServiceDebugInfo(&serviceDebugInfos); + !status.isOk()) { + ALOGW("%s Failed to get ServiceDebugInfo", __FUNCTION__); + return ret; + } + for (const auto& serviceDebugInfo : serviceDebugInfos) { + IServiceManager::ServiceDebugInfo retInfo; + retInfo.pid = serviceDebugInfo.debugPid; + retInfo.name = serviceDebugInfo.name; + ret.emplace_back(retInfo); + } + return ret; +} + #ifndef __ANDROID__ // ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index c4475c7780..03553f36e9 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -23,7 +23,6 @@ #include <utils/Log.h> #include <utils/SortedVector.h> #include <utils/String8.h> -#include <utils/threads.h> #include <stdint.h> #include <stdio.h> diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index e1cbc1996d..8fe1d2bb3d 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -18,10 +18,13 @@ #include <errno.h> #include <fcntl.h> +#include <linux/memfd.h> #include <stdint.h> #include <stdlib.h> #include <sys/ioctl.h> +#include <sys/mman.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> @@ -34,6 +37,24 @@ namespace android { // --------------------------------------------------------------------------- +#ifdef __BIONIC__ +static int memfd_create_region(const char* name, size_t size) { + int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd == -1) { + ALOGE("%s: memfd_create(%s, %zd) failed: %s\n", __func__, name, size, strerror(errno)); + return -1; + } + + if (ftruncate(fd, size) == -1) { + ALOGE("%s, ftruncate(%s, %zd) failed for memfd creation: %s\n", __func__, name, size, + strerror(errno)); + close(fd); + return -1; + } + return fd; +} +#endif + MemoryHeapBase::MemoryHeapBase() : mFD(-1), mSize(0), mBase(MAP_FAILED), mDevice(nullptr), mNeedUnmap(false), mOffset(0) @@ -45,15 +66,33 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size); - ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); - if (fd >= 0) { - if (mapfd(fd, true, size) == NO_ERROR) { - if (flags & READ_ONLY) { - ashmem_set_prot_region(fd, PROT_READ); - } + size = ((size + pagesize - 1) & ~(pagesize - 1)); + int fd = -1; + if (mFlags & FORCE_MEMFD) { +#ifdef __BIONIC__ + ALOGV("MemoryHeapBase: Attempting to force MemFD"); + 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_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)); + munmap(mBase, mSize); + mBase = nullptr; + mSize = 0; + close(fd); } + return; +#else + mFlags &= ~(FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG); +#endif + } + 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; + if (mFlags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); } } @@ -61,6 +100,9 @@ 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_FLAG)) { + LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); + } int open_flags = O_RDWR; if (flags & NO_CACHING) open_flags |= O_SYNC; @@ -80,6 +122,9 @@ 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_FLAG)) { + LOG_ALWAYS_FATAL("FORCE_MEMFD, MEMFD_ALLOW_SEALING only valid with creating constructor"); + } const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 504c6c2d83..8739105393 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -40,6 +40,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> @@ -87,7 +88,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; @@ -151,6 +153,14 @@ static void release_object(const sp<ProcessState>& proc, const flat_binder_objec ALOGE("Invalid object type 0x%08x", obj.hdr.type); } +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) { internal::Stability::tryMarkCompilationUnit(binder.get()); @@ -182,13 +192,14 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { 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; @@ -259,9 +270,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) { 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 +280,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; } @@ -378,8 +391,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 +421,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 +460,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 +475,120 @@ 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()) { + 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 { + 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,10 +645,66 @@ 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; + + const auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return ret; + } + + size_t initPosition = dataPosition(); + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_BINDER) continue; + + setDataPosition(offset); + + sp<IBinder> binder = readStrongBinder(); + if (binder != nullptr) ret.push_back(binder); + } + + setDataPosition(initPosition); + return ret; +} + +std::vector<int> Parcel::debugReadAllFileDescriptors() const { + std::vector<int> ret; + + if (const auto* kernelFields = maybeKernelFields()) { + size_t initPosition = dataPosition(); + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + binder_size_t offset = kernelFields->mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; + + setDataPosition(offset); + + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + setDataPosition(initPosition); + } else if (const auto* rpcFields = maybeRpcFields(); rpcFields && rpcFields->mFds) { + for (const auto& fd : *rpcFields->mFds) { + ret.push_back(toRawFd(fd)); + } + } + + return ret; } status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const { @@ -580,17 +718,33 @@ 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()) { + 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 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; @@ -613,20 +767,24 @@ 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; } } @@ -645,7 +803,7 @@ 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()) { const IPCThreadState* threadState = IPCThreadState::self(); writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); updateWorkSourceRequestHeaderPosition(); @@ -660,12 +818,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; @@ -673,12 +835,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; @@ -699,7 +865,7 @@ bool Parcel::enforceInterface(const char16_t* interface, size_t len, IPCThreadState* threadState) const { - if (CC_LIKELY(!isForRpc())) { + if (auto* kernelFields = maybeKernelFields()) { // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { @@ -754,7 +920,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 @@ -1196,11 +1365,40 @@ 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; + } + } } flat_binder_object obj; @@ -1320,7 +1518,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; @@ -1360,8 +1558,11 @@ 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"); + 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; @@ -1372,14 +1573,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)); @@ -1390,14 +1591,15 @@ 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; @@ -1411,54 +1613,64 @@ 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; + } + // 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; } @@ -1472,7 +1684,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 @@ -1499,7 +1712,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 @@ -1543,9 +1757,11 @@ status_t Parcel::readOutVectorSizeWithCheck(size_t elmSize, int32_t* size) const template<class T> status_t Parcel::readAligned(T *pArg) const { static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + 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 @@ -1554,9 +1770,8 @@ status_t Parcel::readAligned(T *pArg) const { } } - const void* data = mData+mDataPos; + memcpy(pArg, mData + mDataPos, sizeof(T)); mDataPos += sizeof(T); - *pArg = *reinterpret_cast<const T*>(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; @@ -1576,10 +1791,11 @@ T Parcel::readAligned() const { template<class T> status_t Parcel::writeAligned(T val) { static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + static_assert(std::is_trivially_copyable_v<T>); if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: - *reinterpret_cast<T*>(mData+mDataPos) = val; + memcpy(mData + mDataPos, &val, sizeof(val)); return finishWrite(sizeof(val)); } @@ -1862,6 +2078,7 @@ status_t Parcel::readStrongBinder(sp<IBinder>* val) const { status_t status = readNullableStrongBinder(val); if (status == OK && !val->get()) { + ALOGW("Expecting binder but got null!"); status = UNEXPECTED_NULL; } return status; @@ -1922,8 +2139,31 @@ 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)); + } + const flat_binder_object* flat = readObject(true); if (flat && flat->hdr.type == BINDER_TYPE_FD) { @@ -1933,8 +2173,7 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } -int Parcel::readParcelFileDescriptor() const -{ +int Parcel::readParcelFileDescriptor() const { int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { @@ -2043,7 +2282,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; @@ -2089,6 +2328,11 @@ status_t Parcel::read(FlattenableHelperInterface& val) const } 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 @@ -2103,9 +2347,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", @@ -2124,7 +2368,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; } @@ -2137,7 +2381,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; } @@ -2148,20 +2392,23 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const return nullptr; } -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()) { + 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 if (auto* rpcFields = maybeRpcFields()) { + rpcFields->mFds.reset(); } } @@ -2177,35 +2424,43 @@ 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; 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 @@ -2223,7 +2478,7 @@ 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); @@ -2231,6 +2486,47 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, scanForFds(); } +status_t Parcel::rpcSetDataReference(const sp<RpcSession>& session, const uint8_t* data, + size_t dataSize, const uint32_t* objectTable, + size_t objectTableSize, + std::vector<base::unique_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); + + 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; + + if (objectTableSize != ancillaryFds.size()) { + ALOGE("objectTableSize=%zu ancillaryFds.size=%zu", objectTableSize, ancillaryFds.size()); + freeData(); // don't leak mData + return BAD_VALUE; + } + + 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->reserve(ancillaryFds.size()); + for (auto& fd : ancillaryFds) { + rpcFields->mFds->push_back(std::move(fd)); + } + } + + return OK; +} + void Parcel::print(TextOutput& to, uint32_t /*flags*/) const { to << "Parcel("; @@ -2241,14 +2537,16 @@ 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; + 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; + } } } else { to << "NULL"; @@ -2259,34 +2557,42 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const void Parcel::releaseObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + + 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); } } void Parcel::acquireObjects() { - size_t i = mObjectsSize; + auto* kernelFields = maybeKernelFields(); + if (kernelFields == nullptr) { + return; + } + + 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); } } @@ -2302,7 +2608,9 @@ 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(); + mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); } else { LOG_ALLOC("Parcel %p: freeing allocated data", this); releaseObjects(); @@ -2315,7 +2623,8 @@ void Parcel::freeDataNoInit() } free(mData); } - if (mObjects) free(mObjects); + auto* kernelFields = maybeKernelFields(); + if (kernelFields && kernelFields->mObjects) free(kernelFields->mObjects); } } @@ -2390,13 +2699,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; @@ -2410,17 +2724,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--; + } } } } @@ -2441,7 +2765,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); @@ -2452,20 +2776,27 @@ 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); + mOwner(this, mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr, + kernelFields ? kernelFields->mObjectsSize : 0); mOwner = nullptr; LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired); @@ -2473,43 +2804,51 @@ 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) { // 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; + } + if (rpcFields) { + if (status_t status = truncateRpcObjects(objectsSize); status != OK) { + return status; + } } // We own the data, so we can just do a realloc(). @@ -2545,9 +2884,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); @@ -2564,6 +2906,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); @@ -2574,37 +2945,20 @@ 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; } size_t Parcel::getBlobAshmemSize() const @@ -2617,10 +2971,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; diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp index 2e86b7415a..3cf94e3047 100644 --- a/libs/binder/ParcelableHolder.cpp +++ b/libs/binder/ParcelableHolder.cpp @@ -52,7 +52,10 @@ status_t ParcelableHolder::writeToParcel(Parcel* p) const { } status_t ParcelableHolder::readFromParcel(const Parcel* p) { - this->mStability = static_cast<Stability>(p->readInt32()); + int32_t wireStability; + if (status_t status = p->readInt32(&wireStability); status != OK) return status; + if (static_cast<int32_t>(this->mStability) != wireStability) return BAD_VALUE; + this->mParcelable = nullptr; this->mParcelableName = std::nullopt; int32_t rawDataSize; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index baa817c6b9..7faff47627 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -25,23 +25,25 @@ #include <binder/IServiceManager.h> #include <binder/Stability.h> #include <cutils/atomic.h> +#include <utils/AndroidThreads.h> #include <utils/Log.h> #include <utils/String8.h> -#include <utils/threads.h> +#include <utils/Thread.h> #include "Static.h" #include "binder_module.h" #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 @@ -99,6 +101,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); @@ -169,6 +176,10 @@ void ProcessState::childPostFork() { // the thread handler is installed if (gProcess) { gProcess->mForked = true; + + // "O_CLOFORK" + close(gProcess->mDriverFD); + gProcess->mDriverFD = -1; } gProcessMutex.unlock(); } @@ -181,7 +192,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); } @@ -289,12 +299,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) { @@ -385,6 +400,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); } } @@ -401,12 +419,44 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { return result; } -size_t ProcessState::getThreadPoolMaxThreadCount() const { +size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { // 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/" +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; + if (fd == -1) { + ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, + names[static_cast<int>(feature)], strerror(errno)); + return false; + } + if (read(fd, &on, sizeof(on)) == -1) { + ALOGE("%s: error reading to %s: %s", __func__, + names[static_cast<int>(feature)], strerror(errno)); + return false; + } + close(fd); + return on == '1'; } status_t ProcessState::enableOnewaySpamDetection(bool enable) { @@ -452,7 +502,9 @@ static base::Result<int> open_driver(const char* driver) { uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION; result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); if (result == -1) { - ALOGV("Binder ioctl to enable oneway spam detection failed: %s", strerror(errno)); + ALOGE_IF(ProcessState::isDriverFeatureEnabled( + ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION), + "Binder ioctl to enable oneway spam detection failed: %s", strerror(errno)); } return fd; } @@ -466,6 +518,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..3bb21adde8 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -24,18 +24,19 @@ #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 "FdTrigger.h" #include "RpcSocketAddress.h" #include "RpcState.h" #include "RpcWireFormat.h" +#include "Utils.h" namespace android { @@ -121,6 +122,14 @@ 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); mRootObjectFactory = nullptr; @@ -134,7 +143,7 @@ void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { mRootObjectWeak = binder; } void RpcServer::setPerSessionRootObject( - std::function<sp<IBinder>(const sockaddr*, socklen_t)>&& makeObject) { + std::function<sp<IBinder>(const void*, size_t)>&& makeObject) { std::lock_guard<std::mutex> _l(mLock); mRootObject.clear(); mRootObjectWeak.clear(); @@ -177,14 +186,16 @@ void RpcServer::join() { status_t status; while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) { - sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); + std::array<uint8_t, kRpcAddressSize> addr; + static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small"); + socklen_t addrLen = addr.size(); unique_fd clientFd( - TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(&addr), + TEMP_FAILURE_RETRY(accept4(mServer.get(), reinterpret_cast<sockaddr*>(addr.data()), &addrLen, SOCK_CLOEXEC | SOCK_NONBLOCK))); - LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(addr)), "Truncated address"); + LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)), + "Truncated address"); if (clientFd < 0) { ALOGE("Could not accept4 socket: %s", strerror(errno)); @@ -267,7 +278,7 @@ size_t RpcServer::numUninitializedSessions() { } void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen) { + std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen) { // 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); @@ -288,7 +299,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, /*enableAncillaryFds=*/false); if (status != OK) { ALOGE("Failed to read ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -302,8 +314,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, /*enableAncillaryFds=*/false); if (status != OK) { ALOGE("Failed to read session ID for client connecting to RPC server: %s", statusToString(status).c_str()); @@ -333,7 +345,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 @@ -380,24 +393,32 @@ 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 (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"); } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index e79cb86ffa..41842a7d84 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -34,6 +34,7 @@ #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> +#include <utils/Compat.h> #include <utils/String8.h> #include "FdTrigger.h" @@ -42,11 +43,7 @@ #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 @@ -132,6 +129,14 @@ std::optional<uint32_t> RpcSession::getProtocolVersion() { return mProtocolVersion; } +void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) { + mFileDescriptorTransportMode = mode; +} + +RpcSession::FileDescriptorTransportMode RpcSession::getFileDescriptorTransportMode() { + return mFileDescriptorTransportMode; +} + status_t RpcSession::setupUnixDomainClient(const char* path) { return setupSocketClient(UnixSocketAddress(path)); } @@ -152,13 +157,7 @@ status_t RpcSession::setupInetClient(const char* addr, unsigned int port) { } 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); + 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 +166,9 @@ 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); + status_t status = initAndAddConnection(std::move(fd), sessionId, incoming); + fd = unique_fd(); // Explicitly reset after move to avoid analyzer warning. + return status; }); } @@ -323,7 +324,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 @@ -613,6 +614,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 +623,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 +635,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(), @@ -696,7 +699,7 @@ status_t RpcSession::addOutgoingConnection(std::unique_ptr<RpcTransport> rpcTran { std::lock_guard<std::mutex> _l(mMutex); connection->rpcTransport = std::move(rpcTransport); - connection->exclusiveTid = gettid(); + connection->exclusiveTid = base::GetThreadId(); mConnections.mOutgoing.push_back(connection); } @@ -706,10 +709,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 +722,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; @@ -753,7 +754,7 @@ sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread( sp<RpcConnection> session = sp<RpcConnection>::make(); session->rpcTransport = std::move(rpcTransport); - session->exclusiveTid = gettid(); + session->exclusiveTid = base::GetThreadId(); mConnections.mIncoming.push_back(session); mConnections.mMaxIncoming = mConnections.mIncoming.size(); @@ -779,6 +780,15 @@ bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { return false; } +void RpcSession::clearConnectionTid(const sp<RpcConnection>& connection) { + std::unique_lock<std::mutex> _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,7 +799,7 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co connection->mConnection = nullptr; connection->mReentrant = false; - pid_t tid = gettid(); + uint64_t tid = base::GetThreadId(); std::unique_lock<std::mutex> _l(session->mMutex); session->mConnections.mWaitingThreads++; @@ -853,10 +863,16 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co } if (session->mConnections.mOutgoing.size() == 0) { - ALOGE("Session has no client connections. This is required for an RPC server to make " - "any non-nested (e.g. oneway or on another thread) calls. Use: %d. Server " - "connections: %zu", - static_cast<int>(use), session->mConnections.mIncoming.size()); + ALOGE("Session has no outgoing connections. This is required for an RPC server to make " + "any non-nested (e.g. oneway or on another thread) calls. Use code request " + "reason: %d. Incoming connections: %zu. %s.", + static_cast<int>(use), session->mConnections.mIncoming.size(), + (session->server() + ? "This is a server session, so see RpcSession::setMaxIncomingThreads " + "for the corresponding client" + : "This is a client session, so see RpcSession::setMaxOutgoingThreads " + "for this client or RpcServer::setMaxThreads for the corresponding " + "server")); return WOULD_BLOCK; } @@ -870,7 +886,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) { @@ -902,12 +918,7 @@ RpcSession::ExclusiveConnection::~ExclusiveConnection() { // is using this fd, and it retains the right to it. So, we don't give up // exclusive ownership, and no thread is freed. if (!mReentrant && mConnection != nullptr) { - std::unique_lock<std::mutex> _l(mSession->mMutex); - mConnection->exclusiveTid = std::nullopt; - if (mSession->mConnections.mWaitingThreads > 0) { - _l.unlock(); - mSession->mAvailableConnectionCv.notify_one(); - } + mSession->clearConnectionTid(mConnection); } } diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 2e7084e12e..2b1d0e5bf6 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> @@ -35,6 +37,7 @@ namespace android { using base::ScopeGuard; +using base::StringPrintf; #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { @@ -49,6 +52,15 @@ void rpcMaybeWaitToFlake() { } #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() {} @@ -309,17 +321,21 @@ 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 on RpcTransport %p: %s", what, connection->rpcTransport.get(), + LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", + what, i + 1, niovs, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } 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()); @@ -332,9 +348,9 @@ status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection, status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs) { - if (status_t status = - connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(), - iovs, niovs, {}); + if (status_t status = connection->rpcTransport->interruptableReadFully( + session->mShutdownTrigger.get(), iovs, niovs, std::nullopt, + enableAncillaryFds(session->getFileDescriptorTransportMode())); 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()); @@ -343,7 +359,8 @@ status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection, } for (int i = 0; i < niovs; i++) { - LOG_RPC_DETAIL("Received %s on RpcTransport %p: %s", what, connection->rpcTransport.get(), + LOG_RPC_DETAIL("Received %s (part %d of %d) on RpcTransport %p: %s", + what, i + 1, niovs, connection->rpcTransport.get(), android::base::HexString(iovs[i].iov_base, iovs[i].iov_len).c_str()); } return OK; @@ -367,7 +384,7 @@ 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, @@ -446,20 +463,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; @@ -491,14 +500,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{ @@ -506,6 +522,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; @@ -514,31 +532,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. @@ -561,7 +581,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti 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)); + delete[] const_cast<uint8_t*>(data); (void)dataSize; LOG_ALWAYS_FATAL_IF(objects != nullptr); LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount); @@ -583,29 +603,59 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, return status; } - CommandData data(command.bodySize); - if (!data.valid()) return NO_MEMORY; - - iovec iov{data.data(), command.bodySize}; - if (status_t status = rpcRec(connection, session, "reply body", &iov, 1); status != OK) - return status; + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); - if (command.bodySize < sizeof(RpcWireReply)) { + if (command.bodySize < rpcReplyWireSize) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", sizeof(RpcWireReply), command.bodySize); (void)session->shutdownAndWait(false); return BAD_VALUE; } - RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data()); - if (rpcReply->status != OK) return rpcReply->status; - data.release(); - reply->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 + + CommandData data(command.bodySize - rpcReplyWireSize); + if (!data.valid()) return NO_MEMORY; - reply->markForRpc(session); + iovec iovs[]{ + {&rpcReply, rpcReplyWireSize}, + {data.data(), data.size()}, + }; + if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs)); + status != OK) + return status; - return OK; + // Check if the reply came with any ancillary data. + std::vector<base::unique_fd> pendingFds; + if (status_t status = connection->rpcTransport->consumePendingAncillaryData(&pendingFds); + 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) { + Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize); + 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(pendingFds), cleanup_reply_data); } status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& connection, @@ -641,7 +691,7 @@ 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, @@ -659,9 +709,12 @@ status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& con status_t RpcState::drainCommands(const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, CommandType type) { - uint8_t buf; - while (connection->rpcTransport->peek(&buf, sizeof(buf)).value_or(0) > 0) { - status_t status = getAndExecuteCommand(connection, session, type); + while (true) { + status_t status = connection->rpcTransport->pollRead(); + if (status == WOULD_BLOCK) break; + if (status != OK) return status; + + status = getAndExecuteCommand(connection, session, type); if (status != OK) return status; } return OK; @@ -816,54 +869,82 @@ processTransactInternalTailCall: reply.markForRpc(session); if (replyStatus == OK) { + // Check if the transaction came with any ancillary data. + std::vector<base::unique_fd> pendingFds; + if (status_t status = connection->rpcTransport->consumePendingAncillaryData(&pendingFds); + status != OK) { + return status; + } + + 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) { + Span<const uint8_t> objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize); + 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(pendingFds), do_nothing_to_transact_data); - 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."); } } } @@ -935,48 +1016,67 @@ 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); + RpcDecStrong body; + iovec iov{&body, sizeof(RpcDecStrong)}; + if (status_t status = rpcRec(connection, session, "dec ref body", &iov, 1); status != OK) + return status; + + uint64_t addr = RpcWireAddress::toRaw(body.address); std::unique_lock<std::mutex> _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { @@ -994,19 +1094,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 @@ -1014,6 +1114,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..08a314ebef 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -178,9 +178,12 @@ 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 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); @@ -200,6 +203,10 @@ private: 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 diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 636e5d06e5..d9059e9a34 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -18,35 +18,48 @@ #include <log/log.h> #include <poll.h> +#include <stddef.h> #include <binder/RpcTransportRaw.h> #include "FdTrigger.h" #include "RpcState.h" -using android::base::ErrnoError; -using android::base::Result; - 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)) {} - Result<size_t> peek(void *buf, size_t size) override { - ssize_t ret = TEMP_FAILURE_RETRY(::recv(mSocket.get(), buf, size, MSG_PEEK)); + status_t pollRead(void) override { + uint8_t buf; + ssize_t ret = TEMP_FAILURE_RETRY( + ::recv(mSocket.get(), &buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT)); if (ret < 0) { - return ErrnoError() << "recv(MSG_PEEK)"; + int savedErrno = errno; + if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) { + return WOULD_BLOCK; + } + + LOG_RPC_DETAIL("RpcTransport poll(): %s", strerror(savedErrno)); + return -savedErrno; + } else if (ret == 0) { + return DEAD_OBJECT; } - return 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) { + status_t interruptableReadOrWrite( + 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) { @@ -76,15 +89,7 @@ public: bool havePolled = false; while (true) { - msghdr msg{ - .msg_iov = iovs, - // posix uses int, glibc uses size_t. niovs is a - // non-negative int and can be cast to either. - .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), - }; - ssize_t processSize = - TEMP_FAILURE_RETRY(sendOrReceiveFun(mSocket.get(), &msg, MSG_NOSIGNAL)); - + ssize_t processSize = sendOrReceiveFun(iovs, niovs); if (processSize < 0) { int savedErrno = errno; @@ -121,7 +126,7 @@ public: } if (altPoll) { - if (status_t status = altPoll(); status != OK) return status; + if (status_t status = (*altPoll)(); status != OK) return status; if (fdTrigger->isTriggered()) { return DEAD_OBJECT; } @@ -134,20 +139,135 @@ public: } } - status_t interruptableWriteFully(FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::function<status_t()>& altPoll) override { - return interruptableReadOrWrite(fdTrigger, iovs, niovs, sendmsg, "sendmsg", POLLOUT, - altPoll); + 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; + } + + // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then + // use memcpy. + int fds[kMaxFdsPerMsg]; + for (size_t i = 0; i < ancillaryFds->size(); i++) { + fds[i] = std::visit([](const auto& fd) { return fd.get(); }, + ancillaryFds->at(i)); + } + const size_t fdsByteSize = sizeof(int) * ancillaryFds->size(); + + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(fdsByteSize); + memcpy(CMSG_DATA(cmsg), fds, fdsByteSize); + + msg.msg_controllen = CMSG_SPACE(fdsByteSize); + + ssize_t processedSize = TEMP_FAILURE_RETRY( + sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC)); + if (processedSize > 0) { + sentFds = true; + } + return processedSize; + } + + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(sendmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT, altPoll); + } + + status_t interruptableReadFully( + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<android::base::function_ref<status_t()>>& altPoll, + bool enableAncillaryFds) override { + auto recv = [&](iovec* iovs, int niovs) -> ssize_t { + if (enableAncillaryFds) { + int fdBuffer[kMaxFdsPerMsg]; + alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))]; + + msghdr msg{ + .msg_iov = iovs, + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + .msg_control = msgControlBuf, + .msg_controllen = sizeof(msgControlBuf), + }; + ssize_t processSize = + TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + if (processSize < 0) { + return -1; + } + + for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks + // application devs to memcpy the data to ensure memory alignment. + size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0); + memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen); + size_t fdCount = dataLen / sizeof(int); + for (size_t i = 0; i < fdCount; i++) { + mFdsPendingRead.emplace_back(fdBuffer[i]); + } + break; + } + } + + if (msg.msg_flags & MSG_CTRUNC) { + ALOGE("msg was truncated. Aborting session."); + errno = EPIPE; + return -1; + } + + return processSize; + } + msghdr msg{ + .msg_iov = iovs, + // posix uses int, glibc uses size_t. niovs is a + // non-negative int and can be cast to either. + .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs), + }; + return TEMP_FAILURE_RETRY(recvmsg(mSocket.get(), &msg, MSG_NOSIGNAL)); + }; + return interruptableReadOrWrite(fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN, altPoll); } - 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); + status_t consumePendingAncillaryData(std::vector<base::unique_fd>* fds) override { + fds->reserve(fds->size() + mFdsPendingRead.size()); + for (auto& fd : mFdsPendingRead) { + fds->emplace_back(std::move(fd)); + } + mFdsPendingRead.clear(); + return OK; } private: base::unique_fd mSocket; + std::vector<base::unique_fd> mFdsPendingRead; }; // RpcTransportCtx with TLS disabled. diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index 3936204bdf..77831114be 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -37,10 +37,6 @@ #define LOG_TLS_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking #endif -using android::base::ErrnoError; -using android::base::Error; -using android::base::Result; - namespace android { namespace { @@ -165,17 +161,8 @@ public: return ret; } - // |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 toStatus(int sslError, const char* fnString) { switch (sslError) { - case SSL_ERROR_WANT_READ: - return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); - case SSL_ERROR_WANT_WRITE: - return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll); case SSL_ERROR_SYSCALL: { auto queue = toString(); LOG_TLS_DETAIL("%s(): %s. Treating as DEAD_OBJECT. Error queue: %s", fnString, @@ -191,14 +178,32 @@ 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::optional<android::base::function_ref<status_t()>>& altPoll) { + switch (sslError) { + case SSL_ERROR_WANT_READ: + return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); + case SSL_ERROR_WANT_WRITE: + return handlePoll(POLLOUT | additionalEvent, fd, fdTrigger, fnString, altPoll); + default: + return toStatus(sslError, fnString); + } + } + 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) { + 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); @@ -274,11 +279,21 @@ class RpcTransportTls : public RpcTransport { public: RpcTransportTls(android::base::unique_fd socket, Ssl ssl) : mSocket(std::move(socket)), mSsl(std::move(ssl)) {} - Result<size_t> peek(void* buf, size_t 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, + bool enableAncillaryFds) override; + + status_t consumePendingAncillaryData(std::vector<base::unique_fd>* fds) override { + (void)fds; + return OK; + } private: android::base::unique_fd mSocket; @@ -286,25 +301,30 @@ private: }; // Error code is errno. -Result<size_t> RpcTransportTls::peek(void* buf, size_t 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) { // Seen EAGAIN / EWOULDBLOCK on recv(2) / send(2). // Like RpcTransportRaw::peek(), don't handle it here. - return Error(EWOULDBLOCK) << "SSL_peek(): " << errorQueue.toString(); + errorQueue.clear(); + return WOULD_BLOCK; } - return Error() << "SSL_peek(): " << errorQueue.toString(); + return errorQueue.toStatus(err, "SSL_peek"); } errorQueue.clear(); LOG_TLS_DETAIL("TLS: Peeked %d bytes!", ret); - return 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; @@ -345,8 +365,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, + bool enableAncillaryFds) { + (void)enableAncillaryFds; + MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) return BAD_VALUE; @@ -411,8 +435,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(fd, sslError, fdTrigger, + "SSL_do_handshake", 0, std::nullopt); if (pollStatus != OK) return false; } } diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h index 171550e620..13989e5bf5 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; @@ -131,7 +132,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 +143,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/Static.h b/libs/binder/Static.h index 83524e8575..8444fe780c 100644 --- a/libs/binder/Static.h +++ b/libs/binder/Static.h @@ -17,8 +17,6 @@ // All static variables go here, to control initialization and // destruction order in the library. -#include <utils/threads.h> - #include <binder/IBinder.h> #include <binder/ProcessState.h> 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..0232f509a2 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -83,5 +83,10 @@ { "name": "rustBinderSerializationTest" } + ], + "hwasan-presubmit": [ + { + "name": "binderLibTest" + } ] } diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp index d2a5be1102..b0289a7196 100644 --- a/libs/binder/Utils.cpp +++ b/libs/binder/Utils.cpp @@ -16,6 +16,7 @@ #include "Utils.h" +#include <android-base/file.h> #include <string.h> using android::base::ErrnoError; @@ -38,4 +39,17 @@ Result<void> setNonBlocking(android::base::borrowed_fd fd) { return {}; } +status_t getRandomBytes(uint8_t* data, size_t size) { + int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ret == -1) { + return -errno; + } + + base::unique_fd fd(ret); + if (!base::ReadFully(fd, data, size)) { + return -errno; + } + return OK; +} + } // namespace android diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index ff2fad8834..37c1262c9b 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -14,12 +14,14 @@ * limitations under the License. */ -#include <cstdint> #include <stddef.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 { \ @@ -36,4 +38,39 @@ void zeroMemory(uint8_t* data, size_t size); android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +status_t getRandomBytes(uint8_t* data, size_t size); + +// 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. + // + // Aborts if offset > size. + Span<T> splitOff(size_t offset) { + LOG_ALWAYS_FATAL_IF(offset > size); + 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/binder_module.h b/libs/binder/binder_module.h index 793795e1d4..7574c29e8b 100644 --- a/libs/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -100,4 +100,23 @@ struct binder_frozen_status_info { #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) #endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION +#ifndef BINDER_GET_EXTENDED_ERROR +/* struct binder_extened_error - extended error information + * @id: identifier for the failed operation + * @command: command as defined by binder_driver_return_protocol + * @param: parameter holding a negative errno value + * + * Used with BINDER_GET_EXTENDED_ERROR. This extends the error information + * returned by the driver upon a failed operation. Userspace can pull this + * data to properly handle specific error scenarios. + */ +struct binder_extended_error { + __u32 id; + __u32 command; + __s32 param; +}; + +#define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error) +#endif // BINDER_GET_EXTENDED_ERROR + #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 8deb2fe4a8..19ad5e6efe 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -18,7 +18,6 @@ #include <binder/IBinder.h> #include <utils/Mutex.h> -#include <utils/threads.h> #include <map> #include <unordered_map> diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index f295417ab2..e864f9e6a3 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -72,9 +72,9 @@ class BnInterface : public INTERFACE, public BBinder public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; @@ -85,9 +85,9 @@ class BpInterface : public INTERFACE, public BpRefBase { public: explicit BpInterface(const sp<IBinder>& remote); + typedef INTERFACE BaseInterface; protected: - typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index bf02099ffb..cd6a27414d 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -216,6 +216,7 @@ private: static void freeBuffer(Parcel* parcel, 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/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index ea40db8ffa..413c97f349 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -56,8 +56,16 @@ public: static const int DUMP_FLAG_PROTO = 1 << 4; /** - * Retrieve an existing service, blocking for a few seconds - * if it doesn't yet exist. + * Retrieve an existing service, blocking for a few seconds if it doesn't yet exist. This + * does polling. A more efficient way to make sure you unblock as soon as the service is + * available is to use waitForService or to use service notifications. + * + * Warning: when using this API, typically, you should call it in a loop. It's dangerous to + * assume that nullptr could mean that the service is not available. The service could just + * be starting. Generally, whether a service exists, this information should be declared + * externally (for instance, an Android feature might imply the existence of a service, + * a system property, or in the case of services in the VINTF manifest, it can be checked + * with isDeclared). */ virtual sp<IBinder> getService( const String16& name) const = 0; @@ -126,6 +134,12 @@ public: virtual status_t unregisterForNotifications(const String16& name, const sp<LocalRegistrationCallback>& callback) = 0; + + struct ServiceDebugInfo { + std::string name; + int pid; + }; + virtual std::vector<ServiceDebugInfo> getServiceDebugInfo() = 0; }; sp<IServiceManager> defaultServiceManager(); diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index dd76943ac7..c7177bd8eb 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -26,15 +26,30 @@ 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 // process. DONT_MAP_LOCALLY = 0x00000100, - NO_CACHING = 0x00000200 + NO_CACHING = 0x00000200, + // Bypass ashmem-libcutils to create a memfd shared region. + // Ashmem-libcutils will eventually migrate to memfd. + // Memfd has security benefits and supports file sealing. + // Calling process will need to modify selinux permissions to + // open access to tmpfs files. See audioserver for examples. + // This is only valid for size constructor. + // For host compilation targets, memfd is stubbed in favor of /tmp + // files so sealing is not enforced. + FORCE_MEMFD = 0x00000400, + // Default opt-out of sealing behavior in memfd to avoid potential DOS. + // 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 = FORCE_MEMFD | MEMFD_ALLOW_SEALING_FLAG }; /* diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 450e3888f1..68a4aef362 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); @@ -95,6 +101,12 @@ public: bool hasFileDescriptors() const; status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const; + // returns all binder objects in the Parcel + std::vector<sp<IBinder>> debugReadAllStrongBinders() const; + // returns all file descriptors in the Parcel + // does not dup + std::vector<int> debugReadAllFileDescriptors() const; + // Zeros data when reallocating. Other mitigations may be added // in the future. // @@ -594,16 +606,24 @@ private: 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<base::unique_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; @@ -1241,19 +1261,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 @@ -1262,7 +1320,6 @@ private: release_func mOwner; - sp<RpcSession> mSession; size_t mReserved; class Blob { diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h index 42c85f9121..88790a803b 100644 --- a/libs/binder/include/binder/ParcelableHolder.h +++ b/libs/binder/include/binder/ParcelableHolder.h @@ -86,7 +86,7 @@ public: *ret = nullptr; return android::BAD_VALUE; } - *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::static_pointer_cast<T>(mParcelable); return android::OK; } this->mParcelPtr->setDataPosition(0); @@ -105,7 +105,7 @@ public: return status; } this->mParcelPtr = nullptr; - *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::static_pointer_cast<T>(mParcelable); return android::OK; } diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h index e65857417b..6f9eb5e4b9 100644 --- a/libs/binder/include/binder/PermissionController.h +++ b/libs/binder/include/binder/PermissionController.h @@ -19,8 +19,7 @@ #ifndef __ANDROID_VNDK__ #include <binder/IPermissionController.h> - -#include <utils/threads.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- namespace android { diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index cf8d8e433c..e17a76cf23 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -18,11 +18,10 @@ #include <binder/IBinder.h> #include <utils/KeyedVector.h> +#include <utils/Mutex.h> #include <utils/String16.h> #include <utils/String8.h> -#include <utils/threads.h> - #include <pthread.h> // --------------------------------------------------------------------------- @@ -85,11 +84,18 @@ 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); private: static sp<ProcessState> init(const char* defaultDriver, bool requireDefault); @@ -127,8 +133,12 @@ private: 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 aaa812b067..7fea76e051 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -77,7 +77,7 @@ public: * have */ [[nodiscard]] status_t setupInetServer(const char* address, unsigned int port, - unsigned int* assignedPort); + unsigned int* assignedPort = nullptr); /** * If setup*Server has been successful, return true. Otherwise return false. @@ -114,6 +114,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 +134,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(); /** @@ -177,13 +194,17 @@ private: void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; + static constexpr size_t kRpcAddressSize = 128; static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd, - const sockaddr_storage addr, socklen_t addrLen); + std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen); [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address); const std::unique_ptr<RpcTransportCtx> mCtx; size_t mMaxThreads = 1; std::optional<uint32_t> mProtocolVersion; + // A mode is supported if the N'th bit is on, where N is the mode enum's value. + std::bitset<8> mSupportedFileDescriptorTransportModes = std::bitset<8>().set( + static_cast<size_t>(RpcSession::FileDescriptorTransportMode::NONE)); base::unique_fd mServer; // socket we are accepting sessions on std::mutex mLock; // for below @@ -192,7 +213,7 @@ private: std::map<std::thread::id, std::thread> 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; diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index 1bc84641f2..b98b0ebea9 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,6 +15,7 @@ */ #pragma once +#include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcTransport.h> @@ -35,10 +36,16 @@ class RpcState; class RpcTransport; class FdTrigger; -constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 0; +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 = 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 * and a server. Multiple connections are needed for multiple parallel "binder" @@ -88,6 +95,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. @@ -211,7 +230,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; }; @@ -257,6 +276,7 @@ private: sp<RpcConnection> assignIncomingConnectionToThisThread( std::unique_ptr<RpcTransport> rpcTransport); [[nodiscard]] bool removeIncomingConnection(const sp<RpcConnection>& connection); + void clearConnectionTid(const sp<RpcConnection>& connection); [[nodiscard]] status_t initShutdownTrigger(); @@ -276,7 +296,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 +326,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 @@ -325,6 +345,7 @@ private: size_t mMaxIncomingThreads = 0; size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; std::optional<uint32_t> mProtocolVersion; + FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE; std::condition_variable mAvailableConnectionCv; // for mWaitingThreads diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index ade2d94609..80f5a32479 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -20,9 +20,12 @@ #include <functional> #include <memory> +#include <optional> #include <string> +#include <variant> +#include <vector> -#include <android-base/result.h> +#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> @@ -40,8 +43,15 @@ class RpcTransport { public: virtual ~RpcTransport() = default; - // replacement of ::recv(MSG_PEEK). Error code may not be set if TLS is enabled. - [[nodiscard]] virtual android::base::Result<size_t> peek(void *buf, size_t 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. @@ -53,16 +63,31 @@ 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. + * + * enableAncillaryFds - Whether to check for FDs in the ancillary data and + * queue for them for use in `consumePendingAncillaryData`. If false and FDs + * are received, they will be silently dropped (and closed) by the operating + * system. + * * 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, + bool enableAncillaryFds) = 0; + + // Consume the ancillary data that was accumulated from previous + // `interruptableReadFully` calls. + // + // Appends to `fds`. + virtual status_t consumePendingAncillaryData(std::vector<base::unique_fd> *fds) = 0; protected: RpcTransport() = default; diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h index b772b80227..abc7f1d374 100644 --- a/libs/binder/include_activitymanager/binder/ActivityManager.h +++ b/libs/binder/include_activitymanager/binder/ActivityManager.h @@ -18,10 +18,9 @@ #ifndef __ANDROID_VNDK__ -#include <binder/IActivityManager.h> #include <android/app/ProcessStateEnum.h> - -#include <utils/threads.h> +#include <binder/IActivityManager.h> +#include <utils/Mutex.h> // --------------------------------------------------------------------------- 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..32e018d597 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -113,6 +113,7 @@ cc_library { "abseil-*", "android-*", "bugprone-*", + "-bugprone-branch-clone", // b/155034972 "cert-*", "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 28e3ff43ff..b21a7e9584 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -36,6 +36,7 @@ using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; using ::android::wp; @@ -133,7 +134,8 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor - << "' to dead binder."; + << "' to dead binder with cached descriptor '" << SanitizeString(descriptor) + << "'."; } return false; } @@ -458,7 +460,8 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { LOG(ERROR) << __func__ - << ": removed reference to death recipient but unlink failed."; + << ": removed reference to death recipient but unlink failed: " + << statusToString(status); } return PruneStatusT(status); } @@ -539,7 +542,8 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -550,7 +554,8 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { - LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient (" + << recipient << ")"; return STATUS_UNEXPECTED_NULL; } @@ -625,7 +630,8 @@ void* AIBinder_getUserData(AIBinder* binder) { binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { if (binder == nullptr || in == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder + << ") and in (" << in << ")."; return STATUS_UNEXPECTED_NULL; } const AIBinder_Class* clazz = binder->getClass(); @@ -671,7 +677,9 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa AutoParcelDestroyer forIn(in, DestroyParcel); if (!isUserCommand(code)) { - LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK."; + LOG(ERROR) << __func__ + << ": Only user-defined transactions can be made from the NDK, but requested: " + << code; return STATUS_UNKNOWN_TRANSACTION; } @@ -682,7 +690,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa } if (binder == nullptr || *in == nullptr || out == nullptr) { - LOG(ERROR) << __func__ << ": requires non-null parameters."; + LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in (" + << in << "), and out (" << out << ")."; return STATUS_UNEXPECTED_NULL; } diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index b3bc7f449e..766df16635 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_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index 28819bb42a..f45aa7631b 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -101,7 +101,12 @@ class AParcelableHolder { return STATUS_INVALID_OPERATION; } - RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability)); + parcelable_stability_t wireStability; + RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability)); + if (this->mStability != wireStability) { + return STATUS_BAD_VALUE; + } + int32_t dataSize; binder_status_t status = AParcel_readInt32(parcel, &dataSize); 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_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 2a66941cef..dfa8ea28e7 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -53,11 +53,19 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const /** * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible - * for calling AIBinder_decStrong). + * for calling AIBinder_decStrong). This does polling. A more efficient way to make sure you + * unblock as soon as the service is available is to use AIBinder_waitForService. * * WARNING: when using this API across an APEX boundary, do not use with unstable * AIDL services. TODO(b/139325195) * + * WARNING: when using this API, typically, you should call it in a loop. It's dangerous to + * assume that nullptr could mean that the service is not available. The service could just + * be starting. Generally, whether a service exists, this information should be declared + * externally (for instance, an Android feature might imply the existence of a service, + * a system property, or in the case of services in the VINTF manifest, it can be checked + * with AServiceManager_isDeclared). + * * \param instance identifier of the service used to lookup the service. */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance) diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index f113ba8f21..683a43372b 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -97,6 +97,10 @@ 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 defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 +__attribute__((weak)) +#endif // defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 30 void AIBinder_markVintfStability(AIBinder* binder); __END_DECLS diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 467e51e276..5d6206de0b 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -1086,7 +1086,7 @@ macro_rules! declare_binder_enum { { $( #[$attr:meta] )* $enum:ident : [$backing:ty; $size:expr] { - $( $name:ident = $value:expr, )* + $( $( #[$value_attr:meta] )* $name:ident = $value:expr, )* } } => { $( #[$attr] )* @@ -1094,7 +1094,7 @@ macro_rules! declare_binder_enum { #[allow(missing_docs)] pub struct $enum(pub $backing); impl $enum { - $( #[allow(missing_docs)] pub const $name: Self = Self($value); )* + $( $( #[$value_attr] )* #[allow(missing_docs)] pub const $name: Self = Self($value); )* #[inline(always)] #[allow(missing_docs)] diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index b7c7ae4b54..3edeebf892 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -335,11 +335,16 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { // We don't own this file, so we need to be careful not to drop it. let file = ManuallyDrop::new(File::from_raw_fd(fd)); - if args.is_null() { + if args.is_null() && num_args != 0 { return StatusCode::UNEXPECTED_NULL as status_t; } - let args = slice::from_raw_parts(args, num_args as usize); - let args: Vec<_> = args.iter().map(|s| CStr::from_ptr(*s)).collect(); + + let args = if args.is_null() || num_args == 0 { + vec![] + } else { + slice::from_raw_parts(args, num_args as usize) + .iter().map(|s| CStr::from_ptr(*s)).collect() + }; let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index d58e839ad4..432da5dfd7 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -233,7 +233,9 @@ impl Parcelable for ParcelableHolder { } fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> { - self.stability = parcel.read()?; + if self.stability != parcel.read()? { + return Err(StatusCode::BAD_VALUE); + } let data_size: i32 = parcel.read()?; if data_size < 0 { diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index e3e47305d4..4df557bee0 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -575,6 +575,20 @@ struct DeathRecipientVtable { cookie_decr_refcount: unsafe extern "C" fn(*mut c_void), } +/// # Safety +/// +/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer +/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// `AIBinder_DeathRecipient` is threadsafe, this structure is too. +unsafe impl Send for DeathRecipient {} + +/// # Safety +/// +/// A `DeathRecipient` is a wrapper around `AIBinder_DeathRecipient` and a pointer +/// to a `Fn` which is `Sync` and `Send` (the cookie field). As +/// `AIBinder_DeathRecipient` is threadsafe, this structure is too. +unsafe impl Sync for DeathRecipient {} + impl DeathRecipient { /// Create a new death recipient that will call the given callback when its /// associated object dies. 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..f6bdf5c9ed 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -117,8 +117,8 @@ fn on_transact( ) -> Result<(), StatusCode> { match code { bindings::Transaction_TEST_BOOL => { - assert_eq!(parcel.read::<bool>()?, true); - assert_eq!(parcel.read::<bool>()?, false); + assert!(parcel.read::<bool>()?); + assert!(!parcel.read::<bool>()?); assert_eq!(parcel.read::<Vec<bool>>()?, unsafe { bindings::TESTDATA_BOOL }); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index ff55d6ebdc..2f96d0e0e0 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -99,6 +99,7 @@ cc_test { "binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp", "binderStatusUnitTest.cpp", + "binderMemoryHeapBaseUnitTest.cpp", ], shared_libs: [ "libbinder", @@ -318,7 +319,6 @@ cc_test { "libbinder", "libutils", ], - clang: true, cflags: [ "-g", "-Wno-missing-field-initializers", 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..60b3c94f5e 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -15,8 +15,11 @@ */ #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 <gtest/gtest.h> #include <utils/CallStack.h> @@ -124,12 +127,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 +184,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*/)); diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp index 1be0c59c78..ce2770f943 100644 --- a/libs/binder/tests/binderBinderUnitTest.cpp +++ b/libs/binder/tests/binderBinderUnitTest.cpp @@ -41,3 +41,10 @@ TEST(Binder, DetachObject) { EXPECT_EQ(kObject1, binder->detachObject(kObjectId1)); EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject2, nullptr, nullptr)); } + +TEST(Binder, AttachExtension) { + auto binder = sp<BBinder>::make(); + auto ext = sp<BBinder>::make(); + binder->setExtension(ext); + EXPECT_EQ(ext, binder->getExtension()); +} diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index b1e17b7892..4ed3309e0c 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()); @@ -1221,6 +1233,53 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +struct TooManyFdsFlattenable : Flattenable<TooManyFdsFlattenable> { + TooManyFdsFlattenable(size_t fdCount) : mFdCount(fdCount) {} + + // Flattenable protocol + size_t getFlattenedSize() const { + // Return a valid non-zero size here so we don't get an unintended + // BAD_VALUE from Parcel::write + return 16; + } + size_t getFdCount() const { return mFdCount; } + status_t flatten(void *& /*buffer*/, size_t & /*size*/, int *&fds, size_t &count) const { + for (size_t i = 0; i < count; i++) { + fds[i] = STDIN_FILENO; + } + return NO_ERROR; + } + status_t unflatten(void const *& /*buffer*/, size_t & /*size*/, int const *& /*fds*/, + size_t & /*count*/) { + /* This doesn't get called */ + return NO_ERROR; + } + + size_t mFdCount; +}; + +TEST_F(BinderLibTest, TooManyFdsFlattenable) { + rlimit origNofile; + int ret = getrlimit(RLIMIT_NOFILE, &origNofile); + ASSERT_EQ(0, ret); + + // Restore the original file limits when the test finishes + base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + + rlimit testNofile = {1024, 1024}; + ret = setrlimit(RLIMIT_NOFILE, &testNofile); + ASSERT_EQ(0, ret); + + Parcel parcel; + // Try to write more file descriptors than supported by the OS + TooManyFdsFlattenable tooManyFds1(1024); + EXPECT_THAT(parcel.write(tooManyFds1), StatusEq(-EMFILE)); + + // Try to write more file descriptors than the internal limit + TooManyFdsFlattenable tooManyFds2(1025); + EXPECT_THAT(parcel.write(tooManyFds2), StatusEq(BAD_VALUE)); +} + TEST(ServiceNotifications, Unregister) { auto sm = defaultServiceManager(); using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; @@ -1234,6 +1293,80 @@ 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 - 1; 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(1); + // 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(); + // No more than 16 threads should exist. + EXPECT_TRUE(replyi == kKernelThreads || 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 +1773,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 +1819,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 +1921,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 new file mode 100644 index 0000000000..278dd2bf81 --- /dev/null +++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include <binder/MemoryHeapBase.h> +#include <cutils/ashmem.h> +#include <fcntl.h> + +#include <gtest/gtest.h> +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)); + EXPECT_NE(fcntl(fd, F_GET_SEALS), -1); +} + +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); +} + +TEST(MemoryHeapBase, MemfdUnsealed) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + 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); +} + +TEST(MemoryHeapBase, MemfdSealedProtected) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + 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); +} + +TEST(MemoryHeapBase, MemfdUnsealedProtected) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + 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); +} + +#else +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); + EXPECT_TRUE(ashmem_valid(fd)); + EXPECT_EQ(mHeap->getFlags(), MemoryHeapBase::READ_ONLY); +} + +TEST(MemoryHeapBase,HostMemfdException) { + auto mHeap = sp<MemoryHeapBase>::make(8192, + MemoryHeapBase::FORCE_MEMFD | + 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); + EXPECT_TRUE(ashmem_valid(fd)); + EXPECT_NE(ptr, MAP_FAILED); +} + +#endif diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index aee15d8bd9..359c783de5 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -20,9 +20,12 @@ #include <cutils/ashmem.h> #include <gtest/gtest.h> +using android::BBinder; +using android::IBinder; using android::IPCThreadState; using android::OK; using android::Parcel; +using android::sp; using android::status_t; using android::String16; using android::String8; @@ -75,6 +78,40 @@ TEST(Parcel, EnforceNoDataAvail) { EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE); } +TEST(Parcel, DebugReadAllBinders) { + sp<IBinder> binder1 = sp<BBinder>::make(); + sp<IBinder> binder2 = sp<BBinder>::make(); + + Parcel p; + p.writeInt32(4); + p.writeStrongBinder(binder1); + p.writeStrongBinder(nullptr); + p.writeInt32(4); + p.writeStrongBinder(binder2); + p.writeInt32(4); + + auto ret = p.debugReadAllStrongBinders(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], binder1); + EXPECT_EQ(ret[1], binder2); +} + +TEST(Parcel, DebugReadAllFds) { + Parcel p; + p.writeInt32(4); + p.writeFileDescriptor(STDOUT_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + p.writeFileDescriptor(STDIN_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + + auto ret = p.debugReadAllFileDescriptors(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], STDOUT_FILENO); + EXPECT_EQ(ret[1], STDIN_FILENO); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) { diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index c2639e7c67..3b1fc82226 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -23,6 +23,7 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> +#include <android-base/stringprintf.h> #include <android/binder_auto_utils.h> #include <android/binder_libbinder.h> #include <binder/Binder.h> @@ -53,6 +54,7 @@ #include "../RpcSocketAddress.h" // for testing preconnected clients #include "../RpcState.h" // for debugging #include "../vm_sockets.h" // for VMADDR_* +#include "utils/Errors.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -92,6 +94,21 @@ static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( } } +// Create an FD that returns `contents` when read. +static base::unique_fd mockFileDescriptor(std::string contents) { + android::base::unique_fd readFd, writeFd; + CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno); + std::thread([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; + EXPECT_EQ(EPIPE, savedErrno) + << "mockFileDescriptor write failed: " << strerror(savedErrno); + } + }).detach(); + return readFd; +} + TEST(BinderRpcParcel, EntireParcelFormatted) { Parcel p; p.writeInt32(3); @@ -99,17 +116,19 @@ TEST(BinderRpcParcel, EntireParcelFormatted) { EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); } -class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { +class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { public: static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { - return newFactory(info.param)->toCString(); + return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + + std::to_string(std::get<1>(info.param)); } }; -TEST_P(BinderRpcSimple, SetExternalServerTest) { +TEST_P(BinderRpcServerOnly, SetExternalServerTest) { base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); - auto server = RpcServer::make(newFactory(GetParam())); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); @@ -200,6 +219,10 @@ public: } 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; @@ -322,9 +345,36 @@ public: (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(); + } }; sp<IBinder> MyBinderRpcTest::mHeldBinder; +static std::string WaitStatusToString(int wstatus) { + if (WIFEXITED(wstatus)) { + return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus)); + } + if (WIFSIGNALED(wstatus)) { + return base::StringPrintf("term signal %d", WTERMSIG(wstatus)); + } + return base::StringPrintf("unexpected state %d", wstatus); +} + class Process { public: Process(Process&&) = default; @@ -345,13 +395,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; @@ -419,10 +484,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)); @@ -480,18 +545,40 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } -class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { +class BinderRpc + : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t>> { public: struct Options { 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; }; + 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()); } + + // 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 PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [type, security] = info.param; - return PrintToString(type) + "_" + newFactory(security)->toCString(); + auto [type, security, clientVersion, serverVersion] = info.param; + return PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); } static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { @@ -533,17 +620,22 @@ public: 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()); unsigned int vsockPort = allocateVsockPort(); std::string addr = allocateSocketAddress(); 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->setProtocolVersion(serverVersion); server->setMaxThreads(options.numThreads); + server->setSupportedFileDescriptorTransportModes( + options.serverSupportedFileDescriptorTransportModes); unsigned int outPort = 0; @@ -618,8 +710,10 @@ 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: @@ -639,6 +733,10 @@ 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()}); } @@ -650,8 +748,11 @@ public: .proc = createRpcTestSocketServerProcess( options, [&](const sp<RpcServer>& server) { - server->setPerSessionRootObject([&](const sockaddr* addr, - socklen_t len) { + 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: @@ -684,7 +785,7 @@ public: }), }; - 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; @@ -752,7 +853,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 +892,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({}); @@ -928,7 +1036,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)); @@ -1248,9 +1362,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; + std::unique_lock<std::mutex> _l(cb->mMutex); + cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); }); + } EXPECT_EQ(cb->mValues.size(), 1) << "callIsOneway: " << callIsOneway @@ -1270,6 +1389,12 @@ TEST_P(BinderRpc, Callbacks) { // need to manually shut it down EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true)); + proc.proc.host.setCustomExitStatusCheck([](int wstatus) { + // Flaky. Sometimes gets SIGABRT. + EXPECT_TRUE((WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) || + (WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)) + << "server process failed: " << WaitStatusToString(wstatus); + }); proc.expectAlreadyShutdown = true; } } @@ -1299,6 +1424,10 @@ 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; } } @@ -1322,9 +1451,150 @@ TEST_P(BinderRpc, 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) { auto proc = createRpcTestSocketServerProcess({}); @@ -1412,9 +1682,20 @@ 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())), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject @@ -1468,9 +1749,10 @@ private: bool mValue = false; }; -TEST_P(BinderRpcSimple, Shutdown) { +TEST_P(BinderRpcServerOnly, Shutdown) { auto addr = allocateSocketAddress(); - auto server = RpcServer::make(newFactory(GetParam())); + auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); + server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared<OneOffSignal>(); @@ -1540,12 +1822,17 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } -INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), - BinderRpcSimple::PrintTestParam); +INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, + ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testVersions())), + BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: - using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; + // Only parameterized only server version because `RpcSession` is bypassed + // in the client half of the tests. + using Param = + std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. @@ -1557,8 +1844,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"; @@ -1676,7 +1964,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 +1976,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"; @@ -1708,8 +1998,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, false); if (readStatus != OK) { return AssertionFailure() << statusToString(readStatus); } @@ -1757,23 +2048,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,7 +2077,8 @@ 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); } }; @@ -1817,7 +2114,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 +2133,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 +2158,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 +2179,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 +2208,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 +2220,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 " @@ -1962,23 +2268,24 @@ INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest - : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { + : public testing::TestWithParam< + std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); + (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { - auto [socketType, certificateFormat, keyFormat] = info.param; - auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + - "_key_" + PrintToString(keyFormat); - return ret; + auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; + return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { - auto [socketType, certificateFormat, keyFormat] = GetParam(); + auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { @@ -1993,8 +2300,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 +2320,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/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index a807afa926..3dab2c748b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -233,12 +233,17 @@ 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 == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL, - "you better update this test!"); + "If the binder wire protocol is updated, this test should test additional versions. " + "The binder wire protocol should only be updated on upstream AOSP."); TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) { if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { diff --git a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h index 094addd690..50d12c408f 100644 --- a/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h +++ b/libs/binder/tests/include_tls_test_utils/binder/RpcTlsTestUtils.h @@ -18,6 +18,7 @@ #include <memory> #include <mutex> +#include <vector> #include <binder/RpcAuth.h> #include <binder/RpcCertificateFormat.h> diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 38bde3a011..2ca6ebdbd2 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -29,7 +29,6 @@ cc_fuzz { "libcutils", "libhidlbase", "liblog", - "libutils", ], target: { @@ -37,12 +36,14 @@ cc_fuzz { shared_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, host: { static_libs: [ "libbinder_ndk", "libbinder", + "libutils", ], }, darwin: { @@ -58,6 +59,7 @@ cc_fuzz { cc_library_static { name: "libbinder_random_parcel", host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, @@ -66,10 +68,13 @@ cc_library_static { srcs: [ "random_fd.cpp", "random_parcel.cpp", + "libbinder_driver.cpp", + "libbinder_ndk_driver.cpp", ], shared_libs: [ "libbase", "libbinder", + "libbinder_ndk", "libcutils", "libutils", ], diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 13f7195eb7..7059d30bb4 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -73,20 +73,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,20 +102,24 @@ 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"; }, PARCEL_READ_NO_STATUS(size_t, allowFds), PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), - [] (const ::android::Parcel& p, uint8_t len) { - std::string interface(len, 'a'); + PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), + PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), + [] (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()); @@ -123,13 +127,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"); @@ -147,13 +154,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); @@ -163,7 +170,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); @@ -261,13 +268,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); @@ -282,7 +289,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; @@ -301,15 +308,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; }, @@ -317,20 +325,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(); @@ -338,7 +345,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(); diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 5aeb5cc6f2..26d67704b2 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -70,7 +70,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 +80,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; }, diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h index cf24ab932b..81e79b5382 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h @@ -43,6 +43,10 @@ public: return aParcel()->get()->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: ndk::ScopedAParcel mParcel; }; 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/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h new file mode 100644 index 0000000000..a9a6197439 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h @@ -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. + */ + +#pragma once + +#include <binder/IBinder.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { +/** + * Based on the random data in provider, construct an arbitrary number of + * Parcel objects and send them to the service in serial. + * + * Usage: + * + * extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + * FuzzedDataProvider provider = FuzzedDataProvider(data, size); + * // can use provider here to create a service with different options + * sp<IFoo> myService = sp<IFoo>::make(...); + * fuzzService(myService, std::move(provider)); + * } + */ +void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider); +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h new file mode 100644 index 0000000000..f2b782337c --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h @@ -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. + */ + +#pragma once + +#include <android/binder_parcel.h> +#include <fuzzer/FuzzedDataProvider.h> + +namespace android { +/** + * Based on the random data in provider, construct an arbitrary number of + * Parcel objects and send them to the service in serial. + * + * Usage: + * + * extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + * FuzzedDataProvider provider = FuzzedDataProvider(data, size); + * // can use provider here to create a service with different options + * std::shared_ptr<IFoo> myService = ndk::SharedRefBase<IFoo>::make(...); + * fuzzService(myService->asBinder().get(), std::move(provider)); + * } + */ +void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider); +} // namespace android 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 0a083d7665..843b6e3011 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 @@ -16,12 +16,13 @@ #pragma once +#include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> namespace android { -// ownership to callee, always valid or aborts +// always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -int getRandomFd(FuzzedDataProvider* provider); +base::unique_fd getRandomFd(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 749bf212e6..459fb127c2 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 @@ -19,13 +19,24 @@ #include <binder/Parcel.h> #include <fuzzer/FuzzedDataProvider.h> +#include <functional> +#include <vector> + namespace android { + +struct RandomParcelOptions { + std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader; + std::vector<sp<IBinder>> extraBinders; + std::vector<base::unique_fd> extraFds; +}; + /** * Fill parcel data, including some random binder objects and FDs + * + * 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); -/** - * Fill parcel data, but don't fill any objects. - */ -void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp new file mode 100644 index 0000000000..d5aa353af0 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -0,0 +1,65 @@ +/* + * 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 <fuzzbinder/random_parcel.h> + +namespace android { + +void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { + sp<IBinder> target; + + RandomParcelOptions options{ + .extraBinders = {binder}, + .extraFds = {}, + }; + + while (provider.remaining_bytes() > 0) { + uint32_t code = provider.ConsumeIntegral<uint32_t>(); + uint32_t flags = provider.ConsumeIntegral<uint32_t>(); + Parcel data; + + sp<IBinder> target = options.extraBinders.at( + provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); + options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) { + // most code will be behind checks that the head of the Parcel + // is exactly this, so make it easier for fuzzers to reach this + if (provider.ConsumeBool()) { + p->writeInterfaceToken(target->getInterfaceDescriptor()); + } + }; + + 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); + + Parcel reply; + (void)target->transact(code, data, &reply, flags); + + // feed back in binders and fds that are returned from the service, so that + // we can fuzz those binders, and use the fds and binders to feed back into + // the binders + auto retBinders = reply.debugReadAllStrongBinders(); + options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(), + retBinders.end()); + auto retFds = reply.debugReadAllFileDescriptors(); + for (size_t i = 0; i < retFds.size(); i++) { + options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); + } + } +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp new file mode 100644 index 0000000000..462ef9a5e9 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp @@ -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. + */ +#include <fuzzbinder/libbinder_ndk_driver.h> + +#include <fuzzbinder/libbinder_driver.h> +#include <fuzzbinder/random_parcel.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/ibinder_internal.h" + +namespace android { + +void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) { + fuzzService(binder->getBinder(), std::move(provider)); +} + +} // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index f435dae659..180a177689 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -83,22 +83,36 @@ 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())); + + P p0, p1; + fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size())); + fillRandomParcel(&p1, std::move(provider)); + + FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "start: " << start << " len: " << len; + + p0.appendFrom(&p1, start, len); +} + void* NothingClass_onCreate(void* args) { return args; } @@ -148,6 +162,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 cef6adb82d..ab0b7e306f 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,13 @@ namespace android { -int getRandomFd(FuzzedDataProvider* provider) { +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); }, })(); CHECK(fd >= 0); - return fd; + return base::unique_fd(fd); } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 8bf04ccae0..0204f5ed61 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -34,15 +34,30 @@ private: String16 mDescriptor; }; -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { +static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { + std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes()); + CHECK(OK == p->write(data.data(), data.size())); +} + +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, + const RandomParcelOptions& options) { if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); + + if (options.writeHeader) { + options.writeHeader(p, provider); + } + fillRandomParcelData(p, std::move(provider)); return; } + if (options.writeHeader) { + options.writeHeader(p, provider); + } + while (provider.remaining_bytes() > 0) { auto fillFunc = provider.PickValueInArray<const std::function<void()>>({ // write data @@ -54,8 +69,16 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { }, // write FD [&]() { - int fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/)); + if (options.extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options.extraFds.at( + provider.ConsumeIntegralInRange<size_t>(0, + 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*/)); + } }, // write binder [&]() { @@ -74,7 +97,15 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { // candidate for checking usage of an actual BpBinder return IInterface::asBinder(defaultServiceManager()); }, - []() { return nullptr; }, + [&]() -> sp<IBinder> { + if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { + return options.extraBinders.at( + provider.ConsumeIntegralInRange< + size_t>(0, options.extraBinders.size() - 1)); + } else { + return nullptr; + } + }, }); sp<IBinder> binder = makeFunc(); CHECK(OK == p->writeStrongBinder(binder)); @@ -85,9 +116,4 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) { } } -void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { - std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes()); - CHECK(OK == p->write(data.data(), data.size())); -} - } // namespace android diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h index 741987f754..5079431058 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h @@ -30,7 +30,6 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/threads.h> #include <stdio.h> diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h index 4a0aebac43..bf7c61347f 100644 --- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h @@ -27,7 +27,6 @@ #include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/threads.h> namespace android { diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 7e9bb7d468..d1690437ef 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -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; diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 1513ecafc8..45a6d47bb4 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -31,6 +31,7 @@ #include <android-base/unique_fd.h> #include <bpf/BpfMap.h> #include <cputimeinstate.h> +#include <cutils/android_filesystem_config.h> #include <libbpf.h> namespace android { @@ -219,6 +220,7 @@ TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { uint32_t totalFreqsCount = totalTimes.size(); std::vector<uint64_t> allUidTimes(totalFreqsCount, 0); for (auto const &[uid, uidTimes]: *allUid) { + if (uid == AID_SDK_SANDBOX) continue; for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) { allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx]; } @@ -646,5 +648,55 @@ TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { } } +void *forceSwitchWithUid(void *uidPtr) { + if (!uidPtr) return nullptr; + setuid(*(uint32_t *)uidPtr); + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + return nullptr; +} + +TEST_F(TimeInStateTest, SdkSandboxUid) { + // Find an unused app UID and its corresponding SDK sandbox uid. + uint32_t appUid = AID_APP_START, sandboxUid; + { + auto times = getUidsCpuFreqTimes(); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + for (const auto &kv : *times) { + if (kv.first > AID_APP_END) break; + appUid = std::max(appUid, kv.first); + } + appUid++; + sandboxUid = appUid + (AID_SDK_SANDBOX_PROCESS_START - AID_APP_START); + } + + // Create a thread to run with the fake sandbox uid. + pthread_t thread; + ASSERT_EQ(pthread_create(&thread, NULL, &forceSwitchWithUid, &sandboxUid), 0); + pthread_join(thread, NULL); + + // Confirm we recorded stats for appUid and AID_SDK_SANDBOX but not sandboxUid + auto allTimes = getUidsCpuFreqTimes(); + ASSERT_TRUE(allTimes.has_value()); + ASSERT_FALSE(allTimes->empty()); + ASSERT_NE(allTimes->find(appUid), allTimes->end()); + ASSERT_NE(allTimes->find(AID_SDK_SANDBOX), allTimes->end()); + ASSERT_EQ(allTimes->find(sandboxUid), allTimes->end()); + + auto allConcurrentTimes = getUidsConcurrentTimes(); + ASSERT_TRUE(allConcurrentTimes.has_value()); + ASSERT_FALSE(allConcurrentTimes->empty()); + ASSERT_NE(allConcurrentTimes->find(appUid), allConcurrentTimes->end()); + ASSERT_NE(allConcurrentTimes->find(AID_SDK_SANDBOX), allConcurrentTimes->end()); + ASSERT_EQ(allConcurrentTimes->find(sandboxUid), allConcurrentTimes->end()); + + ASSERT_TRUE(clearUidTimes(appUid)); +} + } // namespace bpf } // namespace android diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp index acda402993..09fbdea0e8 100644 --- a/libs/dumputils/Android.bp +++ b/libs/dumputils/Android.bp @@ -26,6 +26,7 @@ cc_library { shared_libs: [ "libbase", + "libbinder", "libhidlbase", "liblog", "libutils", @@ -33,7 +34,10 @@ cc_library { srcs: ["dump_utils.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], export_include_dirs: [ "include", diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 29c788bca3..e5439174a9 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -20,6 +20,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android/hidl/manager/1.0/IServiceManager.h> +#include <binder/IServiceManager.h> #include <dumputils/dump_utils.h> #include <log/log.h> @@ -52,8 +53,8 @@ static const char* debuggable_native_processes_to_dump[] = { NULL, }; -/* list of hal interface to dump containing process during native dumps */ -static const char* hal_interfaces_to_dump[] { +/* list of hidl hal interface to dump containing process during native dumps */ +static const char* hidl_hal_interfaces_to_dump[] { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", @@ -82,6 +83,23 @@ static const char* hal_interfaces_to_dump[] { NULL, }; +/* list of hal interface to dump containing process during native dumps */ +static const std::vector<std::string> aidl_interfaces_to_dump { + "android.hardware.automotive.audiocontrol.IAudioControl", + "android.hardware.automotive.evs.IEvsEnumerator", + "android.hardware.biometrics.face.IBiometricsFace", + "android.hardware.biometrics.fingerprint.IBiometricsFingerprint", + "android.hardware.camera.provider.ICameraProvider", + "android.hardware.drm.IDrmFactory", + "android.hardware.graphics.allocator.IAllocator", + "android.hardware.graphics.composer3.IComposer", + "android.hardware.health.IHealth", + "android.hardware.neuralnetworks.IDevice", + "android.hardware.power.IPower", + "android.hardware.power.stats.IPowerStats", + "android.hardware.sensors.ISensors", +}; + /* list of extra hal interfaces to dump containing process during native dumps */ // This is filled when dumpstate is called. static std::set<const std::string> extra_hal_interfaces_to_dump; @@ -104,7 +122,7 @@ static void read_extra_hals_to_dump_from_property() { // check if interface is included in either default hal list or extra hal list bool should_dump_hal_interface(const std::string& interface) { - for (const char** i = hal_interfaces_to_dump; *i; i++) { + for (const char** i = hidl_hal_interfaces_to_dump; *i; i++) { if (interface == *i) { return true; } @@ -130,14 +148,26 @@ bool should_dump_native_traces(const char* path) { return false; } -std::set<int> get_interesting_hal_pids() { +static void get_interesting_aidl_pids(std::set<int> &pids) { + using ServiceDebugInfo = android::IServiceManager::ServiceDebugInfo; + auto sm = android::defaultServiceManager(); + std::vector<ServiceDebugInfo> serviceDebugInfos = sm->getServiceDebugInfo(); + for (const auto & serviceDebugInfo : serviceDebugInfos) { + for (const auto &aidl_prefix : aidl_interfaces_to_dump) { + // Check for prefix match with aidl interface to dump + if (serviceDebugInfo.name.rfind(aidl_prefix, 0) == 0) { + pids.insert(serviceDebugInfo.pid); + } + } + } +} + +static void get_interesting_hidl_pids(std::set<int> &pids) { using android::hidl::manager::V1_0::IServiceManager; using android::sp; using android::hardware::Return; sp<IServiceManager> manager = IServiceManager::getService(); - std::set<int> pids; - read_extra_hals_to_dump_from_property(); Return<void> ret = manager->debugDump([&](auto& hals) { @@ -146,11 +176,9 @@ std::set<int> get_interesting_hal_pids() { continue; } - if (!should_dump_hal_interface(info.interfaceName)) { - continue; + if (should_dump_hal_interface(info.interfaceName)) { + pids.insert(info.pid); } - - pids.insert(info.pid); } }); @@ -158,7 +186,14 @@ std::set<int> get_interesting_hal_pids() { ALOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str()); } - return pids; // whether it was okay or not + return; +} + +std::set<int> get_interesting_pids() { + std::set<int> interesting_pids; + get_interesting_hidl_pids(interesting_pids); + get_interesting_aidl_pids(interesting_pids); + return interesting_pids; } bool IsZygote(int pid) { diff --git a/libs/dumputils/include/dumputils/dump_utils.h b/libs/dumputils/include/dumputils/dump_utils.h index 25f712733a..7c5329d01b 100644 --- a/libs/dumputils/include/dumputils/dump_utils.h +++ b/libs/dumputils/include/dumputils/dump_utils.h @@ -21,7 +21,7 @@ bool should_dump_native_traces(const char* path); -std::set<int> get_interesting_hal_pids(); +std::set<int> get_interesting_pids(); bool IsZygote(int pid); diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 61e4a98846..6c6d7f3641 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -94,4 +94,8 @@ status_t ServiceManager::unregisterForNotifications(const String16&, return INVALID_OPERATION; } +std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() { + std::vector<IServiceManager::ServiceDebugInfo> ret; + return ret; +} } // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h index 6d6e008c4f..e0af5d4ba8 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/ServiceManager.h @@ -20,6 +20,7 @@ #include <map> #include <optional> +#include <vector> namespace android { @@ -58,6 +59,9 @@ public: status_t unregisterForNotifications(const String16& name, const sp<LocalRegistrationCallback>& callback) override; + + std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override; + private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp index 81a529df7e..e2f072a7ab 100644 --- a/libs/gralloc/types/Gralloc4.cpp +++ b/libs/gralloc/types/Gralloc4.cpp @@ -196,35 +196,6 @@ status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output); status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType); /** - * Private helper functions - */ -template <class T> -status_t encodeInteger(const T& input, OutputHidlVec* output) { - static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value || - std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value || - std::is_same<T, float>::value || std::is_same<T, double>::value); - if (!output) { - return BAD_VALUE; - } - - const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input); - return output->encode(tmp, sizeof(input)); -} - -template <class T> -status_t decodeInteger(InputHidlVec* input, T* output) { - static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value || - std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value || - std::is_same<T, float>::value || std::is_same<T, double>::value); - if (!output) { - return BAD_VALUE; - } - - uint8_t* tmp = reinterpret_cast<uint8_t*>(output); - return input->decode(tmp, sizeof(*output)); -} - -/** * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper * function to turn T into the hidl_vec byte stream. * @@ -280,45 +251,10 @@ status_t encodeMetadata(const MetadataType& metadataType, const T& input, hidl_v template <class T> status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) { - OutputHidlVec outputHidlVec{output}; - - status_t err = encodeMetadataType(metadataType, &outputHidlVec); - if (err) { - return err; - } - - err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec); - if (err) { - return err; - } - - if (input) { - err = encodeHelper(*input, &outputHidlVec); - if (err) { - return err; - } - } - - err = outputHidlVec.resize(); - if (err) { - return err; - } - - err = encodeMetadataType(metadataType, &outputHidlVec); - if (err) { - return err; - } - - err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec); - if (err) { - return err; - } - - if (input) { - return encodeHelper(*input, &outputHidlVec); + if (!input) { + return NO_ERROR; } - - return NO_ERROR; + return encodeMetadata(metadataType, *input, output, encodeHelper); } /** @@ -379,36 +315,45 @@ status_t decodeOptionalMetadata(const MetadataType& metadataType, const hidl_vec if (!output) { return BAD_VALUE; } - - InputHidlVec inputHidlVec{&input}; - - status_t err = validateMetadataType(&inputHidlVec, metadataType); - if (err) { - return err; + if (input.size() <= 0) { + output->reset(); + return NO_ERROR; } - - uint32_t present = 0; - err = decodeInteger<uint32_t>(&inputHidlVec, &present); - if (err) { - return err; + T tmp; + status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper); + if (!err) { + *output = tmp; } + return err; +} - if (present) { - T tmp; - err = decodeHelper(&inputHidlVec, &tmp); - if (err) { - return err; - } - - *output = tmp; +/** + * Private helper functions + */ +template <class T> +status_t encodeInteger(const T& input, OutputHidlVec* output) { + static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value || + std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value || + std::is_same<T, float>::value || std::is_same<T, double>::value); + if (!output) { + return BAD_VALUE; } - err = inputHidlVec.hasRemainingData(); - if (err) { + const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input); + return output->encode(tmp, sizeof(input)); +} + +template <class T> +status_t decodeInteger(InputHidlVec* input, T* output) { + static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value || + std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value || + std::is_same<T, float>::value || std::is_same<T, double>::value); + if (!output) { return BAD_VALUE; } - return NO_ERROR; + uint8_t* tmp = reinterpret_cast<uint8_t*>(output); + return input->decode(tmp, sizeof(*output)); } status_t encodeString(const std::string& input, OutputHidlVec* output) { 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 bd9abc1996..6641b7777c 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -91,7 +91,7 @@ cc_library_static { ], aidl: { - export_aidl_headers: true + export_aidl_headers: true, }, include_dirs: [ @@ -308,7 +308,6 @@ filegroup { cc_defaults { name: "libgui_bufferqueue-defaults", - clang: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 77a883b332..bf275a5900 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -391,6 +391,71 @@ void DisplayState::merge(const DisplayState& other) { } } +void layer_state_t::sanitize(int32_t permissions) { + // TODO: b/109894387 + // + // SurfaceFlinger's renderer is not prepared to handle cropping in the face of arbitrary + // rotation. To see the problem observe that if we have a square parent, and a child + // of the same size, then we rotate the child 45 degrees around its center, the child + // must now be cropped to a non rectangular 8 sided region. + // + // Of course we can fix this in the future. For now, we are lucky, SurfaceControl is + // private API, and arbitrary rotation is used in limited use cases, for instance: + // - WindowManager only uses rotation in one case, which is on a top level layer in which + // cropping is not an issue. + // - Launcher, as a privileged app, uses this to transition an application to PiP + // (picture-in-picture) mode. + // + // However given that abuse of rotation matrices could lead to surfaces extending outside + // of cropped areas, we need to prevent non-root clients without permission + // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER + // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle + // preserving transformations. + if (what & eMatrixChanged) { + if (!(permissions & Permission::ROTATE_SURFACE_FLINGER)) { + ui::Transform t; + t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + if (!t.preserveRects()) { + what &= ~eMatrixChanged; + ALOGE("Stripped non rect preserving matrix in sanitize"); + } + } + } + + if (what & layer_state_t::eInputInfoChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eInputInfoChanged; + ALOGE("Stripped attempt to set eInputInfoChanged in sanitize"); + } + } + if (what & layer_state_t::eTrustedOverlayChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eTrustedOverlayChanged; + ALOGE("Stripped attempt to set eTrustedOverlay in sanitize"); + } + } + if (what & layer_state_t::eDropInputModeChanged) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eDropInputModeChanged; + ALOGE("Stripped attempt to set eDropInputModeChanged in sanitize"); + } + } + if (what & layer_state_t::eFrameRateSelectionPriority) { + if (!(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateSelectionPriority; + ALOGE("Stripped attempt to set eFrameRateSelectionPriority in sanitize"); + } + } + if (what & layer_state_t::eFrameRateChanged) { + if (!ValidateFrameRate(frameRate, frameRateCompatibility, + changeFrameRateStrategy, + "layer_state_t::sanitize", + permissions & Permission::ACCESS_SURFACE_FLINGER)) { + what &= ~eFrameRateChanged; // logged in ValidateFrameRate + } + } +} + void layer_state_t::merge(const layer_state_t& other) { if (other.what & ePositionChanged) { what |= ePositionChanged; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 725ea6571d..4bcc9d56c8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -551,6 +551,13 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mListenerCallbacks = other.mListenerCallbacks; } +void SurfaceComposerClient::Transaction::sanitize() { + for (auto & [handle, composerState] : mComposerStates) { + composerState.state.sanitize(0 /* permissionMask */); + } + mInputWindowCommands.clear(); +} + std::unique_ptr<SurfaceComposerClient::Transaction> SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { auto transaction = std::make_unique<Transaction>(); @@ -635,7 +642,6 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } - composerStates[surfaceControlHandle] = composerState; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 03e4aacdbe..2a8d30d2da 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -61,6 +61,12 @@ struct client_cache_t { * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { + enum Permission { + ACCESS_SURFACE_FLINGER = 0x1, + ROTATE_SURFACE_FLINGER = 0x2, + INTERNAL_SYSTEM_WINDOW = 0x4, + }; + enum { eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java eLayerOpaque = 0x02, // SURFACE_OPAQUE @@ -128,6 +134,7 @@ struct layer_state_t { status_t read(const Parcel& input); bool hasBufferChanges() const; bool hasValidBuffer() const; + void sanitize(int32_t permissions); struct matrix22_t { float dsdx{0}; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0d1d1a37bd..76b6d44fd2 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -590,6 +590,14 @@ public: void setAnimationTransaction(); void setEarlyWakeupStart(); void setEarlyWakeupEnd(); + + /** + * Strip the transaction of all permissioned requests, required when + * accepting transactions across process boundaries. + * + * TODO (b/213644870): Remove all permissioned things from Transaction + */ + void sanitize(); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 3d26c3d858..abdbaa5074 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", @@ -62,7 +61,7 @@ cc_test { "libinput", "libui", "libutils", - "libnativewindow" + "libnativewindow", ], header_libs: ["libsurfaceflinger_headers"], @@ -73,7 +72,6 @@ cc_test { name: "libgui_multilib_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -100,7 +98,6 @@ cc_test { name: "SurfaceParcelable_test", test_suites: ["device-tests"], - clang: true, cflags: [ "-Wall", "-Werror", @@ -117,7 +114,7 @@ cc_test { "libgui", "libui", "libutils", - "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui. + "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui. "libpdx_default_transport", ], @@ -129,7 +126,6 @@ cc_test { cc_test { name: "SamplingDemo", - clang: true, cflags: [ "-Wall", "-Werror", @@ -146,5 +142,5 @@ cc_test { "liblog", "libui", "libutils", - ] + ], } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index e73c3b8518..cdcdaabfb1 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -55,8 +55,6 @@ cc_library { "VirtualKeyMap.cpp", ], - clang: true, - header_libs: ["jni_headers"], export_header_lib_headers: ["jni_headers"], @@ -64,6 +62,7 @@ cc_library { "libbase", "liblog", "libcutils", + "libvintf", ], static_libs: [ @@ -113,7 +112,7 @@ cc_library { "frameworks/native/libs/arect/include", ], }, - linux_glibc: { + host_linux: { srcs: [ "InputTransport.cpp", "android/os/IInputConstants.aidl", diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 1aec477081..18cd474058 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -53,33 +53,39 @@ static void appendInputDeviceConfigurationFileRelativePath(std::string& path, } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - std::string versionPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type); + std::string versionPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%" + "04x_Version_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + deviceIdentifier.version, + suffix), + type); if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - std::string productPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type); + std::string productPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + suffix), + type); if (!productPath.empty()) { return productPath; } } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix, + type); } std::string getInputDeviceConfigurationFilePathByName( diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index c365ab070e..84a8ab42a1 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -23,20 +23,33 @@ #include <input/InputEventLabels.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> #include <utils/Timers.h> #include <utils/Tokenizer.h> +#include <vintf/RuntimeInfo.h> +#include <vintf/VintfObject.h> -// Enables debug output for the parser. -#define DEBUG_PARSER 0 +#include <cstdlib> +#include <string_view> +#include <unordered_map> + +/** + * Log debug output for the parser. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart) + */ +const bool DEBUG_PARSER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO); // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - +/** + * Log debug output for mapping. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart) + */ +const bool DEBUG_MAPPING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO); namespace android { @@ -62,6 +75,29 @@ static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; +bool kernelConfigsArePresent(const std::set<std::string>& configs) { + std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo = + android::vintf::VintfObject::GetInstance()->getRuntimeInfo( + vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); + LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched"); + + const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs(); + for (const std::string& requiredConfig : configs) { + const auto configIt = kernelConfigs.find(requiredConfig); + if (configIt == kernelConfigs.end()) { + ALOGI("Required kernel config %s is not found", requiredConfig.c_str()); + return false; + } + const std::string& option = configIt->second; + if (option != "y" && option != "m") { + ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(), + option.c_str()); + return false; + } + } + return true; +} + // --- KeyLayoutMap --- KeyLayoutMap::KeyLayoutMap() { @@ -72,32 +108,34 @@ KeyLayoutMap::~KeyLayoutMap() { base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key layout map.", status); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr<Tokenizer> t(tokenizer); - auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; - } - return ret; + return load(filename, contents); } -base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) { +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename, + const char* contents) { Tokenizer* tokenizer; - status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + status_t status; + if (contents == nullptr) { + status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + } else { + status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + } if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); } std::unique_ptr<Tokenizer> t(tokenizer); auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + if (!ret.ok()) { + return ret; } + const std::shared_ptr<KeyLayoutMap>& map = *ret; + LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); + if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) { + ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str()); + return Errorf("Missing kernel config"); + } + map->mLoadFileName = filename; return ret; } @@ -130,9 +168,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const { const Key* key = getKey(scanCode, usageCode); if (!key) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, + usageCode); *outKeyCode = AKEYCODE_UNKNOWN; *outFlags = 0; return NAME_NOT_FOUND; @@ -141,10 +178,9 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, *outKeyCode = key->keyCode; *outFlags = key->flags; -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); return NO_ERROR; } @@ -152,103 +188,79 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) { auto it = mSensorsByAbsCode.find(absCode); if (it == mSensorsByAbsCode.end()) { -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode); return Errorf("Can't find abs code {}.", absCode); } const Sensor& sensor = it->second; - -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, - NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + NamedEnum::string(sensor.sensorType).c_str(), sensor.sensorDataIndex); return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); + auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + return &it->second; } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); + auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + return &it->second; } } return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey( - int32_t keyCode, std::vector<int32_t>* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; i<N; i++) { - if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->push_back(mKeysByScanCode.keyAt(i)); +std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { + std::vector<int32_t> scanCodes; + for (const auto& [scanCode, key] : mKeysByScanCode) { + if (keyCode == key.keyCode) { + scanCodes.push_back(scanCode); } } - return NO_ERROR; + return scanCodes; } -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); -#endif - return NAME_NOT_FOUND; +std::optional<AxisInfo> KeyLayoutMap::mapAxis(int32_t scanCode) const { + auto it = mAxes.find(scanCode); + if (it == mAxes.end()) { + ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); + return std::nullopt; } - *outAxisInfo = mAxes.valueAt(index); - -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); -#endif - return NO_ERROR; + const AxisInfo& axisInfo = it->second; + ALOGD_IF(DEBUG_MAPPING, + "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + return axisInfo; } -status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { - const size_t N = mLedsByScanCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { - *outScanCode = mLedsByScanCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); -#endif - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const { + for (const auto& [scanCode, led] : mLedsByScanCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode); + return scanCode; } } -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); -#endif - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } -status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { - const size_t N = mLedsByUsageCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { - *outUsageCode = mLedsByUsageCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); -#endif - return NO_ERROR; +std::optional<int32_t> KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const { + for (const auto& [usageCode, led] : mLedsByUsageCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode); + return usageCode; } } -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); -#endif - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } - // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : @@ -260,10 +272,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); mTokenizer->skipDelimiters(WHITESPACE); @@ -285,6 +295,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseSensor(); if (status) return status; + } else if (keywordToken == "requires_kernel_config") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseRequiredKernelConfig(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -321,8 +335,9 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Key>& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -357,14 +372,13 @@ status_t KeyLayoutMap::Parser::parseKey() { flags |= flag; } -#if DEBUG_PARSER - ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", code, keyCode, flags); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); + Key key; key.keyCode = keyCode; key.flags = flags; - map.add(code, key); + map.insert({code, key}); return NO_ERROR; } @@ -377,7 +391,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { scanCodeToken.string()); return BAD_VALUE; } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; @@ -458,14 +472,12 @@ status_t KeyLayoutMap::Parser::parseAxis() { } } -#if DEBUG_PARSER - ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - axisInfo.mode, axisInfo.axis, axisInfo.highAxis, - axisInfo.splitValue, axisInfo.flatOverride); -#endif - mMap->mAxes.add(scanCode, axisInfo); + ALOGD_IF(DEBUG_PARSER, + "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + mMap->mAxes.insert({scanCode, axisInfo}); return NO_ERROR; } @@ -485,8 +497,9 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } - KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map<int32_t, Led>& map = + mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -501,14 +514,12 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } -#if DEBUG_PARSER - ALOGD("Parsed led %s: code=%d, ledCode=%d.", - mapUsage ? "usage" : "scan code", code, ledCode); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code", + code, ledCode); Led led; led.ledCode = ledCode; - map.add(code, led); + map.insert({code, led}); return NO_ERROR; } @@ -580,10 +591,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { } int32_t sensorDataIndex = indexOpt.value(); -#if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, - NamedEnum::string(sensorType).c_str(), sensorDataIndex); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + NamedEnum::string(sensorType).c_str(), sensorDataIndex); Sensor sensor; sensor.sensorType = sensorType; @@ -591,4 +600,23 @@ status_t KeyLayoutMap::Parser::parseSensor() { map.emplace(code, sensor); return NO_ERROR; } + +// Parse the name of a required kernel config. +// The layout won't be used if the specified kernel config is not present +// Examples: +// requires_kernel_config CONFIG_HID_PLAYSTATION +status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + std::string configName = codeToken.string(); + + const auto result = mMap->mRequiredKernelConfigs.emplace(configName); + if (!result.second) { + ALOGE("%s: Duplicate entry for required kernel config %s.", + mTokenizer->getLocation().string(), configName.c_str()); + return BAD_VALUE; + } + + ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); + return NO_ERROR; +} }; diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index f0895b32ef..c3f5151fd1 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -20,16 +20,23 @@ #include <unistd.h> #include <limits.h> -#include <input/Keyboard.h> +#include <input/InputDevice.h> #include <input/InputEventLabels.h> -#include <input/KeyLayoutMap.h> #include <input/KeyCharacterMap.h> -#include <input/InputDevice.h> +#include <input/KeyLayoutMap.h> +#include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> namespace android { +static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, + InputDeviceConfigurationFileType type) { + return name.empty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + // --- KeyMap --- KeyMap::KeyMap() { @@ -111,11 +118,25 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, } base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + if (ret.ok()) { + keyLayoutMap = *ret; + keyLayoutFile = path; + return OK; + } + + // Try to load fallback layout if the regular layout could not be loaded due to missing + // kernel modules + std::string fallbackPath( + getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, + InputDeviceConfigurationFileType:: + KEY_LAYOUT, + "_fallback")); + ret = KeyLayoutMap::load(fallbackPath); if (!ret.ok()) { return ret.error().code(); } keyLayoutMap = *ret; - keyLayoutFile = path; + keyLayoutFile = fallbackPath; return OK; } @@ -137,14 +158,6 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return OK; } -std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type) { - return name.empty() - ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) - : getInputDeviceConfigurationFilePathByName(name, type); -} - - // --- Global functions --- bool isKeyboardSpecialFunction(const PropertyMap* config) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 6ffe8518b6..d947cd99e8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -36,8 +36,9 @@ cc_test { "liblog", "libui", "libutils", + "libvintf", ], - data: ["data/*.kcm"], + data: ["data/*"], test_suites: ["device-tests"], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index 61e88df11d..e872fa442b 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -64,13 +64,11 @@ protected: mKeyMap.keyCharacterMapFile = path; } - virtual void SetUp() override { + void SetUp() override { loadKeyLayout("Generic"); loadKeyCharacterMap("Generic"); } - virtual void TearDown() override {} - KeyMap mKeyMap; }; @@ -132,4 +130,20 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); } +TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; + // We assert error message here because it's used by 'validatekeymaps' tool + ASSERT_EQ("Missing kernel config", ret.error().message()); +} + +TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath; + const std::shared_ptr<KeyLayoutMap>& map = *ret; + ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present"; +} + } // namespace android diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl new file mode 100644 index 0000000000..2d0a507fbd --- /dev/null +++ b/libs/input/tests/data/kl_with_required_fake_config.kl @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This KL should not be loaded unless the below config is present in the kernel +# This config will never exist, and therefore this KL should never be loaded +requires_kernel_config CONFIG_HID_FAKEMODULE + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl new file mode 100644 index 0000000000..303b23e48a --- /dev/null +++ b/libs/input/tests/data/kl_with_required_real_config.kl @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This KL should not be loaded unless the below config is present in the kernel +# The CONFIG_UHID option has been required for a while, and therefore it's safe +# to assume that this will always be loaded +requires_kernel_config CONFIG_UHID + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/libs/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/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index ef7602f24e..af55623ac5 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -207,7 +207,11 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, if (result == 0) { outPlanes->planeCount = 3; outPlanes->planes[0].data = yuvData.y; - outPlanes->planes[0].pixelStride = 1; + if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010) { + outPlanes->planes[0].pixelStride = 2; + } else { + outPlanes->planes[0].pixelStride = 1; + } outPlanes->planes[0].rowStride = yuvData.ystride; outPlanes->planes[1].data = yuvData.cb; outPlanes->planes[1].pixelStride = yuvData.chroma_step; diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 928600999c..f7d2451462 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -64,7 +64,7 @@ cc_library { symbol_file: "libnativewindow.map.txt", unversioned: true, override_export_include_dirs: [ - "include" + "include", ], }, export_include_dirs: [ @@ -72,8 +72,6 @@ cc_library { "include-private", ], - clang: true, - cflags: [ "-Wall", "-Werror", diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 570c7bc08d..e8a4146a4c 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -95,7 +95,7 @@ filegroup { "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/LinearEffect.cpp", - "skia/filters/StretchShaderFactory.cpp" + "skia/filters/StretchShaderFactory.cpp", ], } @@ -103,7 +103,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/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 506e308370..2f4f1111b2 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -31,7 +31,6 @@ license { cc_defaults { name: "libui-defaults", - clang: true, cflags: [ "-Wall", "-Werror", @@ -105,7 +104,6 @@ cc_library_shared { }, double_loadable: true, - clang: true, cflags: [ "-Wall", "-Werror", diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 80f6c82bb0..162fd95945 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -300,20 +300,19 @@ status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, cons if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) { continue; } - if (0 != planeLayoutComponent.offsetInBits % 8) { - unlock(bufferHandle); - return BAD_VALUE; - } - uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes + - (planeLayoutComponent.offsetInBits / 8); + uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes; + + // Note that `offsetInBits` may not be a multiple of 8 for packed formats (e.g. P010) + // but we still want to point to the start of the first byte. + tmpData += (planeLayoutComponent.offsetInBits / 8); + uint64_t sampleIncrementInBytes; auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value); switch (type) { case PlaneLayoutComponentType::Y: - if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) || - (planeLayout.sampleIncrementInBits != 8)) { + if ((ycbcr.y != nullptr) || (planeLayout.sampleIncrementInBits % 8 != 0)) { unlock(bufferHandle); return BAD_VALUE; } @@ -329,7 +328,8 @@ status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, cons } sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8; - if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) { + if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2) && + (sampleIncrementInBytes != 4)) { unlock(bufferHandle); return BAD_VALUE; } diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 516aad824e..b780770b86 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -93,7 +93,6 @@ cc_test { cflags: ["-Wall", "-Werror"], shared_libs: [ "libbinder", - "libgui", "liblog", "libui", "libutils", diff --git a/libs/ui/tests/GraphicBufferOverBinder_test.cpp b/libs/ui/tests/GraphicBufferOverBinder_test.cpp index 126a945a0a..4c9d574ef8 100644 --- a/libs/ui/tests/GraphicBufferOverBinder_test.cpp +++ b/libs/ui/tests/GraphicBufferOverBinder_test.cpp @@ -20,9 +20,6 @@ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <gtest/gtest.h> -#include <gui/BufferQueue.h> -#include <gui/IGraphicBufferConsumer.h> -#include <gui/IGraphicBufferProducer.h> #include <ui/GraphicBuffer.h> #include <utils/Log.h> 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/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index d4538f12a2..e72ca74ba7 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -9,7 +9,7 @@ package { cc_library_static { name: "libbroadcastring", - clang: true, + cflags: [ "-Wall", "-Wextra", @@ -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/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index bf848af262..06ecd258ae 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -31,7 +31,7 @@ sourceFiles = [ "vr_flinger.cpp", ] -includeFiles = [ "include" ] +includeFiles = ["include"] staticLibraries = [ "libdisplay", @@ -83,8 +83,6 @@ headerLibraries = [ cc_library_static { srcs: sourceFiles, export_include_dirs: includeFiles, - - clang: true, cflags: [ "-DLOG_TAG=\"vr_flinger\"", "-DTRACE=0", diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp index a8a847664f..de7d8f879b 100644 --- a/libs/vr/libvrflinger/vr_flinger.cpp +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -17,6 +17,7 @@ #include <processgroup/sched_policy.h> #include <sys/prctl.h> #include <sys/resource.h> +#include <utils/ThreadDefs.h> #include <functional> 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 de36a7aea6..8363104704 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1448,7 +1448,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri setError(EGL_BAD_SURFACE, EGL_FALSE); } int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (attribute == EGL_TIMESTAMPS_ANDROID) { @@ -1458,7 +1462,11 @@ EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attri return EGL_TRUE; } int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (err != 0) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } else if (!s->cnx->useAngle) { + return EGL_TRUE; + } // else if ANGLE, fall through to the call to the driver (i.e. ANGLE) below } if (s->setSmpte2086Attribute(attribute, value)) { diff --git a/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/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..36ae179ab7 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()); } 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..3c7644f369 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,10 @@ 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()); + + // 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 +86,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/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 166f358598..26c33d2a9d 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -449,8 +449,7 @@ bool EventHub::Device::hasKeycodeLocked(int keycode) const { return false; } - std::vector<int32_t> scanCodes; - keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + std::vector<int32_t> scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode); const size_t N = scanCodes.size(); for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; @@ -545,10 +544,10 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } - int32_t scanCode; - if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { - *outScanCode = scanCode; + std::optional<int32_t> scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led); + if (scanCode.has_value()) { + if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) { + *outScanCode = *scanCode; return NO_ERROR; } } @@ -862,8 +861,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + std::vector<int32_t> scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode); if (scanCodes.size() != 0) { if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { @@ -921,20 +919,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - std::vector<int32_t> scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], - &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { - outFlags[codeIndex] = 1; - break; - } + std::vector<int32_t> scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); + + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (device->keyBitmask.test(scanCodes[sc])) { + outFlags[codeIndex] = 1; + break; } } } @@ -988,14 +982,15 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } + if (device == nullptr || !device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; } - - return NAME_NOT_FOUND; + std::optional<AxisInfo> info = device->keyMap.keyLayoutMap->mapAxis(scanCode); + if (!info.has_value()) { + return NAME_NOT_FOUND; + } + *outAxisInfo = *info; + return NO_ERROR; } base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId, diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index b419d9ab3d..969c8ba474 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1488,15 +1488,14 @@ protected: sp<TestInputListener> mFakeListener; sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakeEventHub> mFakeEventHub; - std::unique_ptr<InstrumentedInputReader> mReader; + sp<InstrumentedInputReader> mReader; void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + mReader = sp<InstrumentedInputReader>::make(mFakeEventHub, mFakePolicy, mFakeListener); } void TearDown() override { @@ -2407,15 +2406,14 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; - std::unique_ptr<InstrumentedInputReader> mReader; + sp<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + mReader = sp<InstrumentedInputReader>::make(mFakeEventHub, mFakePolicy, mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; @@ -2680,15 +2678,14 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; - std::unique_ptr<InstrumentedInputReader> mReader; + sp<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; virtual void SetUp(Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + mReader = sp<InstrumentedInputReader>::make(mFakeEventHub, mFakePolicy, mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } @@ -8987,15 +8984,14 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; - std::unique_ptr<InstrumentedInputReader> mReader; + sp<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; virtual void SetUp(Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, - mFakeListener); + mReader = sp<InstrumentedInputReader>::make(mFakeEventHub, mFakePolicy, mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index df4db199f8..455a1e2133 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -42,4 +42,7 @@ cc_fuzz { srcs: [ "LatencyTrackerFuzzer.cpp", ], + fuzz_config: { + cc: ["android-framework-input@google.com"], + }, } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index da7ff71510..f94917cf1c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1608,8 +1608,10 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; + const float parentShadowRadius = + newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - newParent->mEffectiveShadowRadius); + parentShadowRadius); } } diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index 691676420c..1834f2a7ad 100644 --- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -94,7 +94,8 @@ TEST_F(OneShotTimerTest, resetTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, resetBackToBackTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), diff --git a/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/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/driver.cpp b/vulkan/libvulkan/driver.cpp index cf774fd9b8..0c5d61b8d5 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -624,8 +624,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { switch (ext_bit) { case ProcHook::KHR_android_surface: case ProcHook::KHR_surface: + case ProcHook::KHR_surface_protected_capabilities: case ProcHook::EXT_swapchain_colorspace: case ProcHook::KHR_get_surface_capabilities2: + case ProcHook::GOOGLE_surfaceless_query: hook_extensions_.set(ext_bit); // return now as these extensions do not require HAL support return; @@ -701,8 +703,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_external_fence_capabilities: case ProcHook::KHR_get_surface_capabilities2: case ProcHook::KHR_surface: + case ProcHook::KHR_surface_protected_capabilities: case ProcHook::EXT_debug_report: case ProcHook::EXT_swapchain_colorspace: + case ProcHook::GOOGLE_surfaceless_query: case ProcHook::ANDROID_native_buffer: case ProcHook::EXTENSION_CORE_1_0: case ProcHook::EXTENSION_CORE_1_1: @@ -913,6 +917,9 @@ VkResult EnumerateInstanceExtensionProperties( loader_extensions.push_back({ VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}); + loader_extensions.push_back( + {VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME, + VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION}); loader_extensions.push_back({ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_SPEC_VERSION}); @@ -922,6 +929,8 @@ VkResult EnumerateInstanceExtensionProperties( loader_extensions.push_back({ VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION}); + loader_extensions.push_back({VK_GOOGLE_SURFACELESS_QUERY_EXTENSION_NAME, + VK_GOOGLE_SURFACELESS_QUERY_SPEC_VERSION}); static const VkExtensionProperties loader_debug_report_extension = { VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION, diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 5f37a50a03..b436db1de7 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -565,11 +565,13 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata; if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace; if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing; + if (strcmp(name, "VK_GOOGLE_surfaceless_query") == 0) return ProcHook::GOOGLE_surfaceless_query; if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface; if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2; if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present; if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image; if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface; + if (strcmp(name, "VK_KHR_surface_protected_capabilities") == 0) return ProcHook::KHR_surface_protected_capabilities; if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain; if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer; if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2; diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 047e774004..688630c0a5 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -41,11 +41,13 @@ struct ProcHook { EXT_hdr_metadata, EXT_swapchain_colorspace, GOOGLE_display_timing, + GOOGLE_surfaceless_query, KHR_android_surface, KHR_get_surface_capabilities2, KHR_incremental_present, KHR_shared_presentable_image, KHR_surface, + KHR_surface_protected_capabilities, KHR_swapchain, ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 6191063894..e60625be89 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -614,42 +614,65 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice /*pdev*/, + VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities) { ATRACE_CALL(); int err; - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - int width, height; - err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - int transform_hint; - err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; - } - int max_buffer_count; - err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count); - if (err != android::OK) { - ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", - strerror(-err), err); - return VK_ERROR_SURFACE_LOST_KHR; + if (surface == VK_NULL_HANDLE) { + const InstanceData& instance_data = GetData(pdev); + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + // It is an error to pass a surface==VK_NULL_HANDLE unless the + // VK_GOOGLE_surfaceless_query extension is enabled + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this + // extension for this function is for + // VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following + // four values cannot be known without a surface. Default values will + // be supplied anyway, but cannot be relied upon. + width = 1000; + height = 1000; + transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + max_buffer_count = 10; + } else { + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + + err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, + &transform_hint); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + + err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, + &max_buffer_count); + if (err != android::OK) { + ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } } capabilities->minImageCount = std::min(max_buffer_count, 3); capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); @@ -690,23 +713,43 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); bool wide_color_support = false; - Surface& surface = *SurfaceFromHandle(surface_handle); - int err = native_window_get_wide_color_support(surface.window.get(), - &wide_color_support); - if (err) { - return VK_ERROR_SURFACE_LOST_KHR; - } - ALOGV("wide_color_support is: %d", wide_color_support); - wide_color_support = - wide_color_support && + uint64_t consumer_usage = 0; + bool colorspace_ext = instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); + if (surface_handle == VK_NULL_HANDLE) { + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The EGL loader + // unconditionally supports wide color formats, even if they will cause + // a SurfaceFlinger fallback. Based on that, wide_color_support will be + // set to true in this case. + wide_color_support = true; + + // TODO(b/203826952): research proper value; temporarily use the + // values seen on Pixel + consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; + } else { + Surface& surface = *SurfaceFromHandle(surface_handle); + int err = native_window_get_wide_color_support(surface.window.get(), + &wide_color_support); + if (err) { + return VK_ERROR_SURFACE_LOST_KHR; + } + ALOGV("wide_color_support is: %d", wide_color_support); + + consumer_usage = surface.consumer_usage; + } + wide_color_support = wide_color_support && colorspace_ext; AHardwareBuffer_Desc desc = {}; desc.width = 1; desc.height = 1; desc.layers = 1; - desc.usage = surface.consumer_usage | - AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + desc.usage = consumer_usage | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; // We must support R8G8B8A8 @@ -714,6 +757,11 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + if (colorspace_ext) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); + } + if (wide_color_support) { all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); @@ -721,6 +769,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); } + // NOTE: Any new formats that are added must be coordinated across different + // Android users. This includes the ANGLE team (a layered implementation of + // OpenGL-ES). + desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; if (AHardwareBuffer_isSupported(&desc)) { all_formats.emplace_back(VkSurfaceFormatKHR{ @@ -797,6 +849,12 @@ VkResult GetPhysicalDeviceSurfaceCapabilities2KHR( .supportedUsageFlags; } break; + case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: { + VkSurfaceProtectedCapabilitiesKHR* protected_caps = + reinterpret_cast<VkSurfaceProtectedCapabilitiesKHR*>(caps); + protected_caps->supportsProtected = VK_TRUE; + } break; + default: // Ignore all other extension structs break; @@ -848,31 +906,50 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); + std::vector<VkPresentModeKHR> present_modes; + if (surface == VK_NULL_HANDLE) { + const InstanceData& instance_data = GetData(pdev); + ProcHook::Extension surfaceless = ProcHook::GOOGLE_surfaceless_query; + bool surfaceless_enabled = + instance_data.hook_extensions.test(surfaceless); + if (!surfaceless_enabled) { + return VK_ERROR_SURFACE_LOST_KHR; + } + // Support for VK_GOOGLE_surfaceless_query. The primary purpose of this + // extension for this function is for + // 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. Therefore, don't return it. + present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); + } else { + ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &query_value); - if (err != android::OK || query_value < 0) { - ALOGE( - "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " - "value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; - } - uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " + "value=%d", + strerror(-err), err, query_value); + return VK_ERROR_SURFACE_LOST_KHR; + } + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + + err = + window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", + strerror(-err), err, query_value); + return VK_ERROR_SURFACE_LOST_KHR; + } + uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); - if (err != android::OK || query_value < 0) { - ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d", - strerror(-err), err, query_value); - return VK_ERROR_SURFACE_LOST_KHR; + if (min_undequeued_buffers + 1 < max_buffer_count) + present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); + present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } - uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - - std::vector<VkPresentModeKHR> present_modes; - if (min_undequeued_buffers + 1 < max_buffer_count) - present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); - present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); VkPhysicalDevicePresentationPropertiesANDROID present_properties; QueryPresentationProperties(pdev, &present_properties); 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/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index 6a73023193..af56764a21 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -27,11 +27,13 @@ _INTERCEPTED_EXTENSIONS = [ 'VK_EXT_hdr_metadata', 'VK_EXT_swapchain_colorspace', 'VK_GOOGLE_display_timing', + 'VK_GOOGLE_surfaceless_query', 'VK_KHR_android_surface', 'VK_KHR_get_surface_capabilities2', 'VK_KHR_incremental_present', 'VK_KHR_shared_presentable_image', 'VK_KHR_surface', + 'VK_KHR_surface_protected_capabilities', 'VK_KHR_swapchain', ] 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", diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 438e5dd10d..61e3859357 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -840,56 +840,52 @@ inline bool Iterate(Visitor* visitor, VkJsonDeviceGroup* device_group) { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { bool ret = true; - switch (device->properties.apiVersion ^ - VK_VERSION_PATCH(device->properties.apiVersion)) { - case VK_API_VERSION_1_2: - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_1: - ret &= - visitor->Visit("subgroupProperties", &device->subgroup_properties) && - visitor->Visit("pointClippingProperties", - &device->point_clipping_properties) && - visitor->Visit("multiviewProperties", - &device->multiview_properties) && - visitor->Visit("idProperties", &device->id_properties) && - visitor->Visit("maintenance3Properties", - &device->maintenance3_properties) && - visitor->Visit("16bitStorageFeatures", - &device->bit16_storage_features) && - visitor->Visit("multiviewFeatures", &device->multiview_features) && - visitor->Visit("variablePointerFeatures", - &device->variable_pointer_features) && - visitor->Visit("protectedMemoryFeatures", - &device->protected_memory_features) && - visitor->Visit("samplerYcbcrConversionFeatures", - &device->sampler_ycbcr_conversion_features) && - visitor->Visit("shaderDrawParameterFeatures", - &device->shader_draw_parameter_features) && - visitor->Visit("externalFenceProperties", - &device->external_fence_properties) && - visitor->Visit("externalSemaphoreProperties", - &device->external_semaphore_properties); - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_0: - ret &= visitor->Visit("properties", &device->properties) && - visitor->Visit("features", &device->features) && - visitor->Visit("memory", &device->memory) && - visitor->Visit("queues", &device->queues) && - visitor->Visit("extensions", &device->extensions) && - visitor->Visit("layers", &device->layers) && - visitor->Visit("formats", &device->formats); - if (device->ext_driver_properties.reported) { - ret &= visitor->Visit("VK_KHR_driver_properties", - &device->ext_driver_properties); - } - if (device->ext_variable_pointer_features.reported) { - ret &= visitor->Visit("VK_KHR_variable_pointers", - &device->ext_variable_pointer_features); - } - if (device->ext_shader_float16_int8_features.reported) { - ret &= visitor->Visit("VK_KHR_shader_float16_int8", - &device->ext_shader_float16_int8_features); - } + if (device->properties.apiVersion >= VK_API_VERSION_1_1) { + ret &= + visitor->Visit("subgroupProperties", &device->subgroup_properties) && + visitor->Visit("pointClippingProperties", + &device->point_clipping_properties) && + visitor->Visit("multiviewProperties", + &device->multiview_properties) && + visitor->Visit("idProperties", &device->id_properties) && + visitor->Visit("maintenance3Properties", + &device->maintenance3_properties) && + visitor->Visit("16bitStorageFeatures", + &device->bit16_storage_features) && + visitor->Visit("multiviewFeatures", &device->multiview_features) && + visitor->Visit("variablePointerFeatures", + &device->variable_pointer_features) && + visitor->Visit("protectedMemoryFeatures", + &device->protected_memory_features) && + visitor->Visit("samplerYcbcrConversionFeatures", + &device->sampler_ycbcr_conversion_features) && + visitor->Visit("shaderDrawParameterFeatures", + &device->shader_draw_parameter_features) && + visitor->Visit("externalFenceProperties", + &device->external_fence_properties) && + visitor->Visit("externalSemaphoreProperties", + &device->external_semaphore_properties); + } + if (device->properties.apiVersion >= VK_API_VERSION_1_0) { + ret &= visitor->Visit("properties", &device->properties) && + visitor->Visit("features", &device->features) && + visitor->Visit("memory", &device->memory) && + visitor->Visit("queues", &device->queues) && + visitor->Visit("extensions", &device->extensions) && + visitor->Visit("layers", &device->layers) && + visitor->Visit("formats", &device->formats); + if (device->ext_driver_properties.reported) { + ret &= visitor->Visit("VK_KHR_driver_properties", + &device->ext_driver_properties); + } + if (device->ext_variable_pointer_features.reported) { + ret &= visitor->Visit("VK_KHR_variable_pointers", + &device->ext_variable_pointer_features); + } + if (device->ext_shader_float16_int8_features.reported) { + ret &= visitor->Visit("VK_KHR_shader_float16_int8", + &device->ext_shader_float16_int8_features); + } } return ret; } @@ -897,16 +893,13 @@ inline bool Iterate(Visitor* visitor, VkJsonDevice* device) { template <typename Visitor> inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) { bool ret = true; - switch (instance->api_version ^ VK_VERSION_PATCH(instance->api_version)) { - case VK_API_VERSION_1_2: - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_1: - ret &= visitor->Visit("deviceGroups", &instance->device_groups); - FALLTHROUGH_INTENDED; - case VK_API_VERSION_1_0: - ret &= visitor->Visit("layers", &instance->layers) && - visitor->Visit("extensions", &instance->extensions) && - visitor->Visit("devices", &instance->devices); + if (instance->api_version >= VK_API_VERSION_1_1) { + ret &= visitor->Visit("deviceGroups", &instance->device_groups); + } + if (instance->api_version >= VK_API_VERSION_1_0) { + ret &= visitor->Visit("layers", &instance->layers) && + visitor->Visit("extensions", &instance->extensions) && + visitor->Visit("devices", &instance->devices); } return ret; } |