Merge "Make per-application memcg hierarchy configurable via a property" into pi-dev
diff --git a/init/README.md b/init/README.md
index 5c2352b..9099427 100644
--- a/init/README.md
+++ b/init/README.md
@@ -187,6 +187,10 @@
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+ _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c3100a5..4172ba7 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -95,6 +95,11 @@
void CreateSerializedPropertyInfo();
+struct PropertyAuditData {
+ const ucred* cr;
+ const char* name;
+};
+
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@@ -111,7 +116,7 @@
return false;
}
- property_audit_data audit_data;
+ PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@@ -388,6 +393,35 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr) {
+ // We check the legacy method first but these properties are dontaudit, so we only log an audit
+ // if the newer method fails as well. We only do this with the legacy ctl. properties.
+ if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+ // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+ // their value is the name of the service to apply that action to. Permissions for these
+ // actions are based on the service, so we must create a fake name of ctl.<service> to
+ // check permissions.
+ auto control_string_legacy = "ctl." + value;
+ const char* target_context_legacy = nullptr;
+ const char* type_legacy = nullptr;
+ property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+ &type_legacy);
+
+ if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+ return true;
+ }
+ }
+
+ auto control_string_full = name + "$" + value;
+ const char* target_context_full = nullptr;
+ const char* type_full = nullptr;
+ property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+ &type_full);
+
+ return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
@@ -397,15 +431,9 @@
}
if (StartsWith(name, "ctl.")) {
- // ctl. properties have their name ctl.<action> and their value is the name of the service
- // to apply that action to. Permissions for these actions are based on the service, so we
- // must create a fake name of ctl.<service> to check permissions.
- auto control_string = "ctl." + value;
- const char* target_context = nullptr;
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
- if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+ if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+ *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+ value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -737,7 +765,7 @@
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
diff --git a/init/property_service.h b/init/property_service.h
index 29eaaa9..897ac15 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -24,11 +24,6 @@
namespace android {
namespace init {
-struct property_audit_data {
- const ucred* cr;
- const char* name;
-};
-
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
diff --git a/init/service.cpp b/init/service.cpp
index 09d8dae..37d3a88 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -59,13 +60,13 @@
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
namespace init {
-static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
@@ -101,36 +102,49 @@
return computed_context;
}
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // It's OK to LOG(FATAL) in this function since it's running in the first
- // child process.
-
// Recursively remount / as slave like zygote does so unmounting and mounting /proc
// doesn't interfere with the parent namespace's /proc mount. This will also
// prevent any other mounts/unmounts initiated by the service from interfering
// with the parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
- }
- // umount() then mount() /proc.
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (umount("/proc") == -1) {
- PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
+ return ErrnoError() << "Could not remount(/) recursively as slave";
}
- if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
- PLOG(FATAL) << "couldn't set name for " << service_name;
+ // umount() then mount() /proc and/or /sys
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (namespace_flags_ & CLONE_NEWPID) {
+ if (umount("/proc") == -1) {
+ return ErrnoError() << "Could not umount(/proc)";
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/proc)";
+ }
+ }
+ bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
+ [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+ if (remount_sys) {
+ if (umount2("/sys", MNT_DETACH) == -1) {
+ return ErrnoError() << "Could not umount(/sys)";
+ }
+ if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/sys)";
+ }
+ }
+ return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+ if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+ return ErrnoError() << "Could not set name";
}
pid_t child_pid = fork();
if (child_pid == -1) {
- PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+ return ErrnoError() << "Could not fork init inside the PID namespace";
}
if (child_pid > 0) {
@@ -153,6 +167,20 @@
}
_exit(WEXITSTATUS(init_exitstatus));
}
+ return Success();
+}
+
+Result<Success> Service::EnterNamespaces() const {
+ for (const auto& [nstype, path] : namespaces_to_enter_) {
+ auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
+ if (!fd) {
+ return ErrnoError() << "Could not open namespace at " << path;
+ }
+ if (setns(fd, nstype) == -1) {
+ return ErrnoError() << "Could not setns() namespace at " << path;
+ }
+ }
+ return Success();
}
static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
@@ -418,6 +446,20 @@
return Success();
}
+Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+ if (args[1] != "net") {
+ return Error() << "Init only supports entering network namespaces";
+ }
+ if (!namespaces_to_enter_.empty()) {
+ return Error() << "Only one network namespace may be entered";
+ }
+ // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+ // present. Therefore, they also require mount namespaces.
+ namespace_flags_ |= CLONE_NEWNS;
+ namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+ return Success();
+}
+
Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
@@ -682,6 +724,8 @@
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
+ {"enter_namespace",
+ {2, 2, &Service::ParseEnterNamespace}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
{"ioprio", {2, 2, &Service::ParseIoprio}},
@@ -783,7 +827,7 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- auto result = ComputeContextFromExecutable(name_, args_[0]);
+ auto result = ComputeContextFromExecutable(args_[0]);
if (!result) {
return result.error();
}
@@ -802,10 +846,24 @@
if (pid == 0) {
umask(077);
+ if (auto result = EnterNamespaces(); !result) {
+ LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+ }
+
+ if (namespace_flags_ & CLONE_NEWNS) {
+ if (auto result = SetUpMountNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up mount namespace: " << result.error();
+ }
+ }
+
if (namespace_flags_ & CLONE_NEWPID) {
// This will fork again to run an init process inside the PID
// namespace.
- SetUpPidNamespace(name_);
+ if (auto result = SetUpPidNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up PID namespace: " << result.error();
+ }
}
for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index bcf1943..87c9ac8 100644
--- a/init/service.h
+++ b/init/service.h
@@ -124,6 +124,9 @@
using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
class OptionParserMap;
+ Result<Success> SetUpMountNamespace() const;
+ Result<Success> SetUpPidNamespace() const;
+ Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
void ZapStdio() const;
@@ -136,6 +139,7 @@
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> ParseEnterNamespace(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> ParseInterface(const std::vector<std::string>& args);
@@ -179,6 +183,8 @@
std::vector<gid_t> supp_gids_;
CapSet capabilities_;
unsigned namespace_flags_;
+ // Pair of namespace type, path to namespace.
+ std::vector<std::pair<int, std::string>> namespaces_to_enter_;
std::string seclabel_;
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 860d71d..c305db2 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -77,10 +77,44 @@
ACTION_BOOT = 1098,
FIELD_PLATFORM_REASON = 1099,
+ FIELD_DURATION_MILLIS = 1304,
+
+ FIELD_END_BATTERY_PERCENT = 1308,
+
ACTION_HIDDEN_API_ACCESSED = 1391,
FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
FIELD_HIDDEN_API_SIGNATURE = 1394,
+
+ ACTION_USB_CONNECTOR_CONNECTED = 1422,
+ ACTION_USB_CONNECTOR_DISCONNECTED = 1423,
+ ACTION_USB_AUDIO_CONNECTED = 1424,
+ FIELD_USB_AUDIO_VIDPID = 1425,
+ ACTION_USB_AUDIO_DISCONNECTED = 1426,
+ ACTION_HARDWARE_FAILED = 1427,
+ FIELD_HARDWARE_TYPE = 1428,
+ FIELD_HARDWARE_FAILURE_CODE = 1429,
+ ACTION_PHYSICAL_DROP = 1430,
+ FIELD_CONFIDENCE_PERCENT = 1431,
+ FIELD_ACCEL_MILLI_G = 1432,
+ ACTION_BATTERY_HEALTH = 1433,
+ FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE = 1434,
+ FIELD_BATTERY_TEMPERATURE_DECI_C = 1435,
+ FIELD_BATTERY_VOLTAGE_UV = 1436,
+ FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE_UV = 1437,
+ ACTION_BATTERY_CHARGE_CYCLES = 1438,
+ FIELD_BATTERY_CHARGE_CYCLES = 1439,
+
+ ACTION_SLOW_IO = 1442,
+ FIELD_IO_OPERATION_TYPE = 1443,
+ FIELD_IO_OPERATION_COUNT = 1444,
+ ACTION_SPEAKER_IMPEDANCE = 1445,
+ FIELD_SPEAKER_IMPEDANCE_MILLIOHMS = 1446,
+ FIELD_SPEAKER_LOCATION = 1447,
+ FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
+ FIELD_BATTERY_CURRENT_UA = 1449,
+ FIELD_HARDWARE_LOCATION = 1450,
+ ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
};
enum {
@@ -94,5 +128,30 @@
ACCESS_METHOD_LINKING = 3,
};
+enum HardwareType {
+ HARDWARE_UNKNOWN = 0,
+ HARDWARE_MICROPHONE = 1,
+ HARDWARE_CODEC = 2,
+ HARDWARE_SPEAKER = 3,
+ HARDWARE_FINGERPRINT = 4,
+};
+
+enum HardwareFailureCode {
+ HARDWARE_FAILURE_UNKNOWN = 0,
+ HARDWARE_FAILURE_COMPLETE = 1,
+ HARDWARE_FAILURE_SPEAKER_HIGH_Z = 2,
+ HARDWARE_FAILURE_SPEAKER_SHORT = 3,
+ HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN = 4,
+ HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5,
+};
+
+enum IoOperation {
+ IOOP_UNKNOWN = 0,
+ IOOP_READ = 1,
+ IOOP_WRITE = 2,
+ IOOP_UNMAP = 3,
+ IOOP_SYNC = 4,
+};
+
} // namespace metricslogger
} // namespace android
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4cc6693..b9464e7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -429,6 +429,7 @@
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
+ mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system