Merge "fs_mgr: move AVB related source into libfs_avb"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0978ec1..fee0857 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -46,6 +46,7 @@
#include <chrono>
#include <functional>
#include <regex>
+#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -78,6 +79,7 @@
using android::base::Split;
using android::base::Trim;
using android::base::unique_fd;
+using namespace std::string_literals;
static const char* serial = nullptr;
@@ -1106,6 +1108,14 @@
return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
}
+static bool is_retrofit_device() {
+ std::string value;
+ if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
+ return false;
+ }
+ return android::base::StartsWith(value, "system_");
+}
+
static void do_flash(const char* pname, const char* fname) {
struct fastboot_buffer buf;
@@ -1319,6 +1329,19 @@
command += ":wipe";
}
fb->RawCommand(command, "Updating super partition");
+
+ // Retrofit devices have two super partitions, named super_a and super_b.
+ // On these devices, secondary slots must be flashed as physical
+ // partitions (otherwise they would not mount on first boot). To enforce
+ // this, we delete any logical partitions for the "other" slot.
+ if (is_retrofit_device()) {
+ for (const auto& [image, slot] : os_images_) {
+ std::string partition_name = image->part_name + "_"s + slot;
+ if (image->IsSecondary() && is_logical(partition_name)) {
+ fb->DeletePartition(partition_name);
+ }
+ }
+ }
}
class ZipImageSource final : public ImageSource {
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
new file mode 100644
index 0000000..d715d7b
--- /dev/null
+++ b/fs_mgr/README.overlayfs.md
@@ -0,0 +1,92 @@
+Android Overlayfs integration with adb remount
+==============================================
+
+Introduction
+------------
+
+Users working with userdebug or eng builds expect to be able to
+remount the system partition as read-write and then add or modify
+any number of files without reflashing the system image, which is
+understandably efficient for a development cycle.
+Limited memory systems that chose to use readonly filesystems like
+*squashfs*, or *Logical Resizable Android Partitions* which land
+system partition images right-sized, and with filesystem that have
+been deduped on the block level to compress the content; means that
+either a remount is not possible directly, or when done offers
+little or no utility because of remaining space limitations or
+support logistics.
+
+*Overlayfs* comes to the rescue for these debug scenarios, and logic
+will _automatically_ setup backing storage for a writable filesystem
+as an upper reference, and mount overtop the lower. These actions
+will be performed in the **adb disable-verity** and **adb remount**
+requests.
+
+Operations
+----------
+
+### Cookbook
+
+The typical action to utilize the remount facility is:
+
+ $ adb root
+ $ adb disable-verity
+ $ adb reboot
+ $ adb wait-for-device
+ $ adb root
+ $ adb remount
+
+Followed by one of the following:
+
+ $ adb stop
+ $ adb sync
+ $ adb start
+ $ adb reboot
+
+*or*
+
+ $ adb push <source> <destination>
+ $ adb reboot
+
+Note that the sequence above:
+
+ $ adb disable-verity
+ $ adb reboot
+
+can be replaced with:
+
+ $ adb reboot -R
+
+which will not reboot if everything is already prepared and ready
+to go.
+
+None of this changes if *overlayfs* needs to be engaged.
+The decisions whether to use traditional direct filesystem remount,
+or one wrapped by *overlayfs* is automatically determined based on
+a probe of the filesystem types and space remaining.
+
+### Backing Storage
+
+When *overlayfs* logic is feasible, it will use either the
+**/cache/overlay/** directory for non-A/B devices, or the
+**/mnt/scratch/overlay** directory for A/B devices that have
+access to *Logical Resizeable Android Partitions*.
+The backing store is used as soon as possible in the boot
+process and can occur at first stage init, or at the
+mount_all init rc commands.
+
+This early as possible attachment of *overlayfs* means that
+*sepolicy* or *init* itself can also be pushed and used after
+the exec phases that accompany each stage.
+
+Caveats
+-------
+
+- Space used in the backing storage is on a file by file basis
+ and will require more space than if updated in place.
+- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
+ with "*overlayfs: override_creds=off option bypass creator_cred*"
+ if higher than 4.6.
+- *adb enable-verity* will free up overlayfs and as a bonus the
+ device will be reverted pristine to before any content was updated.
+- File bugs or submit fixes for review.
diff --git a/init/Android.bp b/init/Android.bp
index ff3b61f..ea66ac6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -71,6 +71,7 @@
"libbinder",
"libbootloader_message",
"libcutils",
+ "libcrypto",
"libdl",
"libext4_utils",
"libfs_mgr",
@@ -79,6 +80,7 @@
"libkeyutils",
"liblog",
"liblogwrap",
+ "liblp",
"libselinux",
"libutils",
],
@@ -92,6 +94,7 @@
"action.cpp",
"action_manager.cpp",
"action_parser.cpp",
+ "boringssl_self_test.cpp",
"bootchart.cpp",
"builtins.cpp",
"capabilities.cpp",
@@ -99,6 +102,7 @@
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
+ "first_stage_init.cpp",
"first_stage_mount.cpp",
"import_parser.cpp",
"init.cpp",
@@ -117,6 +121,7 @@
"sigchld_handler.cpp",
"subcontext.cpp",
"subcontext.proto",
+ "switch_root.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index a9daa8b..69f1d87 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -39,12 +39,15 @@
# --
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
devices.cpp \
+ first_stage_init.cpp \
+ first_stage_main.cpp \
first_stage_mount.cpp \
- init_first_stage.cpp \
reboot_utils.cpp \
selinux.cpp \
switch_root.cpp \
@@ -93,22 +96,16 @@
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
LOCAL_NOSANITIZE := hwaddress
-LOCAL_XOM := false
include $(BUILD_EXECUTABLE)
+endif
include $(CLEAR_VARS)
LOCAL_MODULE := init_system
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_REQUIRED_MODULES := \
- init_first_stage \
- init_second_stage \
-
-else
LOCAL_REQUIRED_MODULES := \
init_second_stage \
-endif
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
@@ -120,5 +117,3 @@
endif
include $(BUILD_PHONY_PACKAGE)
-
-
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
new file mode 100644
index 0000000..0408d30
--- /dev/null
+++ b/init/boringssl_self_test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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 "boringssl_self_test.h"
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <openssl/crypto.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&) {
+ pid_t id = fork();
+
+ if (id == 0) {
+ if (BORINGSSL_self_test() != 1) {
+ LOG(INFO) << "BoringSSL crypto self tests failed";
+
+ // This check has failed, so the device should refuse
+ // to boot. Rebooting to bootloader to wait for
+ // further action from the user.
+
+ int result = android_reboot(ANDROID_RB_RESTART2, 0,
+ "bootloader,boringssl-self-check-failed");
+ if (result != 0) {
+ LOG(ERROR) << "Failed to reboot into bootloader";
+ }
+ }
+
+ _exit(0);
+ } else if (id == -1) {
+ // Failed to fork, so cannot run the test. Refuse to continue.
+ PLOG(FATAL) << "Failed to fork for BoringSSL self test";
+ }
+
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
new file mode 100644
index 0000000..b21fc78
--- /dev/null
+++ b/init/boringssl_self_test.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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 "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&);
+
+} // namespace init
+} // namespace android
diff --git a/init/init_first_stage.cpp b/init/first_stage_init.cpp
similarity index 97%
rename from init/init_first_stage.cpp
rename to init/first_stage_init.cpp
index c2c6868..e11d897 100644
--- a/init/init_first_stage.cpp
+++ b/init/first_stage_init.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "first_stage_init.h"
+
#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
@@ -94,7 +96,7 @@
} // namespace
-int main(int argc, char** argv) {
+int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
@@ -214,7 +216,7 @@
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
+ const char* args[] = {path, "selinux_setup", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
@@ -226,7 +228,3 @@
} // namespace init
} // namespace android
-
-int main(int argc, char** argv) {
- return android::init::main(argc, argv);
-}
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
new file mode 100644
index 0000000..0476e44
--- /dev/null
+++ b/init/first_stage_init.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+int FirstStageMain(int argc, char** argv);
+
+} // namespace init
+} // namespace android
diff --git a/init/first_stage_main.cpp b/init/first_stage_main.cpp
new file mode 100644
index 0000000..7bae84c
--- /dev/null
+++ b/init/first_stage_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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 "first_stage_init.h"
+
+int main(int argc, char** argv) {
+ return android::init::FirstStageMain(argc, argv);
+}
diff --git a/init/init.cpp b/init/init.cpp
index e7dbc11..dc46a82 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -49,6 +49,7 @@
#endif
#include "action_parser.h"
+#include "boringssl_self_test.h"
#include "epoll.h"
#include "first_stage_mount.h"
#include "import_parser.h"
@@ -59,13 +60,8 @@
#include "security.h"
#include "selinux.h"
#include "sigchld_handler.h"
-#include "ueventd.h"
#include "util.h"
-#if __has_feature(address_sanitizer)
-#include <sanitizer/asan_interface.h>
-#endif
-
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -79,25 +75,6 @@
namespace android {
namespace init {
-#if __has_feature(address_sanitizer)
-// Load asan.options if it exists since these are not yet in the environment.
-// Always ensure detect_container_overflow=0 as there are false positives with this check.
-// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
-extern "C" const char* __asan_default_options() {
- return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
-__sanitizer_report_error_summary(const char* summary) {
- LOG(ERROR) << "Main stage (error summary): " << summary;
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
-AsanReportCallback(const char* str) {
- LOG(ERROR) << "Main stage: " << str;
-}
-#endif
-
static int property_triggers_enabled = 0;
static char qemu[32];
@@ -622,57 +599,11 @@
});
}
-static void SetupSelinux(char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain and want to transition to the init domain. File systems that
- // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
- // but other file systems do. In particular, this is needed for ramdisks such as the
- // recovery image for A/B devices.
- if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
- }
-
- setenv("SELINUX_INITIALIZED", "true", 1);
-
- const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
- execv(path, const_cast<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never return from this function.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
-}
-
-int main(int argc, char** argv) {
-#if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
-#endif
-
- if (!strcmp(basename(argv[0]), "ueventd")) {
- return ueventd_main(argc, argv);
- }
-
- if (argc > 1 && !strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap function_map;
- return SubcontextMain(argc, argv, &function_map);
- }
-
+int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
- if (getenv("SELINUX_INITIALIZED") == nullptr) {
- SetupSelinux(argv);
- }
-
// We need to set up stdin/stdout/stderr again now that we're running in init's context.
InitKernelLogging(argv, InitAborter);
LOG(INFO) << "init second stage started!";
@@ -708,7 +639,6 @@
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
- unsetenv("SELINUX_INITIALIZED");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
@@ -768,6 +698,9 @@
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
+ // Starting the BoringSSL self test, for NIAP certification compliance.
+ am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
+
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
diff --git a/init/init.h b/init/init.h
index 65405aa..a76da20 100644
--- a/init/init.h
+++ b/init/init.h
@@ -50,7 +50,7 @@
void ResetWaitForProp();
-int main(int argc, char** argv);
+int SecondStageMain(int argc, char** argv);
} // namespace init
} // namespace android
diff --git a/init/main.cpp b/init/main.cpp
index 9ed451b..868c409 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -14,8 +14,70 @@
* limitations under the License.
*/
+#include "builtins.h"
+#include "first_stage_init.h"
#include "init.h"
+#include "selinux.h"
+#include "subcontext.h"
+#include "ueventd.h"
+
+#include <android-base/logging.h>
+
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+ return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+ LOG(ERROR) << "Init (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+ LOG(ERROR) << "Init: " << str;
+}
+#endif
+
+using namespace android::init;
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+#if __has_feature(address_sanitizer)
+ __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
+ if (!strcmp(basename(argv[0]), "ueventd")) {
+ return ueventd_main(argc, argv);
+ }
+
+ if (argc < 2) {
+ return FirstStageMain(argc, argv);
+ }
+
+ if (!strcmp(argv[1], "subcontext")) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ const BuiltinFunctionMap function_map;
+
+ return SubcontextMain(argc, argv, &function_map);
+ }
+
+ if (!strcmp(argv[1], "selinux_setup")) {
+ return SetupSelinux(argv);
+ }
+
+ if (!strcmp(argv[1], "second_stage")) {
+ return SecondStageMain(argc, argv);
+ }
+
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ LOG(ERROR) << "Unknown argument passed to init '" << argv[1] << "'";
+ return 1;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd7e86f..3a09096 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -18,8 +18,8 @@
// for SELinux operation for init.
// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
-// Init loads the SEPolicy from the file system, restores the context of /init based on this
-// SEPolicy, and finally exec()'s itself to run in the proper domain.
+// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on
+// this SEPolicy, and finally exec()'s itself to run in the proper domain.
// The SEPolicy on Android comes in two variants: monolithic and split.
@@ -58,8 +58,10 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
#include <selinux/android.h>
+#include "reboot_utils.h"
#include "util.h"
using android::base::ParseInt;
@@ -379,8 +381,6 @@
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
-} // namespace
-
void SelinuxInitialize() {
Timer t;
@@ -405,6 +405,8 @@
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
+} // namespace
+
// The files and directories that were created before initial sepolicy load or
// files on ramdisk need to have their security context restored to the proper
// value. This must happen before /dev is populated by ueventd.
@@ -496,6 +498,39 @@
return major_version;
}
+// This function initializes SELinux then execs init to run in the init SELinux context.
+int SetupSelinux(char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ });
+
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
+
+ // We're in the kernel domain and want to transition to the init domain. File systems that
+ // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+ // but other file systems do. In particular, this is needed for ramdisks such as the
+ // recovery image for A/B devices.
+ if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+ }
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "second_stage", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}
+
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
diff --git a/init/selinux.h b/init/selinux.h
index c41d7f0..3aa9406 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -23,7 +23,7 @@
namespace android {
namespace init {
-void SelinuxInitialize();
+int SetupSelinux(char** argv);
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index a046dad..89837f7 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -35,6 +35,13 @@
end = begin + 1;
}
Range range{begin, end};
+ if (valid_mappings_range_.end != 0 &&
+ (begin < valid_mappings_range_.begin || end > valid_mappings_range_.end)) {
+ MEM_LOG_ALWAYS_FATAL("allocation %p-%p is outside mapping range %p-%p",
+ reinterpret_cast<void*>(begin), reinterpret_cast<void*>(end),
+ reinterpret_cast<void*>(valid_mappings_range_.begin),
+ reinterpret_cast<void*>(valid_mappings_range_.end));
+ }
auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
if (inserted.second) {
valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
@@ -87,6 +94,11 @@
}
}
+void HeapWalker::Mapping(uintptr_t begin, uintptr_t end) {
+ valid_mappings_range_.begin = std::min(valid_mappings_range_.begin, begin);
+ valid_mappings_range_.end = std::max(valid_mappings_range_.end, end);
+}
+
void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
roots_.push_back(Range{begin, end});
}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index b37cc62..9e3db08 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -59,6 +59,8 @@
segv_page_count_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
+ valid_mappings_range_.end = 0;
+ valid_mappings_range_.begin = ~valid_allocations_range_.end;
segv_handler_.install(
SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
@@ -68,6 +70,7 @@
~HeapWalker() {}
bool Allocation(uintptr_t begin, uintptr_t end);
+ void Mapping(uintptr_t begin, uintptr_t end);
void Root(uintptr_t begin, uintptr_t end);
void Root(const allocator::vector<uintptr_t>& vals);
@@ -98,6 +101,7 @@
AllocationMap allocations_;
size_t allocation_bytes_;
Range valid_allocations_range_;
+ Range valid_mappings_range_;
allocator::vector<Range> roots_;
allocator::vector<uintptr_t> root_vals_;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index b160de9..3d7b8a8 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -87,6 +87,11 @@
const allocator::vector<Mapping>& mappings,
const allocator::vector<uintptr_t>& refs) {
MEM_ALOGI("searching process %d for allocations", pid_);
+
+ for (auto it = mappings.begin(); it != mappings.end(); it++) {
+ heap_walker_.Mapping(it->begin, it->end);
+ }
+
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 84a0ec6..9610cd6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -73,6 +73,24 @@
ASSERT_FALSE(heap_walker.Allocation(2, 3));
}
+TEST_F(HeapWalkerTest, mapping) {
+ HeapWalker heap_walker(heap_);
+ heap_walker.Mapping(2, 3);
+ heap_walker.Mapping(4, 5);
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ // space between mappings is not checked, but could be in the future
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+
+ // re-enable malloc, ASSERT_DEATH may allocate
+ disable_malloc_.Enable();
+ ASSERT_DEATH({ heap_walker.Allocation(1, 2); }, "0x1-0x2.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(1, 3); }, "0x1-0x3.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(4, 6); }, "0x4-0x6.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(5, 6); }, "0x5-0x6.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(1, 6); }, "0x1-0x6.*outside.*0x2-0x5");
+}
+
#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index ca6aafe..461184a 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -6,35 +6,40 @@
# All binaries gets the same configuration 'legacy'
dir.legacy = /system
+dir.legacy = /product
dir.legacy = /vendor
dir.legacy = /odm
dir.legacy = /sbin
-# Except for /postinstall, where only /system is searched
+# Except for /postinstall, where only /system and /product are searched
dir.postinstall = /postinstall
[legacy]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.search.paths += /vendor/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# /system/lib and /product/lib in the search paths. This is because linker
+# calls realpath on the search paths and this causes selinux denial if the
+# paths (/vendor, /odm) are not allowed to the poinstall binaries.
+# There is no reason to allow the binaries to access the paths.
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}