diff options
author | Tom Cherry <tomcherry@google.com> | 2017-11-17 17:14:05 -0800 |
---|---|---|
committer | Tom Cherry <tomcherry@google.com> | 2017-12-12 00:36:20 -0800 |
commit | 79b724ca5a33e5dff7c2530b2649648021c1258d (patch) | |
tree | 703e81d1d3c41fe093f4747d4d7d4324427dde2c /libc | |
parent | 78b40e8fe1f37de319344c92d8e6e1eb595c1067 (diff) |
Add support for serialized property contexts
This adds support for reading a serialized
/dev/__properties__/property_info file, which contains a
serialized trie that maps property names to the SELinux context to
which they belong.
Performance wise on walleye, this change reduces the start up cost in
libc from ~3000us to ~430us. On a benchmark that calls
__system_property_find() for each property set on the system, it
reduces the time per iteration from ~650us to ~292us.
Bug: 36001741
Test: Boot bullhead, walleye, run unit tests
Test: Benchmark initialization and lookup performance
Change-Id: I0887a3a7da88eb51b6d1bd494fa5bce593423599
Diffstat (limited to 'libc')
-rw-r--r-- | libc/Android.bp | 2 | ||||
-rw-r--r-- | libc/system_properties/context_node.h | 8 | ||||
-rw-r--r-- | libc/system_properties/contexts_serialized.cpp | 171 | ||||
-rw-r--r-- | libc/system_properties/contexts_serialized.h | 63 | ||||
-rw-r--r-- | libc/system_properties/system_properties.cpp | 24 |
5 files changed, 260 insertions, 8 deletions
diff --git a/libc/Android.bp b/libc/Android.bp index 3ec75ee0a..cba86dddc 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -1402,6 +1402,7 @@ cc_library_static { "bionic/wmempcpy.cpp", "system_properties/context_node.cpp", "system_properties/contexts_split.cpp", + "system_properties/contexts_serialized.cpp", "system_properties/prop_area.cpp", "system_properties/prop_info.cpp", "system_properties/system_properties.cpp", @@ -1423,6 +1424,7 @@ cc_library_static { cflags: ["-D__ANDROID_TREBLE__"], }, }, + whole_static_libs: ["libpropertyinfoparser"], cppflags: ["-Wold-style-cast"], local_include_dirs: ["stdio"], include_dirs: ["bionic/libstdc++/include"], diff --git a/libc/system_properties/context_node.h b/libc/system_properties/context_node.h index 1c6cbbb8e..769b85332 100644 --- a/libc/system_properties/context_node.h +++ b/libc/system_properties/context_node.h @@ -41,9 +41,16 @@ class ContextNode { ~ContextNode() { Unmap(); } + + ContextNode(const ContextNode&) = delete; + ContextNode(ContextNode&&) = delete; + void operator=(const ContextNode&) = delete; + void operator=(const ContextNode&&) = delete; + bool Open(bool access_rw, bool* fsetxattr_failed); bool CheckAccessAndOpen(); void ResetAccess(); + void Unmap(); const char* context() const { return context_; @@ -54,7 +61,6 @@ class ContextNode { private: bool CheckAccess(); - void Unmap(); Lock lock_; const char* context_; diff --git a/libc/system_properties/contexts_serialized.cpp b/libc/system_properties/contexts_serialized.cpp new file mode 100644 index 000000000..117b5cf64 --- /dev/null +++ b/libc/system_properties/contexts_serialized.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "contexts_serialized.h" + +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <new> + +#include <async_safe/log.h> + +#include "private/bionic_prctl.h" +#include "property_filename.h" + +bool ContextsSerialized::InitializeContextNodes() { + auto num_context_nodes = property_info_area_file_->num_contexts(); + auto context_nodes_mmap_size = sizeof(ContextNode) * num_context_nodes; + // We want to avoid malloc in system properties, so we take an anonymous map instead (b/31659220). + void* const map_result = mmap(nullptr, context_nodes_mmap_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map_result == MAP_FAILED) { + return false; + } + + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_result, context_nodes_mmap_size, + "System property context nodes"); + + context_nodes_ = reinterpret_cast<ContextNode*>(map_result); + num_context_nodes_ = num_context_nodes; + context_nodes_mmap_size_ = context_nodes_mmap_size; + + for (size_t i = 0; i < num_context_nodes; ++i) { + new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i)); + } + + return true; +} + +bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) { + char filename[PROP_FILENAME_MAX]; + int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", + property_filename); + if (len < 0 || len > PROP_FILENAME_MAX) { + serial_prop_area_ = nullptr; + return false; + } + + if (access_rw) { + serial_prop_area_ = + prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed); + } else { + serial_prop_area_ = prop_area::map_prop_area(filename); + } + return serial_prop_area_; +} + +bool ContextsSerialized::InitializeProperties() { + if (!property_info_area_file_.LoadDefaultPath()) { + return false; + } + + if (!InitializeContextNodes()) { + FreeAndUnmap(); + return false; + } + + return true; +} + +bool ContextsSerialized::Initialize(bool writable) { + if (!InitializeProperties()) { + return false; + } + + if (writable) { + mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH); + bool open_failed = false; + bool fsetxattr_failed = false; + + for (size_t i = 0; i < num_context_nodes_; ++i) { + if (!context_nodes_[i].Open(true, &fsetxattr_failed)) { + open_failed = true; + } + } + if (open_failed || !MapSerialPropertyArea(true, &fsetxattr_failed)) { + FreeAndUnmap(); + return false; + } + + return !fsetxattr_failed; + } else { + if (!MapSerialPropertyArea(false, nullptr)) { + FreeAndUnmap(); + return false; + } + } + return true; +} + +prop_area* ContextsSerialized::GetPropAreaForName(const char* name) { + uint32_t index; + property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr); + if (index == ~0u || index >= num_context_nodes_) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find context for property \"%s\"", + name); + return nullptr; + } + auto* context_node = &context_nodes_[index]; + if (!context_node->pa()) { + // We explicitly do not check no_access_ in this case because unlike the + // case of foreach(), we want to generate an selinux audit for each + // non-permitted property access in this function. + context_node->Open(false, nullptr); + } + return context_node->pa(); +} + +void ContextsSerialized::ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { + for (size_t i = 0; i < num_context_nodes_; ++i) { + if (context_nodes_[i].CheckAccessAndOpen()) { + context_nodes_[i].pa()->foreach (propfn, cookie); + } + } +} + +void ContextsSerialized::ResetAccess() { + for (size_t i = 0; i < num_context_nodes_; ++i) { + context_nodes_[i].ResetAccess(); + } +} + +void ContextsSerialized::FreeAndUnmap() { + property_info_area_file_.Reset(); + if (context_nodes_ != nullptr) { + for (size_t i = 0; i < num_context_nodes_; ++i) { + context_nodes_[i].Unmap(); + } + munmap(context_nodes_, context_nodes_mmap_size_); + context_nodes_ = nullptr; + } + prop_area::unmap_prop_area(&serial_prop_area_); + serial_prop_area_ = nullptr; +} diff --git a/libc/system_properties/contexts_serialized.h b/libc/system_properties/contexts_serialized.h new file mode 100644 index 000000000..52474c326 --- /dev/null +++ b/libc/system_properties/contexts_serialized.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SYSTEM_PROPERTIES_CONTEXTS_SERIALIZED_H +#define SYSTEM_PROPERTIES_CONTEXTS_SERIALIZED_H + +#include <property_info_parser/property_info_parser.h> + +#include "context_node.h" +#include "contexts.h" + +class ContextsSerialized : public Contexts { + public: + virtual ~ContextsSerialized() override { + } + + virtual bool Initialize(bool writable) override; + virtual prop_area* GetPropAreaForName(const char* name) override; + virtual prop_area* GetSerialPropArea() override { + return serial_prop_area_; + } + virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override; + virtual void ResetAccess() override; + virtual void FreeAndUnmap() override; + + private: + bool InitializeContextNodes(); + bool InitializeProperties(); + bool MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed); + + android::properties::PropertyInfoAreaFile property_info_area_file_; + ContextNode* context_nodes_ = nullptr; + size_t num_context_nodes_ = 0; + size_t context_nodes_mmap_size_ = 0; + prop_area* serial_prop_area_ = nullptr; +}; + +#endif diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp index 89a6fe74d..f67fc4d4a 100644 --- a/libc/system_properties/system_properties.cpp +++ b/libc/system_properties/system_properties.cpp @@ -59,6 +59,7 @@ #include "context_node.h" #include "contexts.h" #include "contexts_pre_split.h" +#include "contexts_serialized.h" #include "contexts_split.h" #include "prop_area.h" #include "prop_info.h" @@ -71,6 +72,7 @@ static union ContextsUnion { ContextsUnion() {} ~ContextsUnion() {} + ContextsSerialized contexts_serialized; ContextsSplit contexts_split; ContextsPreSplit contexts_pre_split; } contexts_union; @@ -283,11 +285,19 @@ int __system_properties_init() { } contexts = nullptr; if (is_dir(property_filename)) { - new (&contexts_union.contexts_split) ContextsSplit(); - if (!contexts_union.contexts_split.Initialize(false)) { - return -1; + if (access("/dev/__properties__/property_info", R_OK) == 0) { + new (&contexts_union.contexts_serialized) ContextsSerialized(); + if (!contexts_union.contexts_serialized.Initialize(false)) { + return -1; + } + contexts = &contexts_union.contexts_serialized; + } else { + new (&contexts_union.contexts_split) ContextsSplit(); + if (!contexts_union.contexts_split.Initialize(false)) { + return -1; + } + contexts = &contexts_union.contexts_split; } - contexts = &contexts_union.contexts_split; } else { new (&contexts_union.contexts_pre_split) ContextsPreSplit(); if (!contexts_union.contexts_pre_split.Initialize(false)) { @@ -314,9 +324,9 @@ int __system_property_area_init() { } // We set this unconditionally as we want tests to continue on regardless of if this failed // and property_service will abort on an error condition, so no harm done. - new (&contexts_union.contexts_split) ContextsSplit; - contexts = &contexts_union.contexts_split; - if (!contexts_union.contexts_split.Initialize(true)) { + new (&contexts_union.contexts_serialized) ContextsSerialized; + contexts = &contexts_union.contexts_serialized; + if (!contexts_union.contexts_serialized.Initialize(true)) { return -1; } return 0; |