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/system_properties/contexts_serialized.cpp | |
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/system_properties/contexts_serialized.cpp')
-rw-r--r-- | libc/system_properties/contexts_serialized.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
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; +} |