Merge "Fix a potential memory leak"
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f4756d5..d697efb 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,39 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
on post-fs-data
- mkdir /data/misc/bootstat 0700 root root
+ mkdir /data/misc/bootstat 0700 system log
+ # To deal with ota transition resulting from a change in DAC from
+ # root.root to system.log, may be deleted after ota has settled.
+ chown system log /data/misc/bootstat/absolute_boot_time
+ chown system log /data/misc/bootstat/boot_complete
+ chown system log /data/misc/bootstat/boot_complete_no_encryption
+ chown system log /data/misc/bootstat/boot_reason
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.1BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLE
+ chown system log /data/misc/bootstat/bootime.bootloader.2BLL
+ chown system log /data/misc/bootstat/bootime.bootloader.AVB
+ chown system log /data/misc/bootstat/bootime.bootloader.KD
+ chown system log /data/misc/bootstat/bootime.bootloader.KL
+ chown system log /data/misc/bootstat/bootime.bootloader.ODT
+ chown system log /data/misc/bootstat/bootime.bootloader.SW
+ chown system log /data/misc/bootstat/bootime.bootloader.total
+ chown system log /data/misc/bootstat/build_date
+ chown system log /data/misc/bootstat/factory_reset
+ chown system log /data/misc/bootstat/factory_reset_boot_complete
+ chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/factory_reset_current_time
+ chown system log /data/misc/bootstat/factory_reset_record_value
+ chown system log /data/misc/bootstat/last_boot_time_utc
+ chown system log /data/misc/bootstat/ota_boot_complete
+ chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
+ chown system log /data/misc/bootstat/post_decrypt_time_elapsed
+ chown system log /data/misc/bootstat/ro.boottime.init
+ chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
+ chown system log /data/misc/bootstat/ro.boottime.init.selinux
+ chown system log /data/misc/bootstat/time_since_factory_reset
+ chown system log /data/misc/bootstat/time_since_last_boot
+ # end ota transitional support
# Record the time at which the user has successfully entered the pin to decrypt
# the device, /data is decrypted, and the system is entering the main boot phase.
@@ -10,7 +42,7 @@
# property:init.svc.bootanim=running: The boot animation is running
# property:ro.crypto.type=block: FDE device
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
- exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+ exec - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
# This signaling is necessary to prevent logging boot metrics after a runtime
@@ -33,13 +65,13 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
# Record boot_complete and related stats (decryption, etc).
- exec - root root -- /system/bin/bootstat --record_boot_complete
+ exec - system log -- /system/bin/bootstat --record_boot_complete
# Record the boot reason.
- exec - root root -- /system/bin/bootstat --record_boot_reason
+ exec - system log -- /system/bin/bootstat --record_boot_reason
# Record time since factory reset.
- exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+ exec - system log -- /system/bin/bootstat --record_time_since_factory_reset
# Log all boot events.
- exec - root root -- /system/bin/bootstat -l
+ exec - system log -- /system/bin/bootstat -l
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index f86aaa0..7d17cd9 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -106,6 +106,7 @@
"libdebuggerd",
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libcutils",
],
@@ -171,6 +172,7 @@
static_libs: [
"libbacktrace",
"libunwind",
+ "libunwindstack",
"liblzma",
"libbase",
"libcutils",
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 4b1e51d..34ad7de 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -329,6 +329,11 @@
LOG(FATAL) << "failed to create backtrace map";
}
}
+ std::unique_ptr<BacktraceMap> backtrace_map_new;
+ backtrace_map_new.reset(BacktraceMap::CreateNew(main_tid));
+ if (!backtrace_map_new) {
+ LOG(FATAL) << "failed to create backtrace map new";
+ }
// Collect the list of open files.
OpenFilesList open_files;
@@ -408,8 +413,9 @@
dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0);
} else {
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid,
- process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr);
+ engrave_tombstone(output_fd.get(), backtrace_map.get(), backtrace_map_new.get(), &open_files,
+ target, main_tid, process_name, threads, abort_address,
+ fatal_signal ? &amfd_data : nullptr);
}
// We don't actually need to PTRACE_DETACH, as long as our tracees aren't in
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 55cd03e..1275229 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -116,6 +116,26 @@
fatal("%s: %s", buf, strerror(err));
}
+static bool get_main_thread_name(char* buf, size_t len) {
+ int fd = open("/proc/self/comm", O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ ssize_t rc = read(fd, buf, len);
+ close(fd);
+ if (rc == -1) {
+ return false;
+ } else if (rc == 0) {
+ // Should never happen?
+ return false;
+ }
+
+ // There's a trailing newline, replace it with a NUL.
+ buf[rc - 1] = '\0';
+ return true;
+}
+
/*
* Writes a summary of the signal to the log file. We do this so that, if
* for some reason we're not able to contact debuggerd, there is still some
@@ -188,8 +208,14 @@
}
}
- async_safe_format_log(ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s)",
- signum, signal_name, code_desc, addr_desc, __gettid(), thread_name);
+ char main_thread_name[MAX_TASK_NAME_LEN + 1];
+ if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+ strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+ }
+
+ async_safe_format_log(
+ ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum,
+ signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name);
}
/*
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/tombstone.h
index 79743b6..45740df 100644
--- a/debuggerd/libdebuggerd/include/tombstone.h
+++ b/debuggerd/libdebuggerd/include/tombstone.h
@@ -35,10 +35,10 @@
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
- std::string* amfd_data);
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address, std::string* amfd_data);
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0113131..b809ed4 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -495,8 +495,55 @@
_LOG(log, logtype::REGISTERS, " register dumping unimplemented on this architecture");
}
-static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
- const std::string& thread_name, BacktraceMap* map,
+static bool verify_backtraces_equal(Backtrace* back1, Backtrace* back2) {
+ if (back1->NumFrames() != back2->NumFrames()) {
+ return false;
+ }
+ std::string back1_str;
+ std::string back2_str;
+ for (size_t i = 0; i < back1->NumFrames(); i++) {
+ back1_str += back1->FormatFrameData(i);
+ back2_str += back2->FormatFrameData(i);
+ }
+ return back1_str == back2_str;
+}
+
+static void log_mismatch_data(log_t* log, Backtrace* backtrace) {
+ _LOG(log, logtype::THREAD, "MISMATCH: This unwind is different.\n");
+ if (backtrace->NumFrames() == 0) {
+ _LOG(log, logtype::THREAD, "MISMATCH: No frames in new backtrace.\n");
+ return;
+ }
+ _LOG(log, logtype::THREAD, "MISMATCH: Backtrace from new unwinder.\n");
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ _LOG(log, logtype::THREAD, "MISMATCH: %s\n", backtrace->FormatFrameData(i).c_str());
+ }
+
+ // Get the stack trace up to 8192 bytes.
+ std::vector<uint64_t> buffer(8192 / sizeof(uint64_t));
+ size_t bytes =
+ backtrace->Read(backtrace->GetFrame(0)->sp, reinterpret_cast<uint8_t*>(buffer.data()),
+ buffer.size() * sizeof(uint64_t));
+ std::string log_data;
+ for (size_t i = 0; i < bytes / sizeof(uint64_t); i++) {
+ if ((i % 4) == 0) {
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: stack_data%s\n", log_data.c_str());
+ log_data = "";
+ }
+ }
+ log_data += android::base::StringPrintf(" 0x%016" PRIx64, buffer[i]);
+ }
+
+ if (!log_data.empty()) {
+ _LOG(log, logtype::THREAD, "MISMATCH: data%s\n", log_data.c_str());
+ }
+
+ // If there is any leftover (bytes % sizeof(uint64_t) != 0, ignore it for now.
+}
+
+static bool dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name,
+ const std::string& thread_name, BacktraceMap* map, BacktraceMap* map_new,
uintptr_t abort_msg_address, bool primary_thread) {
log->current_tid = tid;
if (!primary_thread) {
@@ -510,7 +557,18 @@
dump_abort_message(backtrace.get(), log, abort_msg_address);
}
dump_registers(log, tid);
+ bool matches = true;
if (backtrace->Unwind(0)) {
+ // Use the new method and verify it is the same as old.
+ std::unique_ptr<Backtrace> backtrace_new(Backtrace::CreateNew(pid, tid, map_new));
+ if (!backtrace_new->Unwind(0)) {
+ _LOG(log, logtype::THREAD, "Failed to unwind with new unwinder: %s\n",
+ backtrace_new->GetErrorString(backtrace_new->GetError()).c_str());
+ matches = false;
+ } else if (!verify_backtraces_equal(backtrace.get(), backtrace_new.get())) {
+ log_mismatch_data(log, backtrace_new.get());
+ matches = false;
+ }
dump_backtrace_and_stack(backtrace.get(), log);
} else {
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
@@ -524,6 +582,8 @@
}
log->current_tid = log->crashed_tid;
+
+ return matches;
}
// Reads the contents of the specified log device, filters out the entries
@@ -657,9 +717,10 @@
}
// Dumps all information about the specified pid to the tombstone.
-static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid,
- pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address) {
+static void dump_crash(log_t* log, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address) {
// don't copy log messages to tombstone unless this is a dev device
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "0");
@@ -668,7 +729,8 @@
_LOG(log, logtype::HEADER,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_header_info(log);
- dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true);
+ bool new_unwind_matches = dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map,
+ map_new, abort_msg_address, true);
if (want_logs) {
dump_logs(log, pid, 5);
}
@@ -678,7 +740,9 @@
const std::string& thread_name = it.second;
if (thread_tid != tid) {
- dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false);
+ bool match =
+ dump_thread(log, pid, thread_tid, process_name, thread_name, map, map_new, 0, false);
+ new_unwind_matches = new_unwind_matches && match;
}
}
@@ -690,6 +754,14 @@
if (want_logs) {
dump_logs(log, pid, 0);
}
+ if (!new_unwind_matches) {
+ _LOG(log, logtype::THREAD, "MISMATCH: New and old unwinder do not agree.\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: If you see this please file a bug in:\n");
+ _LOG(log, logtype::THREAD,
+ "MISMATCH: Android > Android OS & Apps > Runtime > native > tools "
+ "(debuggerd/gdb/init/simpleperf/strace/valgrind)\n");
+ _LOG(log, logtype::THREAD, "MISMATCH: and attach this tombstone.\n");
+ }
}
// open_tombstone - find an available tombstone slot, if any, of the
@@ -745,16 +817,16 @@
return fd;
}
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uintptr_t abort_msg_address,
- std::string* amfd_data) {
+void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uintptr_t abort_msg_address, std::string* amfd_data) {
log_t log;
log.current_tid = tid;
log.crashed_tid = tid;
log.tfd = tombstone_fd;
log.amfd_data = amfd_data;
- dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address);
+ dump_crash(&log, map, map_new, open_files, pid, tid, process_name, threads, abort_msg_address);
}
void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo,
diff --git a/init/Android.bp b/init/Android.bp
index 0d7240e..b1f0279 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -69,6 +69,9 @@
"import_parser.cpp",
"log.cpp",
"parser.cpp",
+ "property_service.cpp",
+ "security.cpp",
+ "selinux.cpp",
"service.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
@@ -81,7 +84,12 @@
"libselinux",
"liblog",
"libprocessgroup",
+ "libfs_mgr",
],
+ include_dirs: [
+ "system/core/mkbootimg",
+ ],
+
}
/*
@@ -105,15 +113,11 @@
"init.cpp",
"init_first_stage.cpp",
"keychords.cpp",
- "property_service.cpp",
"reboot.cpp",
"signal_handler.cpp",
"ueventd.cpp",
"watchdogd.cpp",
],
- include_dirs: [
- "system/core/mkbootimg"
- ],
static_libs: [
"libinit",
"libbootloader_message",
@@ -155,6 +159,7 @@
"devices_test.cpp",
"init_test.cpp",
"property_service_test.cpp",
+ "result_test.cpp",
"service_test.cpp",
"ueventd_test.cpp",
"util_test.cpp",
@@ -162,9 +167,12 @@
shared_libs: [
"libbase",
"libcutils",
- "libselinux",
],
- static_libs: ["libinit"],
+ static_libs: [
+ "libinit",
+ "libselinux",
+ "libcrypto",
+ ],
}
subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index f30c2a4..3886ed5 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -51,15 +51,12 @@
init.cpp \
init_first_stage.cpp \
keychords.cpp \
- property_service.cpp \
reboot.cpp \
signal_handler.cpp \
ueventd.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
-LOCAL_C_INCLUDES += \
- system/core/mkbootimg
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
diff --git a/init/README.md b/init/README.md
index f3b57bc..0ea00fb 100644
--- a/init/README.md
+++ b/init/README.md
@@ -447,6 +447,9 @@
`rmdir <path>`
> Calls rmdir(2) on the given path.
+`readahead <file|dir>`
+> Calls readahead(2) on the file or files within given directory.
+
`setprop <name> <value>`
> Set system property _name_ to _value_. Properties are expanded
within _value_.
diff --git a/init/action.cpp b/init/action.cpp
index 4ec5f17..f687074 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -31,14 +31,13 @@
Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
: func_(f), args_(args), line_(line) {}
-int Command::InvokeFunc() const {
+Result<Success> Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
expanded_args.resize(args_.size());
expanded_args[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
if (!expand_props(args_[i], &expanded_args[i])) {
- LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
- return -EINVAL;
+ return Error() << "cannot expand '" << args_[i] << "'";
}
}
@@ -54,19 +53,16 @@
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
+Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
if (!function_map_) {
- *err = "no function map available";
- return false;
+ return Error() << "no function map available";
}
- auto function = function_map_->FindFunction(args, err);
- if (!function) {
- return false;
- }
+ auto function = function_map_->FindFunction(args);
+ if (!function) return Error() << function.error();
- AddCommand(function, args, line);
- return true;
+ AddCommand(*function, args, line);
+ return Success();
}
void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
@@ -92,81 +88,74 @@
void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
- int result = command.InvokeFunc();
-
+ auto result = command.InvokeFunc();
auto duration = t.duration();
+
// Any action longer than 50ms will be warned to user as slow operation
if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
- << ":" << command.line() << ") returned " << result << " took "
- << duration.count() << "ms.";
+ << ":" << command.line() << ") took " << duration.count() << "ms and "
+ << (result ? "succeeded" : "failed: " + result.error());
}
}
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
- *err = "property trigger found without matching '='";
- return false;
+ return Error() << "property trigger found without matching '='";
}
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
- *err = "multiple property triggers found for same property";
- return false;
+ return Error() << "multiple property triggers found for same property";
}
- return true;
+ return Success();
}
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Action::InitTriggers(const std::vector<std::string>& args) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (args[i].empty()) {
- *err = "empty trigger is not valid";
- return false;
+ return Error() << "empty trigger is not valid";
}
if (i % 2) {
if (args[i] != "&&") {
- *err = "&& is the only symbol allowed to concatenate actions";
- return false;
+ return Error() << "&& is the only symbol allowed to concatenate actions";
} else {
continue;
}
}
if (!args[i].compare(0, prop_str.length(), prop_str)) {
- if (!ParsePropertyTrigger(args[i], err)) {
- return false;
+ if (auto result = ParsePropertyTrigger(args[i]); !result) {
+ return result;
}
} else {
if (!event_trigger_.empty()) {
- *err = "multiple event triggers are not allowed";
- return false;
+ return Error() << "multiple event triggers are not allowed";
}
event_trigger_ = args[i];
}
}
- return true;
+ return Success();
}
-bool Action::InitSingleTrigger(const std::string& trigger) {
+Result<Success> Action::InitSingleTrigger(const std::string& trigger) {
std::vector<std::string> name_vector{trigger};
- std::string err;
- bool ret = InitTriggers(name_vector, &err);
- if (!ret) {
- LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
+ if (auto result = InitTriggers(name_vector); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
- return ret;
+ return Success();
}
// This function checks that all property triggers are satisfied, that is
@@ -264,7 +253,8 @@
auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
- if (!action->InitSingleTrigger(name)) {
+ if (auto result = action->InitSingleTrigger(name); !result) {
+ LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error();
return;
}
@@ -333,25 +323,25 @@
current_command_ = 0;
}
-bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
- *err = "actions must have a trigger";
- return false;
+ return Error() << "Actions must have a trigger";
}
auto action = std::make_unique<Action>(false, filename, line);
- if (!action->InitTriggers(triggers, err)) {
- return false;
+
+ if (auto result = action->InitTriggers(triggers); !result) {
+ return Error() << "InitTriggers() failed: " << result.error();
}
action_ = std::move(action);
- return true;
+ return Success();
}
-bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- return action_ ? action_->AddCommand(std::move(args), line, err) : false;
+Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
void ActionParser::EndSection() {
diff --git a/init/action.h b/init/action.h
index 50cae71..d977f82 100644
--- a/init/action.h
+++ b/init/action.h
@@ -26,6 +26,7 @@
#include "builtins.h"
#include "keyword_map.h"
#include "parser.h"
+#include "result.h"
namespace android {
namespace init {
@@ -34,7 +35,7 @@
public:
Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
- int InvokeFunc() const;
+ Result<Success> InvokeFunc() const;
std::string BuildCommandString() const;
int line() const { return line_; }
@@ -53,10 +54,10 @@
public:
explicit Action(bool oneshot, const std::string& filename, int line);
- bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ Result<Success> AddCommand(const std::vector<std::string>& args, int line);
void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
- bool InitTriggers(const std::vector<std::string>& args, std::string* err);
- bool InitSingleTrigger(const std::string& trigger);
+ Result<Success> InitTriggers(const std::vector<std::string>& args);
+ Result<Success> InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
@@ -78,7 +79,7 @@
void ExecuteCommand(const Command& command) const;
bool CheckPropertyTriggers(const std::string& name = "",
const std::string& value = "") const;
- bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+ Result<Success> ParsePropertyTrigger(const std::string& trigger);
std::map<std::string, std::string> property_triggers_;
std::string event_trigger_;
@@ -120,9 +121,9 @@
public:
ActionParser(ActionManager* action_manager)
: action_manager_(action_manager), action_(nullptr) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4727f92..ec84317 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -163,37 +163,37 @@
LOG(INFO) << "Bootcharting finished";
}
-static int do_bootchart_start() {
- // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
- std::string start;
- if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
- LOG(VERBOSE) << "Not bootcharting";
- return 0;
- }
+static Result<Success> do_bootchart_start() {
+ // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+ std::string start;
+ if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+ LOG(VERBOSE) << "Not bootcharting";
+ return Success();
+ }
- g_bootcharting_thread = new std::thread(bootchart_thread_main);
- return 0;
+ g_bootcharting_thread = new std::thread(bootchart_thread_main);
+ return Success();
}
-static int do_bootchart_stop() {
- if (!g_bootcharting_thread) return 0;
+static Result<Success> do_bootchart_stop() {
+ if (!g_bootcharting_thread) return Success();
- // Tell the worker thread it's time to quit.
- {
- std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
- g_bootcharting_finished = true;
- g_bootcharting_finished_cv.notify_one();
- }
+ // Tell the worker thread it's time to quit.
+ {
+ std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+ g_bootcharting_finished = true;
+ g_bootcharting_finished_cv.notify_one();
+ }
- g_bootcharting_thread->join();
- delete g_bootcharting_thread;
- g_bootcharting_thread = nullptr;
- return 0;
+ g_bootcharting_thread->join();
+ delete g_bootcharting_thread;
+ g_bootcharting_thread = nullptr;
+ return Success();
}
-int do_bootchart(const std::vector<std::string>& args) {
- if (args[1] == "start") return do_bootchart_start();
- return do_bootchart_stop();
+Result<Success> do_bootchart(const std::vector<std::string>& args) {
+ if (args[1] == "start") return do_bootchart_start();
+ return do_bootchart_stop();
}
} // namespace init
diff --git a/init/bootchart.h b/init/bootchart.h
index e4f7b59..f614f71 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,10 +20,12 @@
#include <string>
#include <vector>
+#include "result.h"
+
namespace android {
namespace init {
-int do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const std::vector<std::string>& args);
} // namespace init
} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 5335608..f807343 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fts.h>
#include <linux/loop.h>
#include <linux/module.h>
#include <mntent.h>
@@ -77,47 +78,13 @@
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static int insmod(const char *filename, const char *options, int flags) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
- if (fd == -1) {
- PLOG(ERROR) << "insmod: open(\"" << filename << "\") failed";
- return -1;
- }
- int rc = syscall(__NR_finit_module, fd.get(), options, flags);
- if (rc == -1) {
- PLOG(ERROR) << "finit_module for \"" << filename << "\" failed";
- }
- return rc;
-}
-
-static int __ifupdown(const char *interface, int up) {
- struct ifreq ifr;
-
- strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
-
- unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
- if (s < 0) return -1;
-
- int ret = ioctl(s, SIOCGIFFLAGS, &ifr);
- if (ret < 0) return ret;
-
- if (up) {
- ifr.ifr_flags |= IFF_UP;
- } else {
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- return ioctl(s, SIOCSIFFLAGS, &ifr);
-}
-
-static int reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
std::string err;
if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << "failed to set bootloader message: " << err;
- return -1;
+ return Error() << "Failed to set bootloader message: " << err;
}
property_set("sys.powerctl", "reboot,recovery");
- return 0;
+ return Success();
}
template <typename F>
@@ -127,90 +94,106 @@
}
}
-static int do_class_start(const std::vector<std::string>& args) {
+static Result<Success> do_class_start(const std::vector<std::string>& args) {
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
- return 0;
+ return Success();
}
-static int do_class_stop(const std::vector<std::string>& args) {
+static Result<Success> do_class_stop(const std::vector<std::string>& args) {
ForEachServiceInClass(args[1], &Service::Stop);
- return 0;
+ return Success();
}
-static int do_class_reset(const std::vector<std::string>& args) {
+static Result<Success> do_class_reset(const std::vector<std::string>& args) {
ForEachServiceInClass(args[1], &Service::Reset);
- return 0;
+ return Success();
}
-static int do_class_restart(const std::vector<std::string>& args) {
+static Result<Success> do_class_restart(const std::vector<std::string>& args) {
ForEachServiceInClass(args[1], &Service::Restart);
- return 0;
+ return Success();
}
-static int do_domainname(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile("/proc/sys/kernel/domainname", args[1], &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_domainname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
}
- return 0;
+ return Success();
}
-static int do_enable(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
- if (!svc) {
- return -1;
- }
- return svc->Enable();
+ if (!svc) return Error() << "Could not find service";
+
+ if (!svc->Enable()) return Error() << "Could not enable service";
+
+ return Success();
}
-static int do_exec(const std::vector<std::string>& args) {
+static Result<Success> do_exec(const std::vector<std::string>& args) {
auto service = Service::MakeTemporaryOneshotService(args);
if (!service) {
- LOG(ERROR) << "Failed to create exec service: " << android::base::Join(args, " ");
- return -1;
+ return Error() << "Could not create exec service";
}
if (!service->ExecStart()) {
- LOG(ERROR) << "Failed to Start exec service";
- return -1;
+ return Error() << "Could not start exec service";
}
+
ServiceList::GetInstance().AddService(std::move(service));
- return 0;
+ return Success();
}
-static int do_exec_start(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const std::vector<std::string>& args) {
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
- LOG(ERROR) << "ExecStart(" << args[1] << "): Service not found";
- return -1;
+ return Error() << "Service not found";
}
+
if (!service->ExecStart()) {
- LOG(ERROR) << "ExecStart(" << args[1] << "): Could not start Service";
- return -1;
+ return Error() << "Could not start Service";
}
- return 0;
+
+ return Success();
}
-static int do_export(const std::vector<std::string>& args) {
- return add_environment(args[1].c_str(), args[2].c_str());
-}
-
-static int do_hostname(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile("/proc/sys/kernel/hostname", args[1], &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_export(const std::vector<std::string>& args) {
+ if (!add_environment(args[1].c_str(), args[2].c_str())) {
+ return Error();
}
- return 0;
+ return Success();
}
-static int do_ifup(const std::vector<std::string>& args) {
- return __ifupdown(args[1].c_str(), 1);
+static Result<Success> do_hostname(const std::vector<std::string>& args) {
+ if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
+ return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
+ }
+ return Success();
}
-static int do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_ifup(const std::vector<std::string>& args) {
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
+
+ unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+ if (s < 0) return ErrnoError() << "opening socket failed";
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+ return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
+ }
+
+ return Success();
+}
+
+static Result<Success> do_insmod(const std::vector<std::string>& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -221,53 +204,56 @@
std::string filename = *it++;
std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');
- return insmod(filename.c_str(), options.c_str(), flags);
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed";
+
+ int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
+ if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
+
+ return Success();
}
-static int do_mkdir(const std::vector<std::string>& args) {
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const std::vector<std::string>& args) {
mode_t mode = 0755;
- int ret;
-
- /* mkdir <path> [mode] [owner] [group] */
-
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
}
- ret = make_dir(args[1].c_str(), mode, sehandle);
- /* chmod in case the directory already exists */
- if (ret == -1 && errno == EEXIST) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- }
- if (ret == -1) {
- return -errno;
+ if (!make_dir(args[1], mode)) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat() failed";
+ }
+ } else {
+ return ErrnoError() << "mkdir() failed";
+ }
}
if (args.size() >= 4) {
- uid_t uid;
- std::string decode_uid_err;
- if (!DecodeUid(args[3], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[3] << "': " << decode_uid_err;
- return -1;
+ auto uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
}
- gid_t gid = -1;
+ Result<gid_t> gid = -1;
if (args.size() == 5) {
- if (!DecodeUid(args[4], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
- return -1;
+ gid = DecodeUid(args[4]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
}
}
- if (lchown(args[1].c_str(), uid, gid) == -1) {
- return -errno;
+ if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown failed";
}
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
if (mode & (S_ISUID | S_ISGID)) {
- ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
- if (ret == -1) {
- return -errno;
+ if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+ return ErrnoError() << "fchmodat failed";
}
}
}
@@ -278,15 +264,18 @@
"--prompt_and_wipe_data",
"--reason=set_policy_failed:"s + args[1]};
reboot_into_recovery(options);
- return -1;
+ return Error() << "reboot into recovery failed";
}
}
- return 0;
+ return Success();
}
/* umount <path> */
-static int do_umount(const std::vector<std::string>& args) {
- return umount(args[1].c_str());
+static Result<Success> do_umount(const std::vector<std::string>& args) {
+ if (umount(args[1].c_str()) < 0) {
+ return ErrnoError() << "umount() failed";
+ }
+ return Success();
}
static struct {
@@ -314,7 +303,7 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static int do_mount(const std::vector<std::string>& args) {
+static Result<Success> do_mount(const std::vector<std::string>& args) {
const char* options = nullptr;
unsigned flags = 0;
bool wait = false;
@@ -345,12 +334,12 @@
if (android::base::StartsWith(source, "loop@")) {
int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
- if (fd < 0) return -1;
+ if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
for (size_t n = 0;; n++) {
std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
- if (loop < 0) return -1;
+ if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
loop_info info;
/* if it is a blank loop device */
@@ -359,26 +348,24 @@
if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
if (mount(tmp.c_str(), target, system, flags, options) < 0) {
ioctl(loop, LOOP_CLR_FD, 0);
- return -1;
+ return ErrnoError() << "mount() failed";
}
- return 0;
+ return Success();
}
}
}
- LOG(ERROR) << "out of loopback devices";
- return -1;
+ return Error() << "out of loopback devices";
} else {
if (wait)
wait_for_file(source, kCommandRetryTimeout);
if (mount(source, target, system, flags, options) < 0) {
- return -1;
+ return ErrnoError() << "mount() failed";
}
}
- return 0;
-
+ return Success();
}
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -410,9 +397,7 @@
*
* Call fs_mgr_mount_all() to mount the given fstab
*/
-static int mount_fstab(const char* fstabfile, int mount_mode) {
- int ret = -1;
-
+static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
@@ -430,9 +415,9 @@
}
if (WIFEXITED(status)) {
- ret = WEXITSTATUS(status);
+ return WEXITSTATUS(status);
} else {
- ret = -1;
+ return Error() << "child aborted";
}
} else if (pid == 0) {
/* child, call fs_mgr_mount_all() */
@@ -449,10 +434,8 @@
}
_exit(child_ret);
} else {
- /* fork failed, return an error */
- return -1;
+ return Error() << "fork() failed";
}
- return ret;
}
/* Queue event based on fs_mgr return code.
@@ -464,29 +447,33 @@
*
* return code is processed based on input code
*/
-static int queue_fs_event(int code) {
- int ret = code;
+static Result<Success> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
- ret = reboot_into_recovery(options);
+ reboot_into_recovery(options);
+ return Error() << "reboot_into_recovery() failed";
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
- return -1;
+ return Error() << "e4crypt_install_keyring() failed";
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
@@ -494,12 +481,13 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+ return Success();
} else if (code > 0) {
- PLOG(ERROR) << "fs_mgr_mount_all returned unexpected error " << code;
+ Error() << "fs_mgr_mount_all() returned unexpected error " << code;
}
/* else ... < 0: error */
- return ret;
+ return Error() << "Invalid code: " << code;
}
/* mount_all <fstab> [ <path> ]* [--<options>]*
@@ -507,7 +495,7 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static int do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const std::vector<std::string>& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
@@ -532,7 +520,10 @@
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
- int ret = mount_fstab(fstabfile, mount_mode);
+ auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+ if (!mount_fstab_return_code) {
+ return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+ }
property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc) {
@@ -543,13 +534,16 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- ret = queue_fs_event(ret);
+ auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+ if (!queue_fs_result) {
+ return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
+ }
}
- return ret;
+ return Success();
}
-static int do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
struct fstab *fstab;
int ret;
@@ -557,89 +551,103 @@
ret = fs_mgr_swapon_all(fstab);
fs_mgr_free_fstab(fstab);
- return ret;
+ if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
+ return Success();
}
-static int do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const std::vector<std::string>& args) {
property_set(args[1], args[2]);
- return 0;
+ return Success();
}
-static int do_setrlimit(const std::vector<std::string>& args) {
- struct rlimit limit;
+static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
int resource;
- if (android::base::ParseInt(args[1], &resource) &&
- android::base::ParseUint(args[2], &limit.rlim_cur) &&
- android::base::ParseUint(args[3], &limit.rlim_max)) {
- return setrlimit(resource, &limit);
+ if (!android::base::ParseInt(args[1], &resource)) {
+ return Error() << "unable to parse resource, " << args[1];
}
- LOG(WARNING) << "ignoring setrlimit " << args[1] << " " << args[2] << " " << args[3];
- return -1;
+
+ struct rlimit limit;
+ if (!android::base::ParseUint(args[2], &limit.rlim_cur)) {
+ return Error() << "unable to parse rlim_cur, " << args[2];
+ }
+ if (!android::base::ParseUint(args[3], &limit.rlim_max)) {
+ return Error() << "unable to parse rlim_max, " << args[3];
+ }
+
+ if (setrlimit(resource, &limit) == -1) {
+ return ErrnoError() << "setrlimit failed";
+ }
+ return Success();
}
-static int do_start(const std::vector<std::string>& args) {
+static Result<Success> do_start(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_start: Service " << args[1] << " not found";
- return -1;
- }
- if (!svc->Start())
- return -1;
- return 0;
+ if (!svc) return Error() << "service " << args[1] << " not found";
+ if (!svc->Start()) return Error() << "failed to start service";
+ return Success();
}
-static int do_stop(const std::vector<std::string>& args) {
+static Result<Success> do_stop(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_stop: Service " << args[1] << " not found";
- return -1;
- }
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
- return 0;
+ return Success();
}
-static int do_restart(const std::vector<std::string>& args) {
+static Result<Success> do_restart(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
- if (!svc) {
- LOG(ERROR) << "do_restart: Service " << args[1] << " not found";
- return -1;
- }
+ if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
- return 0;
+ return Success();
}
-static int do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
- return 0;
+ return Success();
}
-static int do_symlink(const std::vector<std::string>& args) {
- return symlink(args[1].c_str(), args[2].c_str());
-}
-
-static int do_rm(const std::vector<std::string>& args) {
- return unlink(args[1].c_str());
-}
-
-static int do_rmdir(const std::vector<std::string>& args) {
- return rmdir(args[1].c_str());
-}
-
-static int do_sysclktz(const std::vector<std::string>& args) {
- struct timezone tz = {};
- if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
- return 0;
+static Result<Success> do_symlink(const std::vector<std::string>& args) {
+ if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
+ return ErrnoError() << "symlink() failed";
}
- return -1;
+ return Success();
}
-static int do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const std::vector<std::string>& args) {
+ if (unlink(args[1].c_str()) < 0) {
+ return ErrnoError() << "unlink() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+ if (rmdir(args[1].c_str()) < 0) {
+ return ErrnoError() << "rmdir() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+ struct timezone tz = {};
+ if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+ return Error() << "Unable to parse mins_west_of_gmt";
+ }
+
+ if (settimeofday(nullptr, &tz) == -1) {
+ return ErrnoError() << "settimeofday() failed";
+ }
+ return Success();
+}
+
+static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
int mode = -1;
bool loaded = fs_mgr_load_verity_state(&mode);
if (loaded && mode != VERITY_MODE_DEFAULT) {
ActionManager::GetInstance().QueueEventTrigger("verity-logging");
}
- return loaded ? 0 : 1;
+ if (!loaded) return Error() << "Could not load verity state";
+
+ return Success();
}
static void verity_update_property(fstab_rec *fstab, const char *mount_point,
@@ -647,55 +655,113 @@
property_set("partition."s + mount_point + ".verified", std::to_string(mode));
}
-static int do_verity_update_state(const std::vector<std::string>& args) {
- return fs_mgr_update_verity_state(verity_update_property) ? 0 : 1;
+static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+ if (!fs_mgr_update_verity_state(verity_update_property)) {
+ return Error() << "fs_mgr_update_verity_state() failed";
+ }
+ return Success();
}
-static int do_write(const std::vector<std::string>& args) {
- std::string err;
- if (!WriteFile(args[1], args[2], &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_write(const std::vector<std::string>& args) {
+ if (auto result = WriteFile(args[1], args[2]); !result) {
+ return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
- return 0;
+
+ return Success();
}
-static int do_copy(const std::vector<std::string>& args) {
- std::string data;
- std::string err;
- if (!ReadFile(args[1], &data, &err)) {
- LOG(ERROR) << err;
- return -1;
+static Result<Success> do_readahead(const std::vector<std::string>& args) {
+ struct stat sb;
+
+ if (stat(args[1].c_str(), &sb)) {
+ return ErrnoError() << "Error opening " << args[1];
}
- if (!WriteFile(args[2], data, &err)) {
- LOG(ERROR) << err;
- return -1;
+
+ // We will do readahead in a forked process in order not to block init
+ // since it may block while it reads the
+ // filesystem metadata needed to locate the requested blocks. This
+ // occurs frequently with ext[234] on large files using indirect blocks
+ // instead of extents, giving the appearance that the call blocks until
+ // the requested data has been read.
+ pid_t pid = fork();
+ if (pid == 0) {
+ android::base::Timer t;
+ if (S_ISREG(sb.st_mode)) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Error opening file: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ PLOG(ERROR) << "Error readahead file: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ } else if (S_ISDIR(sb.st_mode)) {
+ char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+ std::unique_ptr<FTS, decltype(&fts_close)> fts(
+ fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+ if (!fts) {
+ PLOG(ERROR) << "Error opening directory: " << args[1];
+ _exit(EXIT_FAILURE);
+ }
+ // Traverse the entire hierarchy and do readahead
+ for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+ ftsent = fts_read(fts.get())) {
+ if (ftsent->fts_info & FTS_F) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Error opening file: " << args[1];
+ continue;
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+ }
+ }
+ }
+ }
+ LOG(INFO) << "Readahead " << args[1] << " took " << t;
+ _exit(0);
+ } else if (pid < 0) {
+ return ErrnoError() << "Fork failed";
}
- return 0;
+ return Success();
}
-static int do_chown(const std::vector<std::string>& args) {
- uid_t uid;
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[1] << "': " << decode_uid_err;
- return -1;
+static Result<Success> do_copy(const std::vector<std::string>& args) {
+ auto file_contents = ReadFile(args[1]);
+ if (!file_contents) {
+ return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
+ }
+ if (auto result = WriteFile(args[2], *file_contents); !result) {
+ return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
+ }
+
+ return Success();
+}
+
+static Result<Success> do_chown(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
}
// GID is optional and pushes the index of path out by one if specified.
const std::string& path = (args.size() == 4) ? args[3] : args[2];
- gid_t gid = -1;
+ Result<gid_t> gid = -1;
if (args.size() == 4) {
- if (!DecodeUid(args[2], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[2] << "': " << decode_uid_err;
- return -1;
+ gid = DecodeUid(args[2]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
}
}
- if (lchown(path.c_str(), uid, gid) == -1) return -errno;
+ if (lchown(path.c_str(), *uid, *gid) == -1) {
+ return ErrnoError() << "lchown() failed";
+ }
- return 0;
+ return Success();
}
static mode_t get_mode(const char *s) {
@@ -711,15 +777,15 @@
return mode;
}
-static int do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const std::vector<std::string>& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
- return -errno;
+ return ErrnoError() << "fchmodat() failed";
}
- return 0;
+ return Success();
}
-static int do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const std::vector<std::string>& args) {
int ret = 0;
struct flag_type {const char* name; int value;};
@@ -736,8 +802,7 @@
for (size_t i = 1; i < args.size(); ++i) {
if (android::base::StartsWith(args[i], "--")) {
if (!in_flags) {
- LOG(ERROR) << "restorecon - flags must precede paths";
- return -1;
+ return Error() << "flags must precede paths";
}
bool found = false;
for (size_t j = 0; flags[j].name; ++j) {
@@ -748,26 +813,27 @@
}
}
if (!found) {
- LOG(ERROR) << "restorecon - bad flag " << args[i];
- return -1;
+ return Error() << "bad flag " << args[i];
}
} else {
in_flags = false;
if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
- ret = -errno;
+ ret = errno;
}
}
}
- return ret;
+
+ if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+ return Success();
}
-static int do_restorecon_recursive(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
std::vector<std::string> non_const_args(args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
return do_restorecon(non_const_args);
}
-static int do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const std::vector<std::string>& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -782,88 +848,73 @@
case 1:
case 0: severity = android::base::FATAL; break;
default:
- LOG(ERROR) << "loglevel: invalid log level " << log_level;
- return -EINVAL;
+ return Error() << "invalid log level " << log_level;
}
android::base::SetMinimumLogSeverity(severity);
- return 0;
+ return Success();
}
-static int do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
load_persist_props();
- return 0;
+ return Success();
}
-static int do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
load_system_props();
- return 0;
+ return Success();
}
-static int do_wait(const std::vector<std::string>& args) {
- if (args.size() == 2) {
- return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
- } else if (args.size() == 3) {
- int timeout;
- if (android::base::ParseInt(args[2], &timeout)) {
- return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
+static Result<Success> do_wait(const std::vector<std::string>& args) {
+ auto timeout = kCommandRetryTimeout;
+ if (args.size() == 3) {
+ int timeout_int;
+ if (!android::base::ParseInt(args[2], &timeout_int)) {
+ return Error() << "failed to parse timeout";
}
+ timeout = std::chrono::seconds(timeout_int);
}
- return -1;
+
+ if (wait_for_file(args[1].c_str(), timeout) != 0) {
+ return Error() << "wait_for_file() failed";
+ }
+
+ return Success();
}
-static int do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
if (!is_legal_property_name(name)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: bad name";
- return -1;
+ return Error() << "is_legal_property_name(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: value too long";
- return -1;
+ return Error() << "value too long";
}
if (!start_waiting_for_property(name, value)) {
- LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
- << "\") failed: init already in waiting";
- return -1;
+ return Error() << "already waiting for a property";
}
- return 0;
-}
-
-/*
- * Callback to make a directory from the ext4 code
- */
-static int do_installkeys_ensure_dir_exists(const char* dir) {
- if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
- return -1;
- }
-
- return 0;
+ return Success();
}
static bool is_file_crypto() {
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static int do_installkey(const std::vector<std::string>& args) {
- if (!is_file_crypto()) {
- return 0;
- }
+static Result<Success> do_installkey(const std::vector<std::string>& args) {
+ if (!is_file_crypto()) return Success();
+
auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
- if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
- PLOG(ERROR) << "Failed to create " << unencrypted_dir;
- return -1;
+ if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
+ return ErrnoError() << "Failed to create " << unencrypted_dir;
}
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"enablefilecrypto"};
return do_exec(exec_args);
}
-static int do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const std::vector<std::string>& args) {
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"init_user0"};
return do_exec(exec_args);
@@ -898,6 +949,7 @@
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
+ {"readahead", {1, 1, do_readahead}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
diff --git a/init/builtins.h b/init/builtins.h
index b110f61..f66ae19 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -23,11 +23,12 @@
#include <vector>
#include "keyword_map.h"
+#include "result.h"
namespace android {
namespace init {
-using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
+using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
public:
BuiltinFunctionMap() {}
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
index 0cb639a..cc5b948 100644
--- a/init/descriptors.cpp
+++ b/init/descriptors.cpp
@@ -86,8 +86,7 @@
int flags =
((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
bool passcred = types.size() > 1 && types[1] == "passcred";
- return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
- sehandle);
+ return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
}
const std::string SocketInfo::key() const {
diff --git a/init/devices.cpp b/init/devices.cpp
index 13cf991..d59f53c 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
#include <selinux/android.h>
#include <selinux/selinux.h>
+#include "selinux.h"
#include "ueventd.h"
#include "util.h"
@@ -224,18 +225,13 @@
auto[mode, uid, gid] = GetDevicePermissions(path, links);
mode |= (block ? S_IFBLK : S_IFCHR);
- char* secontext = nullptr;
- if (sehandle_) {
- std::vector<const char*> c_links;
- for (const auto& link : links) {
- c_links.emplace_back(link.c_str());
- }
- c_links.emplace_back(nullptr);
- if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
- PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
- return;
- }
- setfscreatecon(secontext);
+ std::string secontext;
+ if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
+ PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
+ return;
+ }
+ if (!secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
dev_t dev = makedev(major, minor);
@@ -250,7 +246,7 @@
}
/* If the node already exists update its SELinux label to handle cases when
* it was created with the wrong context during coldboot procedure. */
- if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
+ if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
char* fcon = nullptr;
int rc = lgetfilecon(path.c_str(), &fcon);
if (rc < 0) {
@@ -258,10 +254,10 @@
goto out;
}
- bool different = strcmp(fcon, secontext) != 0;
+ bool different = fcon != secontext;
freecon(fcon);
- if (different && lsetfilecon(path.c_str(), secontext)) {
+ if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
<< "' device";
}
@@ -273,8 +269,7 @@
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
- if (secontext) {
- freecon(secontext);
+ if (!secontext.empty()) {
setfscreatecon(nullptr);
}
}
@@ -351,7 +346,7 @@
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
for (const auto& link : links) {
- if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
+ if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
@@ -415,7 +410,7 @@
devpath = "/dev/" + Basename(uevent.path);
}
- mkdir_recursive(Dirname(devpath), 0755, sehandle_);
+ mkdir_recursive(Dirname(devpath), 0755);
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
@@ -426,7 +421,6 @@
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
- sehandle_(selinux_android_file_context_handle()),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
diff --git a/init/devices.h b/init/devices.h
index c64f5fb..dd44337 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -124,7 +124,6 @@
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
- selabel_handle* sehandle_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index ac4ab9b..eba00cb 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -35,13 +35,13 @@
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
std::string platform_device_dir = fake_sys_root.path + platform_device;
- mkdir_recursive(platform_device_dir, 0777, nullptr);
+ mkdir_recursive(platform_device_dir, 0777);
std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
- mkdir_recursive(platform_bus, 0777, nullptr);
+ mkdir_recursive(platform_bus, 0777);
symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
- mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
+ mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
std::vector<std::string> result;
result = device_handler_.GetBlockDeviceSymlinks(uevent);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index b9fa2ce..e335fd1 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,24 +23,22 @@
namespace android {
namespace init {
-bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
- *err = "single argument needed for import\n";
- return false;
+ return Error() << "single argument needed for import\n";
}
std::string conf_file;
bool ret = expand_props(args[1], &conf_file);
if (!ret) {
- *err = "error while expanding import";
- return false;
+ return Error() << "error while expanding import";
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
if (filename_.empty()) filename_ = filename;
imports_.emplace_back(std::move(conf_file), line);
- return true;
+ return Success();
}
void ImportParser::EndFile() {
diff --git a/init/import_parser.h b/init/import_parser.h
index 0d04e0e..5a2f894 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,8 +28,8 @@
class ImportParser : public SectionParser {
public:
ImportParser(Parser* parser) : parser_(parser) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index 69d1564..c65d846 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -16,27 +16,17 @@
#include "init.h"
-#include <ctype.h>
#include <dirent.h>
-#include <errno.h>
#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
#include <paths.h>
#include <seccomp_policy.h>
#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <android-base/chrono_utils.h>
@@ -44,25 +34,22 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <keyutils.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <selinux/selinux.h>
-#include <fstream>
#include <memory>
#include <optional>
-#include <vector>
-#include "bootchart.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
+#include "security.h"
+#include "selinux.h"
#include "signal_handler.h"
#include "ueventd.h"
#include "util.h"
@@ -73,14 +60,10 @@
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Timer;
-using android::base::unique_fd;
namespace android {
namespace init {
-struct selabel_handle *sehandle;
-struct selabel_handle *sehandle_prop;
-
static int property_triggers_enabled = 0;
static char qemu[32];
@@ -255,7 +238,7 @@
}
}
-static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t;
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -274,210 +257,20 @@
}
property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
- return 0;
+ return Success();
}
-/*
- * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
- * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
- * Does nothing if Hardware RNG is not present.
- *
- * Since we don't yet trust the quality of Hardware RNG, these bytes are not
- * mixed into the primary pool of Linux RNG and the entropy estimate is left
- * unmodified.
- *
- * If the HW RNG device /dev/hw_random is present, we require that at least
- * 512 bytes read from it are written into Linux RNG. QA is expected to catch
- * devices/configurations where these I/O operations are blocking for a long
- * time. We do not reboot or halt on failures, as this is a best-effort
- * attempt.
- */
-static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args) {
- unique_fd hwrandom_fd(
- TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
- if (hwrandom_fd == -1) {
- if (errno == ENOENT) {
- LOG(INFO) << "/dev/hw_random not found";
- // It's not an error to not have a Hardware RNG.
- return 0;
- }
- PLOG(ERROR) << "Failed to open /dev/hw_random";
- return -1;
- }
-
- unique_fd urandom_fd(
- TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
- if (urandom_fd == -1) {
- PLOG(ERROR) << "Failed to open /dev/urandom";
- return -1;
- }
-
- char buf[512];
- size_t total_bytes_written = 0;
- while (total_bytes_written < sizeof(buf)) {
- ssize_t chunk_size =
- TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to read from /dev/hw_random";
- return -1;
- } else if (chunk_size == 0) {
- LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
- return -1;
- }
-
- chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
- if (chunk_size == -1) {
- PLOG(ERROR) << "Failed to write to /dev/urandom";
- return -1;
- }
- total_bytes_written += chunk_size;
- }
-
- LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
- return 0;
-}
-
-static void security_failure() {
- LOG(ERROR) << "Security failure...";
- panic();
-}
-
-static bool set_highest_available_option_value(std::string path, int min, int max)
-{
- std::ifstream inf(path, std::fstream::in);
- if (!inf) {
- LOG(ERROR) << "Cannot open for reading: " << path;
- return false;
- }
-
- int current = max;
- while (current >= min) {
- // try to write out new value
- std::string str_val = std::to_string(current);
- std::ofstream of(path, std::fstream::out);
- if (!of) {
- LOG(ERROR) << "Cannot open for writing: " << path;
- return false;
- }
- of << str_val << std::endl;
- of.close();
-
- // check to make sure it was recorded
- inf.seekg(0);
- std::string str_rec;
- inf >> str_rec;
- if (str_val.compare(str_rec) == 0) {
- break;
- }
- current--;
- }
- inf.close();
-
- if (current < min) {
- LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
- return false;
- }
- return true;
-}
-
-#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
-#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
-
-/* __attribute__((unused)) due to lack of mips support: see mips block
- * in set_mmap_rnd_bits_action */
-static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
- std::string path;
- if (compat) {
- path = MMAP_RND_COMPAT_PATH;
- } else {
- path = MMAP_RND_PATH;
- }
-
- return set_highest_available_option_value(path, min, start);
-}
-
-/*
- * Set /proc/sys/vm/mmap_rnd_bits and potentially
- * /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
- * Returns -1 if unable to set these to an acceptable value.
- *
- * To support this sysctl, the following upstream commits are needed:
- *
- * d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
- * e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
- * 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
- * 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
- * ec9ee4acd97c drivers: char: random: add get_random_long()
- * 5ef11c35ce86 mm: ASLR: use get_random_long()
- */
-static int set_mmap_rnd_bits_action(const std::vector<std::string>& args) {
-/* values are arch-dependent */
-#if defined(USER_MODE_LINUX)
- /* uml does not support mmap_rnd_bits */
- return 0;
-#elif defined(__aarch64__)
- /* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
- if (set_mmap_rnd_bits_min(33, 24, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- return 0;
- }
-#elif defined(__x86_64__)
- /* x86_64 supports 28 - 32 bits */
- if (set_mmap_rnd_bits_min(32, 32, false)
- && set_mmap_rnd_bits_min(16, 16, true)) {
- return 0;
- }
-#elif defined(__arm__) || defined(__i386__)
- /* check to see if we're running on 64-bit kernel */
- bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
- /* supported 32-bit architecture must have 16 bits set */
- if (set_mmap_rnd_bits_min(16, 16, h64)) {
- return 0;
- }
-#elif defined(__mips__) || defined(__mips64__)
- // TODO: add mips support b/27788820
- return 0;
-#else
- LOG(ERROR) << "Unknown architecture";
-#endif
-
- LOG(ERROR) << "Unable to set adequate mmap entropy value!";
- security_failure();
- return -1;
-}
-
-#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
-#define KPTR_RESTRICT_MINVALUE 2
-#define KPTR_RESTRICT_MAXVALUE 4
-
-/* Set kptr_restrict to the highest available level.
- *
- * Aborts if unable to set this to an acceptable value.
- */
-static int set_kptr_restrict_action(const std::vector<std::string>& args)
-{
- std::string path = KPTR_RESTRICT_PATH;
-
- if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
- LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
- security_failure();
- }
- return 0;
-}
-
-static int keychord_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
keychord_init();
- return 0;
+ return Success();
}
-static int console_init_action(const std::vector<std::string>& args)
-{
+static Result<Success> console_init_action(const std::vector<std::string>& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
- return 0;
+ return Success();
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -559,18 +352,16 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static int property_enable_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
- return 0;
+ return Success();
}
-static int queue_property_triggers_action(const std::vector<std::string>& args)
-{
+static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyActions();
- return 0;
+ return Success();
}
static void global_seccomp() {
@@ -582,355 +373,6 @@
});
}
-static void selinux_init_all_handles(void)
-{
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
- sehandle_prop = selinux_android_prop_context_handle();
-}
-
-enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
-
-static selinux_enforcing_status selinux_status_from_cmdline() {
- selinux_enforcing_status status = SELINUX_ENFORCING;
-
- import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
- if (key == "androidboot.selinux" && value == "permissive") {
- status = SELINUX_PERMISSIVE;
- }
- });
-
- return status;
-}
-
-static bool selinux_is_enforcing(void)
-{
- if (ALLOW_PERMISSIVE_SELINUX) {
- return selinux_status_from_cmdline() == SELINUX_ENFORCING;
- }
- return true;
-}
-
-static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-
- property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "audit_callback invoked with null data arguments!";
- return 0;
- }
-
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
- d->cr->pid, d->cr->uid, d->cr->gid);
- return 0;
-}
-
-/*
- * Forks, executes the provided program in the child, and waits for the completion in the parent.
- * Child's stderr is captured and logged using LOG(ERROR).
- *
- * Returns true if the child exited with status code 0, returns false otherwise.
- */
-static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
- char* const envp[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[0]));
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- if (execve(filename, argv, envp) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- TEMP_FAILURE_RETRY(close(pipe_fds[1]));
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- TEMP_FAILURE_RETRY(close(child_out_fd));
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
-static bool read_first_line(const char* file, std::string* line) {
- line->clear();
-
- std::string contents;
- if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
- return false;
- }
- std::istringstream in(contents);
- std::getline(in, *line);
- return true;
-}
-
-static bool selinux_find_precompiled_split_policy(std::string* file) {
- file->clear();
-
- static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
- if (access(precompiled_sepolicy, R_OK) == -1) {
- return false;
- }
- std::string actual_plat_id;
- if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
- &actual_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
- return false;
- }
- std::string precompiled_plat_id;
- if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
- &precompiled_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/vendor/etc/selinux/"
- "precompiled_sepolicy.plat_and_mapping.sha256";
- return false;
- }
- if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
- return false;
- }
-
- *file = precompiled_sepolicy;
- return true;
-}
-
-static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
-
-static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
-
-/*
- * Loads SELinux policy split across platform/system and non-platform/vendor files.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_split_policy() {
- // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
- // * platform -- policy needed due to logic contained in the system image,
- // * non-platform -- policy needed due to logic contained in the vendor image,
- // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
- // with newer versions of platform policy.
- //
- // secilc is invoked to compile the above three policy files into a single monolithic policy
- // file. This file is then loaded into the kernel.
-
- // Load precompiled policy from vendor image, if a matching policy is found there. The policy
- // must match the platform policy on the system image.
- std::string precompiled_sepolicy_file;
- if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
- android::base::unique_fd fd(
- open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
- if (fd != -1) {
- if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
- return false;
- }
- return true;
- }
- }
- // No suitable precompiled policy could be loaded
-
- LOG(INFO) << "Compiling SELinux policy";
-
- // Determine the highest policy language version supported by the kernel
- set_selinuxmnt("/sys/fs/selinux");
- int max_policy_version = security_policyvers();
- if (max_policy_version == -1) {
- PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
- return false;
- }
-
- // We store the output of the compilation on /dev because this is the most convenient tmpfs
- // storage mount available this early in the boot sequence.
- char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
- android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
- if (compiled_sepolicy_fd < 0) {
- PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
- return false;
- }
-
- // clang-format off
- const char* compile_args[] = {
- "/system/bin/secilc",
- plat_policy_cil_file,
- "-M", "true",
- // Target the highest policy language version supported by the kernel
- "-c", std::to_string(max_policy_version).c_str(),
- "/system/etc/selinux/mapping_sepolicy.cil",
- "/vendor/etc/selinux/nonplat_sepolicy.cil",
- "-o", compiled_sepolicy,
- // We don't care about file_contexts output by the compiler
- "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
- nullptr};
- // clang-format on
-
- if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
- unlink(compiled_sepolicy);
- return false;
- }
- unlink(compiled_sepolicy);
-
- LOG(INFO) << "Loading compiled SELinux policy";
- if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
- return false;
- }
-
- return true;
-}
-
-/*
- * Loads SELinux policy from a monolithic file.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_monolithic_policy() {
- LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
- if (selinux_android_load_policy() < 0) {
- PLOG(ERROR) << "Failed to load monolithic SELinux policy";
- return false;
- }
- return true;
-}
-
-/*
- * Loads SELinux policy into the kernel.
- *
- * Returns true upon success, false otherwise (failure cause is logged).
- */
-static bool selinux_load_policy() {
- return selinux_is_split_policy_device() ? selinux_load_split_policy()
- : selinux_load_monolithic_policy();
-}
-
-static void selinux_initialize(bool in_kernel_domain) {
- Timer t;
-
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
- cb.func_audit = audit_callback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
- if (in_kernel_domain) {
- LOG(INFO) << "Loading SELinux policy";
- if (!selinux_load_policy()) {
- panic();
- }
-
- bool kernel_enforcing = (security_getenforce() == 1);
- bool is_enforcing = selinux_is_enforcing();
- if (kernel_enforcing != is_enforcing) {
- if (security_setenforce(is_enforcing)) {
- PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
- security_failure();
- }
- }
-
- std::string err;
- if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
- LOG(ERROR) << err;
- security_failure();
- }
-
- // init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
- } else {
- selinux_init_all_handles();
- }
-}
-
-// 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.
-static void selinux_restore_context() {
- LOG(INFO) << "Running restorecon...";
- selinux_android_restorecon("/dev", 0);
- selinux_android_restorecon("/dev/kmsg", 0);
- if constexpr (WORLD_WRITABLE_KMSG) {
- selinux_android_restorecon("/dev/kmsg_debug", 0);
- }
- selinux_android_restorecon("/dev/socket", 0);
- selinux_android_restorecon("/dev/random", 0);
- selinux_android_restorecon("/dev/urandom", 0);
- selinux_android_restorecon("/dev/__properties__", 0);
- selinux_android_restorecon("/plat_property_contexts", 0);
- selinux_android_restorecon("/nonplat_property_contexts", 0);
- selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- selinux_android_restorecon("/dev/device-mapper", 0);
-
- selinux_android_restorecon("/sbin/mke2fs_static", 0);
- selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
-}
-
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -1013,7 +455,7 @@
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
if constexpr (WORLD_WRITABLE_KMSG) {
- mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
+ mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
}
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
@@ -1036,13 +478,14 @@
global_seccomp();
// Set up SELinux, loading the SELinux policy.
- selinux_initialize(true);
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
- PLOG(ERROR) << "restorecon failed";
- security_failure();
+ PLOG(ERROR) << "restorecon failed of /init failed";
+ panic();
}
setenv("INIT_SECOND_STAGE", "true", 1);
@@ -1058,7 +501,7 @@
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(ERROR) << "execv(\"" << path << "\") failed";
- security_failure();
+ panic();
}
// At this point we're in the second stage of init.
@@ -1099,8 +542,9 @@
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
- selinux_initialize(false);
- selinux_restore_context();
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
+ SelinuxRestoreContext();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
@@ -1132,9 +576,9 @@
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
- am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
- am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
- am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
+ am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+ am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
@@ -1143,7 +587,7 @@
// 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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+ am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
diff --git a/init/init.h b/init/init.h
index 92b9b70..50a7c83 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,8 +18,7 @@
#define _INIT_INIT_H
#include <string>
-
-#include <selinux/label.h>
+#include <vector>
#include "action.h"
#include "parser.h"
@@ -33,8 +32,6 @@
// TODO: Have an Init class and remove all globals.
extern const char *ENV[32];
extern std::string default_console;
-extern struct selabel_handle *sehandle;
-extern struct selabel_handle *sehandle_prop;
extern std::vector<std::string> late_import_paths;
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 2062290..27659f9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -37,7 +37,7 @@
void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
Add(name, 0, 0, [function](const std::vector<std::string>&) {
function();
- return 0;
+ return Success();
});
}
@@ -156,10 +156,9 @@
"execute 3";
// clang-format on
// WriteFile() ensures the right mode is set
- std::string err;
- ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script, &err));
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/a.rc", dir_a_script));
- ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5", &err));
+ ASSERT_TRUE(WriteFile(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
// clang-format off
std::string start_script = "import " + std::string(first_import.path) + "\n"
@@ -175,7 +174,7 @@
auto execute_command = [&num_executed](const std::vector<std::string>& args) {
EXPECT_EQ(2U, args.size());
EXPECT_EQ(++num_executed, std::stoi(args[1]));
- return 0;
+ return Success();
};
TestFunctionMap test_function_map;
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 481d637..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -22,6 +22,8 @@
#include <android-base/stringprintf.h>
+#include "result.h"
+
namespace android {
namespace init {
@@ -34,20 +36,17 @@
virtual ~KeywordMap() {
}
- const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
+ const Result<Function> FindFunction(const std::vector<std::string>& args) const {
using android::base::StringPrintf;
- if (args.empty()) {
- *err = "keyword needed, but not provided";
- return nullptr;
- }
+ if (args.empty()) return Error() << "Keyword needed, but not provided";
+
auto& keyword = args[0];
auto num_args = args.size() - 1;
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
- *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
- return nullptr;
+ return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
}
auto function_info = function_info_it->second;
@@ -55,22 +54,18 @@
auto min_args = std::get<0>(function_info);
auto max_args = std::get<1>(function_info);
if (min_args == max_args && num_args != min_args) {
- *err = StringPrintf("%s requires %zu argument%s",
- keyword.c_str(), min_args,
- (min_args > 1 || min_args == 0) ? "s" : "");
- return nullptr;
+ return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
- *err = StringPrintf("%s requires at least %zu argument%s",
- keyword.c_str(), min_args,
- min_args > 1 ? "s" : "");
+ return Error() << StringPrintf("%s requires at least %zu argument%s",
+ keyword.c_str(), min_args, min_args > 1 ? "s" : "");
} else {
- *err = StringPrintf("%s requires between %zu and %zu arguments",
- keyword.c_str(), min_args, max_args);
+ return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+ keyword.c_str(), min_args, max_args);
}
- return nullptr;
}
return std::get<Function>(function_info);
diff --git a/init/parser.cpp b/init/parser.cpp
index c6f4f45..8a4e798 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -67,9 +67,8 @@
if (android::base::StartsWith(args[0], prefix.c_str())) {
if (section_parser) section_parser->EndSection();
- std::string ret_err;
- if (!callback(std::move(args), &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+ if (auto result = callback(std::move(args)); !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
section_parser = nullptr;
break;
@@ -78,16 +77,16 @@
if (section_parsers_.count(args[0])) {
if (section_parser) section_parser->EndSection();
section_parser = section_parsers_[args[0]].get();
- std::string ret_err;
- if (!section_parser->ParseSection(std::move(args), filename, state.line,
- &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+ if (auto result =
+ section_parser->ParseSection(std::move(args), filename, state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
- std::string ret_err;
- if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
- LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+ if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
+ !result) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
args.clear();
@@ -102,15 +101,14 @@
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
- std::string data;
- std::string err;
- if (!ReadFile(path, &data, &err)) {
- LOG(ERROR) << err;
+ auto config_contents = ReadFile(path);
+ if (!config_contents) {
+ LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
- data.push_back('\n'); // TODO: fix parse_config.
- ParseData(path, data);
+ config_contents->push_back('\n'); // TODO: fix parse_config.
+ ParseData(path, *config_contents);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
diff --git a/init/parser.h b/init/parser.h
index fd65ad6..4ab24a4 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -22,6 +22,8 @@
#include <string>
#include <vector>
+#include "result.h"
+
// SectionParser is an interface that can parse a given 'section' in init.
//
// You can implement up to 4 functions below, with ParseSection() being mandatory.
@@ -51,9 +53,9 @@
class SectionParser {
public:
virtual ~SectionParser() {}
- virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) = 0;
- virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+ virtual Result<Success> ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) = 0;
+ virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
virtual void EndSection(){};
virtual void EndFile(){};
};
@@ -67,7 +69,7 @@
// Similar to ParseSection() and ParseLineSection(), this function returns bool with false
// indicating a failure and has an std::string* err parameter into which an error string can
// be written.
- using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+ using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
Parser();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 925cc9b..e07550a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -68,6 +68,8 @@
static int property_set_fd = -1;
+static struct selabel_handle* sehandle_prop;
+
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
@@ -586,14 +588,14 @@
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
- std::string data;
- std::string err;
- if (!ReadFile(filename, &data, &err)) {
- PLOG(WARNING) << "Couldn't load property file: " << err;
+ auto file_contents = ReadFile(filename);
+ if (!file_contents) {
+ PLOG(WARNING) << "Couldn't load property file '" << filename
+ << "': " << file_contents.error();
return;
}
- data.push_back('\n');
- load_properties(&data[0], filter);
+ file_contents->push_back('\n');
+ load_properties(file_contents->data(), filter);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
}
@@ -733,11 +735,30 @@
load_recovery_id_prop();
}
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void start_property_service() {
+ sehandle_prop = selinux_android_prop_context_handle();
+
+ selinux_callback cb;
+ cb.func_audit = SelinuxAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, nullptr, sehandle);
+ false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cfd703e..24ccdfc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -48,6 +48,7 @@
#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include <selinux/selinux.h>
#include "capabilities.h"
#include "init.h"
@@ -519,7 +520,7 @@
auto shutdown_handler = [cmd, command, reboot_target,
run_fsck](const std::vector<std::string>&) {
DoReboot(cmd, command, reboot_target, run_fsck);
- return 0;
+ return Success();
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
diff --git a/init/result.h b/init/result.h
new file mode 100644
index 0000000..64fa69f
--- /dev/null
+++ b/init/result.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 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 file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a std::string describing an error, which can be accessed via
+// Result<T>::error().
+//
+// Success is a typedef that aids in creating Result<T> that do not contain a return value.
+// Result<Success> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning Success() from a function that returns Result<Success> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. Each of these classes
+// take an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() additionally appends ": " + strerror(errno) to the end of the failure string to aid
+// in interacting with C APIs.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Error() << "SomeOtherCppFunction(" << input << ") failed";
+// }
+// if (!c_api_function(output)) {
+// return ErrnoError() << "c_api_function(" << output << ") failed";
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#ifndef _INIT_RESULT_H
+#define _INIT_RESULT_H
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+#include <variant>
+
+namespace android {
+namespace init {
+
+class Error {
+ public:
+ Error() : append_errno_(0) {}
+
+ template <typename T>
+ Error&& operator<<(T&& t) {
+ ss_ << std::forward<T>(t);
+ return std::move(*this);
+ }
+
+ const std::string str() const {
+ if (append_errno_) {
+ return ss_.str() + ": " + strerror(append_errno_);
+ }
+ return ss_.str();
+ }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ protected:
+ Error(int append_errno) : append_errno_(append_errno) {}
+
+ private:
+ std::stringstream ss_;
+ int append_errno_;
+};
+
+class ErrnoError : public Error {
+ public:
+ ErrnoError() : Error(errno) {}
+};
+
+template <typename T>
+class Result {
+ public:
+ template <typename... U>
+ Result(U&&... result) : contents_(std::in_place_index_t<0>(), std::forward<U>(result)...) {}
+
+ Result(Error&& fb) : contents_(std::in_place_index_t<1>(), fb.str()) {}
+
+ bool has_value() const { return contents_.index() == 0; }
+
+ T& value() & { return std::get<0>(contents_); }
+ const T& value() const & { return std::get<0>(contents_); }
+ T&& value() && { return std::get<0>(std::move(contents_)); }
+ const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+ const std::string& error() const & { return std::get<1>(contents_); }
+ std::string&& error() && { return std::get<1>(std::move(contents_)); }
+ const std::string&& error() const && { return std::get<1>(std::move(contents_)); }
+
+ explicit operator bool() const { return has_value(); }
+
+ T& operator*() & { return value(); }
+ const T& operator*() const & { return value(); }
+ T&& operator*() && { return std::move(value()); }
+ const T&& operator*() const && { return std::move(value()); }
+
+ T* operator->() { return &value(); }
+ const T* operator->() const { return &value(); }
+
+ private:
+ std::variant<T, std::string> contents_;
+};
+
+using Success = std::monostate;
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..ca65013
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 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 "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success"));
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+ Result<Success> result = Success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ(Success(), *result);
+ EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+ // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+ // implicitly constructed into a Result<Success> object.
+
+ auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+ ASSERT_TRUE(MakeRvalueSuccessResult());
+ ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+ EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+ EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+ Result<Success> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ("failure1", result.error());
+}
+
+TEST(result, result_error_empty) {
+ Result<Success> result = Error();
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ("", result.error());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<Success> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(5, 'a');
+
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error(), "");
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.cpp b/init/security.cpp
new file mode 100644
index 0000000..f8976de
--- /dev/null
+++ b/init/security.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 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 "security.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
+// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
+// Does nothing if Hardware RNG is not present.
+//
+// Since we don't yet trust the quality of Hardware RNG, these bytes are not
+// mixed into the primary pool of Linux RNG and the entropy estimate is left
+// unmodified.
+//
+// If the HW RNG device /dev/hw_random is present, we require that at least
+// 512 bytes read from it are written into Linux RNG. QA is expected to catch
+// devices/configurations where these I/O operations are blocking for a long
+// time. We do not reboot or halt on failures, as this is a best-effort
+// attempt.
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+ unique_fd hwrandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (hwrandom_fd == -1) {
+ if (errno == ENOENT) {
+ LOG(INFO) << "/dev/hw_random not found";
+ // It's not an error to not have a Hardware RNG.
+ return Success();
+ }
+ return ErrnoError() << "Failed to open /dev/hw_random";
+ }
+
+ unique_fd urandom_fd(
+ TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (urandom_fd == -1) {
+ return ErrnoError() << "Failed to open /dev/urandom";
+ }
+
+ char buf[512];
+ size_t total_bytes_written = 0;
+ while (total_bytes_written < sizeof(buf)) {
+ ssize_t chunk_size =
+ TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to read from /dev/hw_random";
+ } else if (chunk_size == 0) {
+ return Error() << "Failed to read from /dev/hw_random: EOF";
+ }
+
+ chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
+ if (chunk_size == -1) {
+ return ErrnoError() << "Failed to write to /dev/urandom";
+ }
+ total_bytes_written += chunk_size;
+ }
+
+ LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
+ return Success();
+}
+
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+ std::ifstream inf(path, std::fstream::in);
+ if (!inf) {
+ LOG(ERROR) << "Cannot open for reading: " << path;
+ return false;
+ }
+
+ int current = max;
+ while (current >= min) {
+ // try to write out new value
+ std::string str_val = std::to_string(current);
+ std::ofstream of(path, std::fstream::out);
+ if (!of) {
+ LOG(ERROR) << "Cannot open for writing: " << path;
+ return false;
+ }
+ of << str_val << std::endl;
+ of.close();
+
+ // check to make sure it was recorded
+ inf.seekg(0);
+ std::string str_rec;
+ inf >> str_rec;
+ if (str_val.compare(str_rec) == 0) {
+ break;
+ }
+ current--;
+ }
+ inf.close();
+
+ if (current < min) {
+ LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
+ return false;
+ }
+ return true;
+}
+
+#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
+#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
+
+// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
+static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
+ std::string path;
+ if (compat) {
+ path = MMAP_RND_COMPAT_PATH;
+ } else {
+ path = MMAP_RND_PATH;
+ }
+
+ return SetHighestAvailableOptionValue(path, min, start);
+}
+
+// Set /proc/sys/vm/mmap_rnd_bits and potentially
+// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
+// Returns -1 if unable to set these to an acceptable value.
+//
+// To support this sysctl, the following upstream commits are needed:
+//
+// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
+// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
+// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
+// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
+// ec9ee4acd97c drivers: char: random: add get_random_long()
+// 5ef11c35ce86 mm: ASLR: use get_random_long()
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+// values are arch-dependent
+#if defined(USER_MODE_LINUX)
+ // uml does not support mmap_rnd_bits
+ return Success();
+#elif defined(__aarch64__)
+ // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
+ if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__x86_64__)
+ // x86_64 supports 28 - 32 bits
+ if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+ return Success();
+ }
+#elif defined(__arm__) || defined(__i386__)
+ // check to see if we're running on 64-bit kernel
+ bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
+ // supported 32-bit architecture must have 16 bits set
+ if (SetMmapRndBitsMin(16, 16, h64)) {
+ return Success();
+ }
+#elif defined(__mips__) || defined(__mips64__)
+ // TODO: add mips support b/27788820
+ return Success();
+#else
+ LOG(ERROR) << "Unknown architecture";
+#endif
+
+ LOG(ERROR) << "Unable to set adequate mmap entropy value!";
+ panic();
+ return Error();
+}
+
+#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
+#define KPTR_RESTRICT_MINVALUE 2
+#define KPTR_RESTRICT_MAXVALUE 4
+
+// Set kptr_restrict to the highest available level.
+//
+// Aborts if unable to set this to an acceptable value.
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+ std::string path = KPTR_RESTRICT_PATH;
+
+ if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
+ LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
+ panic();
+ return Error();
+ }
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/security.h b/init/security.h
new file mode 100644
index 0000000..31e5790
--- /dev/null
+++ b/init/security.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _INIT_SECURITY_H
+#define _INIT_SECURITY_H
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
+Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
+Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/selinux.cpp b/init/selinux.cpp
new file mode 100644
index 0000000..b9305ed
--- /dev/null
+++ b/init/selinux.cpp
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2017 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 file contains the functions that initialize SELinux during boot as well as helper functions
+// 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.
+
+// The SEPolicy on Android comes in two variants: monolithic and split.
+
+// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
+// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
+
+// The split policy is for supporting treble devices. It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy). This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
+
+// The split SEPolicy is loaded as described below:
+// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
+// Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
+// were used to compile this precompiled policy. The system partition contains a similar sha256
+// of the parts of the SEPolicy that it currently contains. If these two hashes match, then the
+// system loads this precompiled_sepolicy directly.
+// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
+// init needs to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it
+// is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
+// and load it. That function contains even more documentation with the specific implementation
+// details of how the SEPolicy is compiled if needed.
+
+#include "selinux.h"
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <selinux/android.h>
+
+#include "log.h"
+#include "util.h"
+
+using android::base::Timer;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+static struct selabel_handle* sehandle = nullptr;
+
+namespace {
+
+enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+
+EnforcingStatus StatusFromCmdline() {
+ EnforcingStatus status = SELINUX_ENFORCING;
+
+ import_kernel_cmdline(false,
+ [&](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.selinux" && value == "permissive") {
+ status = SELINUX_PERMISSIVE;
+ }
+ });
+
+ return status;
+}
+
+bool IsEnforcing() {
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ return StatusFromCmdline() == SELINUX_ENFORCING;
+ }
+ return true;
+}
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ const char* envp[] = {_PATH_DEFPATH, nullptr};
+ if (execve(filename, argv, (char**)envp) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ TEMP_FAILURE_RETRY(close(child_out_fd));
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
+bool ReadFirstLine(const char* file, std::string* line) {
+ line->clear();
+
+ std::string contents;
+ if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
+ return false;
+ }
+ std::istringstream in(contents);
+ std::getline(in, *line);
+ return true;
+}
+
+bool FindPrecompiledSplitPolicy(std::string* file) {
+ file->clear();
+
+ static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
+ if (access(precompiled_sepolicy, R_OK) == -1) {
+ return false;
+ }
+ std::string actual_plat_id;
+ if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+ return false;
+ }
+ std::string precompiled_plat_id;
+ if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
+ &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/vendor/etc/selinux/"
+ "precompiled_sepolicy.plat_and_mapping.sha256";
+ return false;
+ }
+ if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+ return false;
+ }
+
+ *file = precompiled_sepolicy;
+ return true;
+}
+
+constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+bool IsSplitPolicyDevice() {
+ return access(plat_policy_cil_file, R_OK) != -1;
+}
+
+bool LoadSplitPolicy() {
+ // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+ // * platform -- policy needed due to logic contained in the system image,
+ // * non-platform -- policy needed due to logic contained in the vendor image,
+ // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+ // with newer versions of platform policy.
+ //
+ // secilc is invoked to compile the above three policy files into a single monolithic policy
+ // file. This file is then loaded into the kernel.
+
+ // Load precompiled policy from vendor image, if a matching policy is found there. The policy
+ // must match the platform policy on the system image.
+ std::string precompiled_sepolicy_file;
+ if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+ if (fd != -1) {
+ if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
+ return false;
+ }
+ return true;
+ }
+ }
+ // No suitable precompiled policy could be loaded
+
+ LOG(INFO) << "Compiling SELinux policy";
+
+ // Determine the highest policy language version supported by the kernel
+ set_selinuxmnt("/sys/fs/selinux");
+ int max_policy_version = security_policyvers();
+ if (max_policy_version == -1) {
+ PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
+ return false;
+ }
+
+ // We store the output of the compilation on /dev because this is the most convenient tmpfs
+ // storage mount available this early in the boot sequence.
+ char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+ unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+ if (compiled_sepolicy_fd < 0) {
+ PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+ return false;
+ }
+
+ // clang-format off
+ const char* compile_args[] = {
+ "/system/bin/secilc",
+ plat_policy_cil_file,
+ "-M", "true",
+ // Target the highest policy language version supported by the kernel
+ "-c", std::to_string(max_policy_version).c_str(),
+ "/system/etc/selinux/mapping_sepolicy.cil",
+ "/vendor/etc/selinux/nonplat_sepolicy.cil",
+ "-o", compiled_sepolicy,
+ // We don't care about file_contexts output by the compiler
+ "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
+ nullptr};
+ // clang-format on
+
+ if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
+ unlink(compiled_sepolicy);
+ return false;
+ }
+ unlink(compiled_sepolicy);
+
+ LOG(INFO) << "Loading compiled SELinux policy";
+ if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+ return false;
+ }
+
+ return true;
+}
+
+bool LoadMonolithicPolicy() {
+ LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+ if (selinux_android_load_policy() < 0) {
+ PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+ return false;
+ }
+ return true;
+}
+
+bool LoadPolicy() {
+ return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
+}
+
+} // namespace
+
+void SelinuxInitialize() {
+ Timer t;
+
+ LOG(INFO) << "Loading SELinux policy";
+ if (!LoadPolicy()) {
+ panic();
+ }
+
+ bool kernel_enforcing = (security_getenforce() == 1);
+ bool is_enforcing = IsEnforcing();
+ if (kernel_enforcing != is_enforcing) {
+ if (security_setenforce(is_enforcing)) {
+ PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
+ panic();
+ }
+ }
+
+ if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
+ LOG(ERROR) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
+ panic();
+ }
+
+ // init's first stage can't set properties, so pass the time to the second stage.
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
+}
+
+// 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.
+void SelinuxRestoreContext() {
+ LOG(INFO) << "Running restorecon...";
+ selinux_android_restorecon("/dev", 0);
+ selinux_android_restorecon("/dev/kmsg", 0);
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ selinux_android_restorecon("/dev/kmsg_debug", 0);
+ }
+ selinux_android_restorecon("/dev/socket", 0);
+ selinux_android_restorecon("/dev/random", 0);
+ selinux_android_restorecon("/dev/urandom", 0);
+ selinux_android_restorecon("/dev/__properties__", 0);
+ selinux_android_restorecon("/plat_property_contexts", 0);
+ selinux_android_restorecon("/nonplat_property_contexts", 0);
+ selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon("/dev/device-mapper", 0);
+
+ selinux_android_restorecon("/sbin/mke2fs_static", 0);
+ selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
+}
+
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
+void SelinuxSetupKernelLogging() {
+ selinux_callback cb;
+ cb.func_log = selinux_klog_callback;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+}
+
+// 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
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ char* context;
+ if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ std::vector<const char*> c_aliases;
+ for (const auto& alias : aliases) {
+ c_aliases.emplace_back(alias.c_str());
+ }
+ c_aliases.emplace_back(nullptr);
+
+ char* context;
+ if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.h b/init/selinux.h
new file mode 100644
index 0000000..7b880ec
--- /dev/null
+++ b/init/selinux.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _INIT_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelinuxInitialize();
+void SelinuxRestoreContext();
+
+void SelinuxSetupKernelLogging();
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index 6f756fa..1b5cc19 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -333,12 +333,12 @@
[] (const auto& info) { LOG(INFO) << *info; });
}
-bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCapabilities(const std::vector<std::string>& args) {
capabilities_ = 0;
if (!CapAmbientSupported()) {
- *err = "capabilities requested but the kernel does not support ambient capabilities";
- return false;
+ return Error()
+ << "capabilities requested but the kernel does not support ambient capabilities";
}
unsigned int last_valid_cap = GetLastValidCap();
@@ -350,74 +350,71 @@
const std::string& arg = args[i];
int res = LookupCap(arg);
if (res < 0) {
- *err = StringPrintf("invalid capability '%s'", arg.c_str());
- return false;
+ return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
}
unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
if (cap > last_valid_cap) {
- *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
- return false;
+ return Error() << StringPrintf("capability '%s' not supported by the kernel",
+ arg.c_str());
}
capabilities_[cap] = true;
}
- return true;
+ return Success();
}
-bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseClass(const std::vector<std::string>& args) {
classnames_ = std::set<std::string>(args.begin() + 1, args.end());
- return true;
+ return Success();
}
-bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseConsole(const std::vector<std::string>& args) {
flags_ |= SVC_CONSOLE;
console_ = args.size() > 1 ? "/dev/" + args[1] : "";
- return true;
+ return Success();
}
-bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseCritical(const std::vector<std::string>& args) {
flags_ |= SVC_CRITICAL;
- return true;
+ return Success();
}
-bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseDisabled(const std::vector<std::string>& args) {
flags_ |= SVC_DISABLED;
flags_ |= SVC_RC_DISABLED;
- return true;
+ return Success();
}
-bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) {
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &gid_, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[1] + "': " + decode_uid_err;
- return false;
+Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
+ auto gid = DecodeUid(args[1]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
}
+ gid_ = *gid;
+
for (std::size_t n = 2; n < args.size(); n++) {
- gid_t gid;
- if (!DecodeUid(args[n], &gid, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[n] + "': " + decode_uid_err;
- return false;
+ gid = DecodeUid(args[n]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
}
- supp_gids_.emplace_back(gid);
+ supp_gids_.emplace_back(*gid);
}
- return true;
+ return Success();
}
-bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParsePriority(const std::vector<std::string>& args) {
priority_ = 0;
if (!ParseInt(args[1], &priority_,
static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- *err = StringPrintf("process priority value must be range %d - %d",
- ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
- return false;
+ return Error() << StringPrintf("process priority value must be range %d - %d",
+ ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
}
- return true;
+ return Success();
}
-bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseIoprio(const std::vector<std::string>& args) {
if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
- *err = "priority value must be range 0 - 7";
- return false;
+ return Error() << "priority value must be range 0 - 7";
}
if (args[1] == "rt") {
@@ -427,14 +424,13 @@
} else if (args[1] == "idle") {
ioprio_class_ = IoSchedClass_IDLE;
} else {
- *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
- return false;
+ return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
}
- return true;
+ return Success();
}
-bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
if (ParseInt(args[i], &code)) {
@@ -443,22 +439,24 @@
LOG(WARNING) << "ignoring invalid keycode: " << args[i];
}
}
- return true;
+ return Success();
}
-bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOneshot(const std::vector<std::string>& args) {
flags_ |= SVC_ONESHOT;
- return true;
+ return Success();
}
-bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOnrestart(const std::vector<std::string>& args) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
int line = onrestart_.NumCommands() + 1;
- onrestart_.AddCommand(str_args, line, err);
- return true;
+ if (auto result = onrestart_.AddCommand(str_args, line); !result) {
+ return Error() << "cannot add Onrestart command: " << result.error();
+ }
+ return Success();
}
-bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseNamespace(const std::vector<std::string>& args) {
for (size_t i = 1; i < args.size(); i++) {
if (args[i] == "pid") {
namespace_flags_ |= CLONE_NEWPID;
@@ -467,135 +465,125 @@
} else if (args[i] == "mnt") {
namespace_flags_ |= CLONE_NEWNS;
} else {
- *err = "namespace must be 'pid' or 'mnt'";
- return false;
+ return Error() << "namespace must be 'pid' or 'mnt'";
}
}
- return true;
+ return Success();
}
-bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseOomScoreAdjust(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
- *err = "oom_score_adjust value must be in range -1000 - +1000";
- return false;
+ return Error() << "oom_score_adjust value must be in range -1000 - +1000";
}
- return true;
+ return Success();
}
-bool Service::ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSwappiness(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &swappiness_, 0)) {
- *err = "swappiness value must be equal or greater than 0";
- return false;
+ return Error() << "swappiness value must be equal or greater than 0";
}
- return true;
+ return Success();
}
-bool Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgLimitInBytes(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
- *err = "limit_in_bytes value must be equal or greater than 0";
- return false;
+ return Error() << "limit_in_bytes value must be equal or greater than 0";
}
- return true;
+ return Success();
}
-bool Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args) {
if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
- *err = "soft_limit_in_bytes value must be equal or greater than 0";
- return false;
+ return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
}
- return true;
+ return Success();
}
-bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
- return true;
+ return Success();
}
-bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
envvars_.emplace_back(args[1], args[2]);
- return true;
+ return Success();
}
-bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
if (args[1] == "critical") {
flags_ |= SVC_SHUTDOWN_CRITICAL;
- return true;
+ return Success();
}
- return false;
+ return Error() << "Invalid shutdown option";
}
template <typename T>
-bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- uid_t uid = 0;
- gid_t gid = 0;
+ Result<uid_t> uid = 0;
+ Result<gid_t> gid = 0;
std::string context = args.size() > 6 ? args[6] : "";
- std::string decode_uid_err;
if (args.size() > 4) {
- if (!DecodeUid(args[4], &uid, &decode_uid_err)) {
- *err = "Unable to find UID for '" + args[4] + "': " + decode_uid_err;
- return false;
+ uid = DecodeUid(args[4]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
}
}
if (args.size() > 5) {
- if (!DecodeUid(args[5], &gid, &decode_uid_err)) {
- *err = "Unable to find GID for '" + args[5] + "': " + decode_uid_err;
- return false;
+ gid = DecodeUid(args[5]);
+ if (!gid) {
+ return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
}
}
- auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+ auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
auto old =
std::find_if(descriptors_.begin(), descriptors_.end(),
[&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
if (old != descriptors_.end()) {
- *err = "duplicate descriptor " + args[1] + " " + args[2];
- return false;
+ return Error() << "duplicate descriptor " << args[1] << " " << args[2];
}
descriptors_.emplace_back(std::move(descriptor));
- return true;
+ return Success();
}
// name type perm [ uid gid context ]
-bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseSocket(const std::vector<std::string>& args) {
if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
!StartsWith(args[2], "seqpacket")) {
- *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
- return false;
+ return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
}
- return AddDescriptor<SocketInfo>(args, err);
+ return AddDescriptor<SocketInfo>(args);
}
// name type perm [ uid gid context ]
-bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseFile(const std::vector<std::string>& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
- *err = "file type must be 'r', 'w' or 'rw'";
- return false;
+ return Error() << "file type must be 'r', 'w' or 'rw'";
}
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
- *err = "file name must not be relative";
- return false;
+ return Error() << "file name must not be relative";
}
- return AddDescriptor<FileInfo>(args, err);
+ return AddDescriptor<FileInfo>(args);
}
-bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
- std::string decode_uid_err;
- if (!DecodeUid(args[1], &uid_, &decode_uid_err)) {
- *err = "Unable to find UID for '" + args[1] + "': " + decode_uid_err;
- return false;
+Result<Success> Service::ParseUser(const std::vector<std::string>& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
}
- return true;
+ uid_ = *uid;
+ return Success();
}
-bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseWritepid(const std::vector<std::string>& args) {
writepid_files_.assign(args.begin() + 1, args.end());
- return true;
+ return Success();
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
@@ -643,15 +631,13 @@
return option_parsers;
}
-bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
+Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args, err);
+ auto parser = parser_map.FindFunction(args);
- if (!parser) {
- return false;
- }
+ if (!parser) return Error() << parser.error();
- return (this->*parser)(args, err);
+ return std::invoke(*parser, this, args);
}
bool Service::ExecStart() {
@@ -979,34 +965,35 @@
if (command_arg > 2 && args[1] != "-") {
seclabel = args[1];
}
- uid_t uid = 0;
+ Result<uid_t> uid = 0;
if (command_arg > 3) {
- std::string decode_uid_err;
- if (!DecodeUid(args[2], &uid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[2] << "': " << decode_uid_err;
+ uid = DecodeUid(args[2]);
+ if (!uid) {
+ LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
return nullptr;
}
}
- gid_t gid = 0;
+ Result<gid_t> gid = 0;
std::vector<gid_t> supp_gids;
if (command_arg > 4) {
- std::string decode_uid_err;
- if (!DecodeUid(args[3], &gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find GID for '" << args[3] << "': " << decode_uid_err;
+ gid = DecodeUid(args[3]);
+ if (!gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
return nullptr;
}
std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
for (size_t i = 0; i < nr_supp_gids; ++i) {
- gid_t supp_gid;
- if (!DecodeUid(args[4 + i], &supp_gid, &decode_uid_err)) {
- LOG(ERROR) << "Unable to find UID for '" << args[4 + i] << "': " << decode_uid_err;
+ auto supp_gid = DecodeUid(args[4 + i]);
+ if (!supp_gid) {
+ LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+ << "': " << supp_gid.error();
return nullptr;
}
- supp_gids.push_back(supp_gid);
+ supp_gids.push_back(*supp_gid);
}
}
- return std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
+ return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
namespace_flags, seclabel, str_args);
}
@@ -1039,32 +1026,29 @@
}
}
-bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() < 3) {
- *err = "services must have a name and a program";
- return false;
+ return Error() << "services must have a name and a program";
}
const std::string& name = args[1];
if (!IsValidName(name)) {
- *err = StringPrintf("invalid service name '%s'", name.c_str());
- return false;
+ return Error() << "invalid service name '" << name << "'";
}
Service* old_service = service_list_->FindService(name);
if (old_service) {
- *err = "ignored duplicate definition of service '" + name + "'";
- return false;
+ return Error() << "ignored duplicate definition of service '" << name << "'";
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
- return true;
+ return Success();
}
-bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- return service_ ? service_->ParseLine(std::move(args), err) : false;
+Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return service_ ? service_->ParseLine(std::move(args)) : Success();
}
void ServiceParser::EndSection() {
diff --git a/init/service.h b/init/service.h
index 6c143cb..fe85129 100644
--- a/init/service.h
+++ b/init/service.h
@@ -76,7 +76,7 @@
static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- bool ParseLine(const std::vector<std::string>& args, std::string* err);
+ Result<Success> ParseLine(const std::vector<std::string>& args);
bool ExecStart();
bool Start();
bool StartIfNotDisabled();
@@ -119,8 +119,7 @@
const std::vector<std::string>& args() const { return args_; }
private:
- using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
- std::string* err);
+ using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
void NotifyStateChange(const std::string& new_state) const;
@@ -130,32 +129,32 @@
void KillProcessGroup(int signal);
void SetProcessAttributes();
- bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
- bool ParseClass(const std::vector<std::string>& args, std::string* err);
- bool ParseConsole(const std::vector<std::string>& args, std::string* err);
- bool ParseCritical(const std::vector<std::string>& args, std::string* err);
- bool ParseDisabled(const std::vector<std::string>& args, std::string* err);
- bool ParseGroup(const std::vector<std::string>& args, std::string* err);
- bool ParsePriority(const std::vector<std::string>& args, std::string* err);
- bool ParseIoprio(const std::vector<std::string>& args, std::string* err);
- bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
- bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
- bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
- bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
- bool ParseMemcgLimitInBytes(const std::vector<std::string>& args, std::string* err);
- bool ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args, std::string* err);
- bool ParseMemcgSwappiness(const std::vector<std::string>& args, std::string* err);
- bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
- bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
- bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
- bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
- bool ParseSocket(const std::vector<std::string>& args, std::string* err);
- bool ParseFile(const std::vector<std::string>& args, std::string* err);
- bool ParseUser(const std::vector<std::string>& args, std::string* err);
- bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+ Result<Success> ParseCapabilities(const std::vector<std::string>& args);
+ Result<Success> ParseClass(const std::vector<std::string>& args);
+ Result<Success> ParseConsole(const std::vector<std::string>& args);
+ Result<Success> ParseCritical(const std::vector<std::string>& args);
+ Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseGroup(const std::vector<std::string>& args);
+ Result<Success> ParsePriority(const std::vector<std::string>& args);
+ Result<Success> ParseIoprio(const std::vector<std::string>& args);
+ Result<Success> ParseKeycodes(const std::vector<std::string>& args);
+ Result<Success> ParseOneshot(const std::vector<std::string>& args);
+ Result<Success> ParseOnrestart(const std::vector<std::string>& args);
+ Result<Success> ParseOomScoreAdjust(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
+ Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
+ Result<Success> ParseNamespace(const std::vector<std::string>& args);
+ Result<Success> ParseSeclabel(const std::vector<std::string>& args);
+ Result<Success> ParseSetenv(const std::vector<std::string>& args);
+ Result<Success> ParseShutdown(const std::vector<std::string>& args);
+ Result<Success> ParseSocket(const std::vector<std::string>& args);
+ Result<Success> ParseFile(const std::vector<std::string>& args);
+ Result<Success> ParseUser(const std::vector<std::string>& args);
+ Result<Success> ParseWritepid(const std::vector<std::string>& args);
template <typename T>
- bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+ Result<Success> AddDescriptor(const std::vector<std::string>& args);
static unsigned long next_start_order_;
static bool is_exec_service_running_;
@@ -242,9 +241,9 @@
class ServiceParser : public SectionParser {
public:
ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 62e46f4..98d876f 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -132,29 +132,29 @@
ASSERT_EQ("", svc->seclabel());
}
if (uid) {
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->uid());
+ auto decoded_uid = DecodeUid("log");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->uid());
} else {
ASSERT_EQ(0U, svc->uid());
}
if (gid) {
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->gid());
+ auto decoded_uid = DecodeUid("shell");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->gid());
} else {
ASSERT_EQ(0U, svc->gid());
}
if (supplementary_gids) {
ASSERT_EQ(2U, svc->supp_gids().size());
- uid_t decoded_uid;
- std::string err;
- ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
- ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
- ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
+
+ auto decoded_uid = DecodeUid("system");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);
+
+ decoded_uid = DecodeUid("adb");
+ ASSERT_TRUE(decoded_uid);
+ ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);
} else {
ASSERT_EQ(0U, svc->supp_gids().size());
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c0eae1e..1435d82 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -36,6 +36,7 @@
#include "devices.h"
#include "firmware_handler.h"
#include "log.h"
+#include "selinux.h"
#include "uevent_listener.h"
#include "ueventd_parser.h"
#include "util.h"
@@ -222,10 +223,10 @@
using namespace std::placeholders;
std::vector<SysfsPermissions> sysfs_permissions;
std::vector<Permissions> dev_permissions;
- parser.AddSingleLineParser(
- "/sys/", std::bind(ParsePermissionsLine, _1, _2, &sysfs_permissions, nullptr));
+ parser.AddSingleLineParser("/sys/",
+ std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));
parser.AddSingleLineParser("/dev/",
- std::bind(ParsePermissionsLine, _1, _2, nullptr, &dev_permissions));
+ std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));
parser.ParseConfig("/ueventd.rc");
parser.ParseConfig("/vendor/ueventd.rc");
@@ -257,9 +258,8 @@
LOG(INFO) << "ueventd started!";
- selinux_callback cb;
- cb.func_log = selinux_klog_callback;
- selinux_set_callback(SELINUX_CB_LOG, cb);
+ SelinuxSetupKernelLogging();
+ SelabelInitialize();
DeviceHandler device_handler = CreateDeviceHandler();
UeventListener uevent_listener;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 02e0d42..e831b8b 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -24,18 +24,16 @@
namespace android {
namespace init {
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions) {
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
if (is_sysfs && args.size() != 5) {
- *err = "/sys/ lines must have 5 entries";
- return false;
+ return Error() << "/sys/ lines must have 5 entries";
}
if (!is_sysfs && args.size() != 4) {
- *err = "/dev/ lines must have 4 entries";
- return false;
+ return Error() << "/dev/ lines must have 4 entries";
}
auto it = args.begin();
@@ -49,23 +47,20 @@
char* end_pointer = 0;
mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
if (end_pointer == nullptr || *end_pointer != '\0') {
- *err = "invalid mode '" + perm_string + "'";
- return false;
+ return Error() << "invalid mode '" << perm_string << "'";
}
std::string& uid_string = *it++;
passwd* pwd = getpwnam(uid_string.c_str());
if (!pwd) {
- *err = "invalid uid '" + uid_string + "'";
- return false;
+ return Error() << "invalid uid '" << uid_string << "'";
}
uid_t uid = pwd->pw_uid;
std::string& gid_string = *it++;
struct group* grp = getgrnam(gid_string.c_str());
if (!grp) {
- *err = "invalid gid '" + gid_string + "'";
- return false;
+ return Error() << "invalid gid '" << gid_string << "'";
}
gid_t gid = grp->gr_gid;
@@ -74,53 +69,49 @@
} else {
out_dev_permissions->emplace_back(name, perm, uid, gid);
}
- return true;
+ return Success();
}
-bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line, std::string* err) {
+Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
- *err = "subsystems must have exactly one name";
- return false;
+ return Error() << "subsystems must have exactly one name";
}
if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {
- *err = "ignoring duplicate subsystem entry";
- return false;
+ return Error() << "ignoring duplicate subsystem entry";
}
subsystem_.name_ = args[1];
- return true;
+ return Success();
}
-bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
if (args[1] == "uevent_devname") {
subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
- return true;
+ return Success();
}
if (args[1] == "uevent_devpath") {
subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
- return true;
+ return Success();
}
- *err = "invalid devname '" + args[1] + "'";
- return false;
+ return Error() << "invalid devname '" << args[1] << "'";
}
-bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
if (args[1].front() != '/') {
- *err = "dirname '" + args[1] + " ' does not start with '/'";
- return false;
+ return Error() << "dirname '" << args[1] << " ' does not start with '/'";
}
subsystem_.dir_name_ = args[1];
- return true;
+ return Success();
}
-bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
- using OptionParser =
- bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+
static class OptionParserMap : public KeywordMap<OptionParser> {
private:
const Map& map() const override {
@@ -134,13 +125,11 @@
}
} parser_map;
- auto parser = parser_map.FindFunction(args, err);
+ auto parser = parser_map.FindFunction(args);
- if (!parser) {
- return false;
- }
+ if (!parser) return Error() << parser.error();
- return (this->*parser)(std::move(args), err);
+ return std::invoke(*parser, this, std::move(args));
}
void SubsystemParser::EndSection() {
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 51d83ef..18d1027 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -29,22 +29,22 @@
class SubsystemParser : public SectionParser {
public:
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
- bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
- std::string* err) override;
- bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
void EndSection() override;
private:
- bool ParseDevName(std::vector<std::string>&& args, std::string* err);
- bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+ Result<Success> ParseDevName(std::vector<std::string>&& args);
+ Result<Success> ParseDirName(std::vector<std::string>&& args);
Subsystem subsystem_;
std::vector<Subsystem>* subsystems_;
};
-bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions);
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions);
} // namespace init
} // namespace android
diff --git a/init/util.cpp b/init/util.cpp
index fdcb22d..fcf7ca8 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,7 @@
#include <selinux/android.h>
#include "reboot.h"
+#include "selinux.h"
#ifdef _INIT_INIT_H
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -56,30 +57,20 @@
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
// DecodeUid() - decodes and returns the given string, which can be either the
-// numeric or name representation, into the integer uid or gid. Returns
-// UINT_MAX on error.
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
- *uid = UINT_MAX;
- *err = "";
-
+// numeric or name representation, into the integer uid or gid.
+Result<uid_t> DecodeUid(const std::string& name) {
if (isalpha(name[0])) {
passwd* pwd = getpwnam(name.c_str());
- if (!pwd) {
- *err = "getpwnam failed: "s + strerror(errno);
- return false;
- }
- *uid = pwd->pw_uid;
- return true;
+ if (!pwd) return ErrnoError() << "getpwnam failed";
+
+ return pwd->pw_uid;
}
errno = 0;
uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));
- if (errno) {
- *err = "strtoul failed: "s + strerror(errno);
- return false;
- }
- *uid = result;
- return true;
+ if (errno) return ErrnoError() << "strtoul failed";
+
+ return result;
}
/*
@@ -89,7 +80,7 @@
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon, selabel_handle* sehandle) {
+ const char* socketcon) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -116,11 +107,9 @@
return -1;
}
- char *filecon = NULL;
- if (sehandle) {
- if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
- setfscreatecon(filecon);
- }
+ std::string secontext;
+ if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
if (passcred) {
@@ -134,8 +123,9 @@
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
int savederrno = errno;
- setfscreatecon(NULL);
- freecon(filecon);
+ if (!secontext.empty()) {
+ setfscreatecon(nullptr);
+ }
if (ret) {
errno = savederrno;
@@ -164,65 +154,55 @@
return -1;
}
-bool ReadFile(const std::string& path, std::string* content, std::string* err) {
- content->clear();
- *err = "";
-
+Result<std::string> ReadFile(const std::string& path) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
- *err = "Unable to open '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "open() failed";
}
// For security reasons, disallow world-writable
// or group-writable files.
struct stat sb;
if (fstat(fd, &sb) == -1) {
- *err = "fstat failed for '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "fstat failed()";
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- *err = "Skipping insecure file '" + path + "'";
- return false;
+ return Error() << "Skipping insecure file";
}
- if (!android::base::ReadFdToString(fd, content)) {
- *err = "Unable to read '" + path + "': " + strerror(errno);
- return false;
+ std::string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ return ErrnoError() << "Unable to read file contents";
}
- return true;
+ return content;
}
-bool WriteFile(const std::string& path, const std::string& content, std::string* err) {
- *err = "";
-
+Result<Success> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
- *err = "Unable to open '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "open() failed";
}
if (!android::base::WriteStringToFd(content, fd)) {
- *err = "Unable to write to '" + path + "': " + strerror(errno);
- return false;
+ return ErrnoError() << "Unable to write file contents";
}
- return true;
+ return Success();
}
-int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
+bool mkdir_recursive(const std::string& path, mode_t mode) {
std::string::size_type slash = 0;
while ((slash = path.find('/', slash + 1)) != std::string::npos) {
auto directory = path.substr(0, slash);
struct stat info;
if (stat(directory.c_str(), &info) != 0) {
- auto ret = make_dir(directory.c_str(), mode, sehandle);
- if (ret && errno != EEXIST) return ret;
+ auto ret = make_dir(directory, mode);
+ if (!ret && errno != EEXIST) return false;
}
}
- auto ret = make_dir(path.c_str(), mode, sehandle);
- if (ret && errno != EEXIST) return ret;
- return 0;
+ auto ret = make_dir(path, mode);
+ if (!ret && errno != EEXIST) return false;
+ return true;
}
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
@@ -249,26 +229,21 @@
}
}
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
- int rc;
-
- char *secontext = NULL;
-
- if (sehandle) {
- selabel_lookup(sehandle, &secontext, path, mode);
- setfscreatecon(secontext);
+bool make_dir(const std::string& path, mode_t mode) {
+ std::string secontext;
+ if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
+ setfscreatecon(secontext.c_str());
}
- rc = mkdir(path, mode);
+ int rc = mkdir(path.c_str(), mode);
- if (secontext) {
+ if (!secontext.empty()) {
int save_errno = errno;
- freecon(secontext);
- setfscreatecon(NULL);
+ setfscreatecon(nullptr);
errno = save_errno;
}
- return rc;
+ return rc == 0;
}
/*
diff --git a/init/util.h b/init/util.h
index 29c10cb..298aa1c 100644
--- a/init/util.h
+++ b/init/util.h
@@ -28,6 +28,8 @@
#include <android-base/chrono_utils.h>
#include <selinux/label.h>
+#include "result.h"
+
#define COLDBOOT_DONE "/dev/.coldboot_done"
using android::base::boot_clock;
@@ -37,18 +39,18 @@
namespace init {
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon, selabel_handle* sehandle);
+ const char* socketcon);
-bool ReadFile(const std::string& path, std::string* content, std::string* err);
-bool WriteFile(const std::string& path, const std::string& content, std::string* err);
+Result<std::string> ReadFile(const std::string& path);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
-bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
+Result<uid_t> DecodeUid(const std::string& name);
-int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
+bool mkdir_recursive(const std::string& pathname, mode_t mode);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
-int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
+bool make_dir(const std::string& path, mode_t mode);
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
bool is_dir(const char* pathname);
bool expand_props(const std::string& src, std::string* dst);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index c16ab74..007d10e 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -30,61 +30,51 @@
namespace init {
TEST(util, ReadFile_ENOENT) {
- std::string s("hello");
- std::string err;
errno = 0;
- EXPECT_FALSE(ReadFile("/proc/does-not-exist", &s, &err));
- EXPECT_EQ("Unable to open '/proc/does-not-exist': No such file or directory", err);
+ auto file_contents = ReadFile("/proc/does-not-exist");
EXPECT_EQ(ENOENT, errno);
- EXPECT_EQ("", s); // s was cleared.
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: No such file or directory", file_contents.error());
}
TEST(util, ReadFileGroupWriteable) {
std::string s("hello");
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
- EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error());
}
TEST(util, ReadFileWorldWiteable) {
std::string s("hello");
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno);
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno);
- EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err);
- EXPECT_EQ("", s); // s was cleared.
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_FALSE(file_contents) << strerror(errno);
+ EXPECT_EQ("Skipping insecure file", file_contents.error());
}
TEST(util, ReadFileSymbolicLink) {
- std::string s("hello");
errno = 0;
// lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- std::string err;
- EXPECT_FALSE(ReadFile("/charger", &s, &err));
- EXPECT_EQ("Unable to open '/charger': Too many symbolic links encountered", err);
+ auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
- EXPECT_EQ("", s); // s was cleared.
+ ASSERT_FALSE(file_contents);
+ EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error());
}
TEST(util, ReadFileSuccess) {
- std::string s("hello");
- std::string err;
- EXPECT_TRUE(ReadFile("/proc/version", &s, &err));
- EXPECT_EQ("", err);
- EXPECT_GT(s.length(), 6U);
- EXPECT_EQ('\n', s[s.length() - 1]);
- s[5] = 0;
- EXPECT_STREQ("Linux", s.c_str());
+ auto file_contents = ReadFile("/proc/version");
+ ASSERT_TRUE(file_contents);
+ EXPECT_GT(file_contents->length(), 6U);
+ EXPECT_EQ('\n', file_contents->at(file_contents->length() - 1));
+ (*file_contents)[5] = 0;
+ EXPECT_STREQ("Linux", file_contents->c_str());
}
TEST(util, WriteFileBinary) {
@@ -95,29 +85,23 @@
ASSERT_EQ(10u, contents.size());
TemporaryFile tf;
- std::string err;
ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(WriteFile(tf.path, contents, &err)) << strerror(errno);
- EXPECT_EQ("", err);
+ EXPECT_TRUE(WriteFile(tf.path, contents)) << strerror(errno);
- std::string read_back_contents;
- EXPECT_TRUE(ReadFile(tf.path, &read_back_contents, &err)) << strerror(errno);
- EXPECT_EQ("", err);
- EXPECT_EQ(contents, read_back_contents);
- EXPECT_EQ(10u, read_back_contents.size());
+ auto read_back_contents = ReadFile(tf.path);
+ ASSERT_TRUE(read_back_contents) << strerror(errno);
+ EXPECT_EQ(contents, *read_back_contents);
+ EXPECT_EQ(10u, read_back_contents->size());
}
TEST(util, WriteFileNotExist) {
std::string s("hello");
- std::string s2("hello");
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
- std::string err;
- EXPECT_TRUE(WriteFile(path, s, &err));
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(s, s2);
+ EXPECT_TRUE(WriteFile(path, s));
+ auto file_contents = ReadFile(path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ(s, *file_contents);
struct stat sb;
int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
EXPECT_NE(-1, fd);
@@ -127,37 +111,30 @@
}
TEST(util, WriteFileExist) {
- std::string s2("");
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- std::string err;
- EXPECT_TRUE(WriteFile(tf.path, "1hello1", &err)) << strerror(errno);
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_STREQ("1hello1", s2.c_str());
- EXPECT_TRUE(WriteFile(tf.path, "2ll2", &err));
- EXPECT_EQ("", err);
- EXPECT_TRUE(ReadFile(tf.path, &s2, &err));
- EXPECT_EQ("", err);
- EXPECT_STREQ("2ll2", s2.c_str());
+ EXPECT_TRUE(WriteFile(tf.path, "1hello1")) << strerror(errno);
+ auto file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("1hello1", *file_contents);
+ EXPECT_TRUE(WriteFile(tf.path, "2ll2"));
+ file_contents = ReadFile(tf.path);
+ ASSERT_TRUE(file_contents);
+ EXPECT_EQ("2ll2", *file_contents);
}
TEST(util, DecodeUid) {
- uid_t decoded_uid;
- std::string err;
+ auto decoded_uid = DecodeUid("root");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(0U, *decoded_uid);
- EXPECT_TRUE(DecodeUid("root", &decoded_uid, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(0U, decoded_uid);
+ decoded_uid = DecodeUid("toot");
+ EXPECT_FALSE(decoded_uid);
+ EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error());
- EXPECT_FALSE(DecodeUid("toot", &decoded_uid, &err));
- EXPECT_EQ("getpwnam failed: No such file or directory", err);
- EXPECT_EQ(UINT_MAX, decoded_uid);
-
- EXPECT_TRUE(DecodeUid("123", &decoded_uid, &err));
- EXPECT_EQ("", err);
- EXPECT_EQ(123U, decoded_uid);
+ decoded_uid = DecodeUid("123");
+ EXPECT_TRUE(decoded_uid);
+ EXPECT_EQ(123U, *decoded_uid);
}
TEST(util, is_dir) {
@@ -170,7 +147,7 @@
TEST(util, mkdir_recursive) {
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
- EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
EXPECT_TRUE(is_dir(path1.c_str()));
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
@@ -182,7 +159,7 @@
TEST(util, mkdir_recursive_extra_slashes) {
TemporaryDir test_dir;
std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
- EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
+ EXPECT_TRUE(mkdir_recursive(path, 0755));
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
EXPECT_TRUE(is_dir(path1.c_str()));
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index baf0ada..e79bca3 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -29,6 +29,7 @@
#endif
#include <backtrace/Backtrace.h>
+#include <demangle.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
@@ -84,27 +85,20 @@
}
unwindstack::Elf* elf = map_info->GetElf(pid, true);
- uint64_t rel_pc = regs->pc();
- if (map_info != nullptr) {
- rel_pc = elf->GetRelPc(regs->pc(), map_info);
- }
+ uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
if (num_ignore_frames == 0 && !skip_frame) {
uint64_t adjusted_rel_pc = rel_pc;
- if (map_info != nullptr && adjust_rel_pc) {
+ if (adjust_rel_pc) {
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
}
frames->resize(num_frames + 1);
backtrace_frame_data_t* frame = &frames->at(num_frames);
frame->num = num_frames;
- if (map_info != nullptr) {
- // This will point to the adjusted absolute pc. regs->pc() is
- // unaltered.
- frame->pc = map_info->start + adjusted_rel_pc;
- } else {
- frame->pc = rel_pc;
- }
+ // This will point to the adjusted absolute pc. regs->pc() is
+ // unaltered.
+ frame->pc = map_info->start + adjusted_rel_pc;
frame->sp = regs->sp();
frame->rel_pc = adjusted_rel_pc;
frame->stack_size = 0;
@@ -117,7 +111,9 @@
frame->map.name = map_info->name;
uint64_t func_offset = 0;
- if (!elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+ if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
+ frame->func_name = demangle(frame->func_name.c_str());
+ } else {
frame->func_name = "";
}
frame->func_offset = func_offset;
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e80bf36..55ece54 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -121,6 +121,7 @@
#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
#define AID_AUTOMOTIVE_EVS 1062 /* Automotive rear and surround view system */
#define AID_LOWPAN 1063 /* LoWPAN subsystem */
+#define AID_HSM 1064 /* hardware security module subsystem */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp
index d0b35c3..db8f558 100644
--- a/libunwindstack/DwarfEhFrame.cpp
+++ b/libunwindstack/DwarfEhFrame.cpp
@@ -100,7 +100,7 @@
fde_info_.erase(index);
return nullptr;
}
- info->pc = value;
+ info->pc = value + 4;
return info;
}
@@ -175,7 +175,7 @@
last_error_ = DWARF_ERROR_MEMORY_INVALID;
return false;
}
- info->pc = value;
+ info->pc = value + 4;
if (pc < info->pc) {
if (prev_info == nullptr) {
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index b6e0412..901f492 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -235,7 +235,7 @@
return false;
}
- return AdjustEncodedValue(encoding & 0xf0, value);
+ return AdjustEncodedValue(encoding & 0x70, value);
}
// Instantiate all of the needed template functions.
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index a97ca2b..26485ae 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -106,7 +106,7 @@
DwarfMemory memory_;
DwarfError last_error_;
- uint64_t fde_count_;
+ uint64_t fde_count_ = 0;
std::unordered_map<uint64_t, DwarfFde> fde_entries_;
std::unordered_map<uint64_t, DwarfCie> cie_entries_;
std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index e9501e3..07159b0 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -124,7 +124,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x1380U, info->pc);
+ EXPECT_EQ(0x1384U, info->pc);
EXPECT_EQ(0x1540U, info->offset);
}
@@ -139,7 +139,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x3340U, info->pc);
+ EXPECT_EQ(0x3344U, info->pc);
EXPECT_EQ(0x3500U, info->offset);
}
@@ -153,7 +153,7 @@
auto info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x344U, info->pc);
EXPECT_EQ(0x500U, info->offset);
// Clear the memory so that this will fail if it doesn't read cached data.
@@ -161,7 +161,7 @@
info = this->eh_frame_->GetFdeInfoFromIndex(2);
ASSERT_TRUE(info != nullptr);
- EXPECT_EQ(0x340U, info->pc);
+ EXPECT_EQ(0x344U, info->pc);
EXPECT_EQ(0x500U, info->offset);
}
@@ -220,18 +220,18 @@
// Verify that if entries is zero, that it fails.
uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
this->eh_frame_->TestSetCurEntriesOffset(0x1040);
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
EXPECT_EQ(0x500U, fde_offset);
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
EXPECT_EQ(0x600U, fde_offset);
// Expect that the data is cached so no more memory reads will occur.
this->memory_.Clear();
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset));
+ ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
EXPECT_EQ(0x600U, fde_offset);
}
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 08fe7cf..f12d2fe 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -52,6 +52,8 @@
void ReadEncodedValue_non_zero_adjust();
template <typename AddressType>
void ReadEncodedValue_overflow();
+ template <typename AddressType>
+ void ReadEncodedValue_high_bit_set();
MemoryFake memory_;
std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -435,6 +437,26 @@
ReadEncodedValue_overflow<uint64_t>();
}
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_high_bit_set() {
+ uint64_t value;
+ memory_.SetData32(0, 0x15234);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xc3, &value));
+ ASSERT_EQ(0x75234U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint32_t) {
+ ReadEncodedValue_high_bit_set<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_high_bit_set_uint64_t) {
+ ReadEncodedValue_high_bit_set<uint64_t>();
+}
+
TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
uint64_t value = 0x1234;
ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
diff --git a/logcat/Android.bp b/logcat/Android.bp
new file mode 100644
index 0000000..729c8ff
--- /dev/null
+++ b/logcat/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2006-2017 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.
+//
+
+cc_defaults {
+ name: "logcat_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libpcrecpp",
+ ],
+ logtags: ["event.logtags"],
+}
+
+cc_library {
+ name: "liblogcat",
+
+ defaults: ["logcat_defaults"],
+ srcs: [
+ "logcat.cpp",
+ "getopt_long.cpp",
+ "logcat_system.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "logcat",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcat_main.cpp",
+ ],
+}
+
+cc_binary {
+ name: "logcatd",
+
+ defaults: ["logcat_defaults"],
+ shared_libs: ["liblogcat"],
+ srcs: [
+ "logcatd_main.cpp",
+ ],
+}
+
+cc_prebuilt_binary {
+ name: "logpersist.start",
+ srcs: ["logpersist"],
+ init_rc: ["logcatd.rc"],
+ symlinks: ["logpersist.stop", "logpersist.cat"],
+ strip: {
+ none: true,
+ }
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 4e11ca9..a716993 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -2,48 +2,4 @@
LOCAL_PATH := $(call my-dir)
-logcatLibs := liblog libbase libcutils libpcrecpp
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcat
-LOCAL_SRC_FILES := logcat_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logcatd
-LOCAL_MODULE_TAGS := debug
-LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
-LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
-LOCAL_SHARED_LIBRARIES := $(logcatLibs)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_INIT_RC := logcatd.rc
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index b31db58..3d56472 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1019,7 +1019,6 @@
break;
case 'm': {
- char* end = nullptr;
if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
"-%c \"%s\" isn't an "
@@ -1182,7 +1181,6 @@
std::unique_ptr<char, void (*)(void*)> formats(
strdup(optctx.optarg), free);
char* arg = formats.get();
- unsigned idMask = 0;
char* sv = nullptr; // protect against -ENOMEM above
while (!!(arg = strtok_r(arg, delimiters, &sv))) {
err = setLogFormat(context, arg);
@@ -1733,7 +1731,7 @@
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- int save_errno = errno;
+ save_errno = errno;
goto pthread_attr_exit;
}
@@ -1773,7 +1771,7 @@
context->retval = EXIT_SUCCESS;
if (pthread_create(&context->thr, &attr,
(void*(*)(void*))__logcat, context)) {
- int save_errno = errno;
+ save_errno = errno;
goto argv_exit;
}
pthread_attr_destroy(&attr);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 381c974..f20ac45 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -41,22 +41,20 @@
mTid(tid),
mRealTime(realtime),
mMsgLen(len),
- mLogId(log_id) {
+ mLogId(log_id),
+ mDropped(false) {
mMsg = new char[len];
memcpy(mMsg, msg, len);
- mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t)))
- ? le32toh(reinterpret_cast<android_event_header_t*>(mMsg)->tag)
- : 0;
}
LogBufferElement::LogBufferElement(const LogBufferElement& elem)
- : mTag(elem.mTag),
- mUid(elem.mUid),
+ : mUid(elem.mUid),
mPid(elem.mPid),
mTid(elem.mTid),
mRealTime(elem.mRealTime),
mMsgLen(elem.mMsgLen),
- mLogId(elem.mLogId) {
+ mLogId(elem.mLogId),
+ mDropped(elem.mDropped) {
mMsg = new char[mMsgLen];
memcpy(mMsg, elem.mMsg, mMsgLen);
}
@@ -65,6 +63,32 @@
delete[] mMsg;
}
+uint32_t LogBufferElement::getTag() const {
+ return (isBinary() &&
+ ((mDropped && mMsg != nullptr) ||
+ (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+ ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+ : 0;
+}
+
+unsigned short LogBufferElement::setDropped(unsigned short value) {
+ // The tag information is saved in mMsg data, if the tag is non-zero
+ // save only the information needed to get the tag.
+ if (getTag() != 0) {
+ if (mMsgLen > sizeof(android_event_header_t)) {
+ char* truncated_msg = new char[sizeof(android_event_header_t)];
+ memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+ delete[] mMsg;
+ mMsg = truncated_msg;
+ } // mMsgLen == sizeof(android_event_header_t), already at minimum.
+ } else {
+ delete[] mMsg;
+ mMsg = nullptr;
+ }
+ mDropped = true;
+ return mDroppedCount = value;
+}
+
// caller must own and free character string
char* android::tidToName(pid_t tid) {
char* retval = NULL;
@@ -164,8 +188,8 @@
// identical to below to calculate the buffer size required
const char* type = lastSame ? "identical" : "expire";
size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
size_t hdrLen;
if (isBinary()) {
@@ -196,8 +220,8 @@
}
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
- commName ? commName : "", type, mDropped,
- (mDropped > 1) ? "s" : "");
+ commName ? commName : "", type, getDropped(),
+ (getDropped() > 1) ? "s" : "");
free(const_cast<char*>(name));
free(const_cast<char*>(commName));
@@ -225,7 +249,7 @@
char* buffer = NULL;
- if (!mMsg) {
+ if (mDropped) {
entry.len = populateDroppedMessage(buffer, parent, lastSame);
if (!entry.len) return mRealTime;
iovec[1].iov_base = buffer;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 814ec87..b168645 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -32,25 +32,25 @@
// chatty for the temporal expire messages
#define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration
-class LogBufferElement {
+class __attribute__((packed)) LogBufferElement {
friend LogBuffer;
// sized to match reality of incoming log packets
- uint32_t mTag; // only valid for isBinary()
const uint32_t mUid;
const uint32_t mPid;
const uint32_t mTid;
log_time mRealTime;
char* mMsg;
union {
- const uint16_t mMsgLen; // mMSg != NULL
- uint16_t mDropped; // mMsg == NULL
+ const uint16_t mMsgLen; // mDropped == false
+ uint16_t mDroppedCount; // mDropped == true
};
const uint8_t mLogId;
+ bool mDropped;
static atomic_int_fast64_t sequence;
- // assumption: mMsg == NULL
+ // assumption: mDropped == true
size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
bool lastSame);
@@ -58,7 +58,7 @@
LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
pid_t tid, const char* msg, unsigned short len);
LogBufferElement(const LogBufferElement& elem);
- virtual ~LogBufferElement();
+ ~LogBufferElement();
bool isBinary(void) const {
return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
@@ -76,24 +76,16 @@
pid_t getTid(void) const {
return mTid;
}
- uint32_t getTag() const {
- return mTag;
- }
+ uint32_t getTag() const;
unsigned short getDropped(void) const {
- return mMsg ? 0 : mDropped;
+ return mDropped ? mDroppedCount : 0;
}
- unsigned short setDropped(unsigned short value) {
- if (mMsg) {
- delete[] mMsg;
- mMsg = NULL;
- }
- return mDropped = value;
- }
+ unsigned short setDropped(unsigned short value);
unsigned short getMsgLen() const {
- return mMsg ? mMsgLen : 0;
+ return mDropped ? 0 : mMsgLen;
}
const char* getMsg() const {
- return mMsg;
+ return mDropped ? nullptr : mMsg;
}
log_time getRealTime(void) const {
return mRealTime;
diff --git a/rootdir/asan.options b/rootdir/asan.options
index d728f12..a264d2d 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -5,3 +5,4 @@
detect_container_overflow=0
abort_on_error=1
include_if_exists=/system/asan.options.%b
+include_if_exists=/data/asan/system/asan.options.%b
diff --git a/trusty/keymaster/keymaster_ipc.h b/trusty/keymaster/keymaster_ipc.h
index b38eb05..d63757b 100644
--- a/trusty/keymaster/keymaster_ipc.h
+++ b/trusty/keymaster/keymaster_ipc.h
@@ -24,7 +24,8 @@
// Commands
enum keymaster_command : uint32_t {
KEYMASTER_RESP_BIT = 1,
- KEYMASTER_REQ_SHIFT = 1,
+ KEYMASTER_STOP_BIT = 2,
+ KEYMASTER_REQ_SHIFT = 2,
KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/trusty_keymaster_device.cpp b/trusty/keymaster/trusty_keymaster_device.cpp
index cfe94cc..55a03bd 100644
--- a/trusty/keymaster/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/trusty_keymaster_device.cpp
@@ -36,7 +36,8 @@
#include "trusty_keymaster_device.h"
#include "trusty_keymaster_ipc.h"
-const uint32_t RECV_BUF_SIZE = PAGE_SIZE;
+// Maximum size of message from Trusty is 8K (for RSA attestation key and chain)
+const uint32_t RECV_BUF_SIZE = 2*PAGE_SIZE;
const uint32_t SEND_BUF_SIZE = (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
const size_t kMaximumAttestationChallengeLength = 128;
@@ -176,14 +177,14 @@
}
AuthorizationSet params_copy(*params);
- ConfigureRequest request;
+ ConfigureRequest request(message_version_);
if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
!params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
ALOGD("Configuration parameters must contain OS version and patch level");
return KM_ERROR_INVALID_ARGUMENT;
}
- ConfigureResponse response;
+ ConfigureResponse response(message_version_);
keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -199,9 +200,9 @@
return error_;
}
- AddEntropyRequest request;
+ AddEntropyRequest request(message_version_);
request.random_data.Reinitialize(data, data_length);
- AddEntropyResponse response;
+ AddEntropyResponse response(message_version_);
return Send(KM_ADD_RNG_ENTROPY, request, &response);
}
@@ -260,11 +261,11 @@
return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- GetKeyCharacteristicsRequest request;
+ GetKeyCharacteristicsRequest request(message_version_);
request.SetKeyMaterial(*key_blob);
AddClientAndAppData(client_id, app_data, &request);
- GetKeyCharacteristicsResponse response;
+ GetKeyCharacteristicsResponse response(message_version_);
keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -378,7 +379,7 @@
cert_chain->entry_count = 0;
cert_chain->entries = nullptr;
- AttestKeyRequest request;
+ AttestKeyRequest request(message_version_);
request.SetKeyMaterial(*key_to_attest);
request.attest_params.Reinitialize(*attest_params);
@@ -390,7 +391,7 @@
return KM_ERROR_INVALID_INPUT_LENGTH;
}
- AttestKeyResponse response;
+ AttestKeyResponse response(message_version_);
keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -438,11 +439,11 @@
return KM_ERROR_OUTPUT_PARAMETER_NULL;
}
- UpgradeKeyRequest request;
+ UpgradeKeyRequest request(message_version_);
request.SetKeyMaterial(*key_to_upgrade);
request.upgrade_params.Reinitialize(*upgrade_params);
- UpgradeKeyResponse response;
+ UpgradeKeyResponse response(message_version_);
keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -479,12 +480,12 @@
*out_params = {};
}
- BeginOperationRequest request;
+ BeginOperationRequest request(message_version_);
request.purpose = purpose;
request.SetKeyMaterial(*key);
request.additional_params.Reinitialize(*in_params);
- BeginOperationResponse response;
+ BeginOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -527,7 +528,7 @@
*output = {};
}
- UpdateOperationRequest request;
+ UpdateOperationRequest request(message_version_);
request.op_handle = operation_handle;
if (in_params) {
request.additional_params.Reinitialize(*in_params);
@@ -537,7 +538,7 @@
request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
}
- UpdateOperationResponse response;
+ UpdateOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -588,7 +589,7 @@
*output = {};
}
- FinishOperationRequest request;
+ FinishOperationRequest request(message_version_);
request.op_handle = operation_handle;
if (signature && signature->data && signature->data_length > 0) {
request.signature.Reinitialize(signature->data, signature->data_length);
@@ -600,7 +601,7 @@
request.additional_params.Reinitialize(*in_params);
}
- FinishOperationResponse response;
+ FinishOperationResponse response(message_version_);
keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
if (err != KM_ERROR_OK) {
return err;
@@ -633,9 +634,9 @@
return error_;
}
- AbortOperationRequest request;
+ AbortOperationRequest request(message_version_);
request.op_handle = operation_handle;
- AbortOperationResponse response;
+ AbortOperationResponse response(message_version_);
return Send(KM_ABORT_OPERATION, request, &response);
}
@@ -770,6 +771,9 @@
ALOGV("Sending %d byte request\n", (int)req.SerializedSize());
int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size);
if (rc < 0) {
+ // Reset the connection on tipc error
+ trusty_keymaster_disconnect();
+ trusty_keymaster_connect();
ALOGE("tipc error: %d\n", rc);
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
@@ -777,8 +781,7 @@
ALOGV("Received %d byte response\n", rsp_size);
}
- const keymaster_message* msg = (keymaster_message*)recv_buf;
- const uint8_t* p = msg->payload;
+ const uint8_t* p = recv_buf;
if (!rsp->Deserialize(&p, p + rsp_size)) {
ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
return KM_ERROR_UNKNOWN_ERROR;
diff --git a/trusty/keymaster/trusty_keymaster_ipc.cpp b/trusty/keymaster/trusty_keymaster_ipc.cpp
index cdc2778..54b251e 100644
--- a/trusty/keymaster/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/trusty_keymaster_ipc.cpp
@@ -23,6 +23,8 @@
#include <string.h>
#include <unistd.h>
+#include <algorithm>
+
#include <log/log.h>
#include <trusty/tipc.h>
@@ -31,7 +33,7 @@
#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
-static int handle_ = 0;
+static int handle_ = -1;
int trusty_keymaster_connect() {
int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
@@ -45,7 +47,7 @@
int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
uint32_t* out_size) {
- if (handle_ == 0) {
+ if (handle_ < 0) {
ALOGE("not connected\n");
return -EINVAL;
}
@@ -62,32 +64,43 @@
ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno));
return -errno;
}
+ size_t out_max_size = *out_size;
+ *out_size = 0;
+ struct iovec iov[2];
+ struct keymaster_message header;
+ iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};
+ while (true) {
+ iov[1] = {
+ .iov_base = out + *out_size,
+ .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, out_max_size - *out_size)};
+ rc = readv(handle_, iov, 2);
+ if (rc < 0) {
+ ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
+ strerror(errno));
+ return -errno;
+ }
- rc = read(handle_, out, *out_size);
- if (rc < 0) {
- ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
- strerror(errno));
- return -errno;
+ if ((size_t)rc < sizeof(struct keymaster_message)) {
+ ALOGE("invalid response size (%d)\n", (int)rc);
+ return -EINVAL;
+ }
+
+ if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {
+ ALOGE("invalid command (%d)", header.cmd);
+ return -EINVAL;
+ }
+ *out_size += ((size_t)rc - sizeof(struct keymaster_message));
+ if (header.cmd & KEYMASTER_STOP_BIT) {
+ break;
+ }
}
- if ((size_t)rc < sizeof(struct keymaster_message)) {
- ALOGE("invalid response size (%d)\n", (int)rc);
- return -EINVAL;
- }
-
- msg = (struct keymaster_message*)out;
-
- if ((cmd | KEYMASTER_RESP_BIT) != msg->cmd) {
- ALOGE("invalid command (%d)", msg->cmd);
- return -EINVAL;
- }
-
- *out_size = ((size_t)rc) - sizeof(struct keymaster_message);
return rc;
}
void trusty_keymaster_disconnect() {
- if (handle_ != 0) {
+ if (handle_ >= 0) {
tipc_close(handle_);
}
+ handle_ = -1;
}