From 7078870da1b9e25a84511f9c5b74594de333c370 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 5 Jun 2024 16:10:54 -0700 Subject: [PATCH 01/10] Make the library buildable locally --- Android.mk | 20 ++++++++++++ context_node.cpp | 2 -- contexts_serialized.cpp | 3 -- contexts_split.cpp | 2 -- include/api/hacks.h | 32 +++++++++++++++++++ include/api/system_properties.h | 9 ++---- include/system_properties/prop_info.h | 2 +- include/system_properties/system_properties.h | 2 +- prop_area.cpp | 2 -- system_properties.cpp | 4 --- system_property_api.cpp | 8 +---- system_property_set.cpp | 5 +-- 12 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 Android.mk create mode 100644 include/api/hacks.h diff --git a/Android.mk b/Android.mk new file mode 100644 index 000000000..5e989df73 --- /dev/null +++ b/Android.mk @@ -0,0 +1,20 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE:= libsystemproperties +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) +LOCAL_STATIC_LIBRARIES := libcxx +LOCAL_CFLAGS := -std=c++17 -Wno-unused-function +LOCAL_SRC_FILES := \ + context_node.cpp \ + contexts_serialized.cpp \ + contexts_split.cpp \ + prop_area.cpp \ + prop_info.cpp \ + system_properties.cpp \ + system_property_api.cpp \ + system_property_set.cpp \ + property_info_parser.cpp + +include $(BUILD_STATIC_LIBRARY) diff --git a/context_node.cpp b/context_node.cpp index 572bf97ce..9a48e8183 100644 --- a/context_node.cpp +++ b/context_node.cpp @@ -31,8 +31,6 @@ #include #include -#include - #include "system_properties/system_properties.h" // pthread_mutex_lock() calls into system_properties in the case of contention. diff --git a/contexts_serialized.cpp b/contexts_serialized.cpp index 73c913640..bfaf47a02 100644 --- a/contexts_serialized.cpp +++ b/contexts_serialized.cpp @@ -37,9 +37,6 @@ #include -#include -#include - #include "system_properties/system_properties.h" bool ContextsSerialized::InitializeContextNodes() { diff --git a/contexts_split.cpp b/contexts_split.cpp index 78bdc6436..a0ba21924 100644 --- a/contexts_split.cpp +++ b/contexts_split.cpp @@ -34,8 +34,6 @@ #include #include -#include - #include "system_properties/context_node.h" #include "system_properties/system_properties.h" diff --git a/include/api/hacks.h b/include/api/hacks.h new file mode 100644 index 000000000..8638d1c1a --- /dev/null +++ b/include/api/hacks.h @@ -0,0 +1,32 @@ +#pragma once + +#undef __INTRODUCED_IN +#define __INTRODUCED_IN(...) + +#undef __BIONIC_AVAILABILITY_GUARD +#define __BIONIC_AVAILABILITY_GUARD(...) 1 + +// +#define CHECK(x) /* NOP */ + +// +#define async_safe_format_buffer snprintf +#define async_safe_format_log(...) /* NOP */ + +// Rename symbols +#pragma redefine_extname __system_property_set __system_property_set2 +#pragma redefine_extname __system_property_find __system_property_find2 +#pragma redefine_extname __system_property_read_callback __system_property_read_callback2 +#pragma redefine_extname __system_property_foreach __system_property_foreach2 +#pragma redefine_extname __system_property_wait __system_property_wait2 +#pragma redefine_extname __system_property_read __system_property_read2 +#pragma redefine_extname __system_property_get __system_property_get2 +#pragma redefine_extname __system_property_find_nth __system_property_find_nth2 +#pragma redefine_extname __system_property_set_filename __system_property_set_filename2 +#pragma redefine_extname __system_property_area_init __system_property_area_init2 +#pragma redefine_extname __system_property_area_serial __system_property_area_serial2 +#pragma redefine_extname __system_property_add __system_property_add2 +#pragma redefine_extname __system_property_update __system_property_update2 +#pragma redefine_extname __system_property_serial __system_property_serial2 +#pragma redefine_extname __system_properties_init __system_properties_init2 +#pragma redefine_extname __system_property_wait_any __system_property_wait_any2 diff --git a/include/api/system_properties.h b/include/api/system_properties.h index 1303079f0..44496073a 100644 --- a/include/api/system_properties.h +++ b/include/api/system_properties.h @@ -38,6 +38,8 @@ #include #include +#include "hacks.h" + __BEGIN_DECLS /** An opaque structure representing a system property. */ @@ -247,11 +249,4 @@ int __system_properties_zygote_reload(void) __INTRODUCED_IN(35); #endif /* __BIONIC_AVAILABILITY_GUARD(35) */ -/** - * Deprecated: previously for testing, but now that SystemProperties is its own - * testable class, there is never a reason to call this function and its - * implementation simply returns -1. - */ -int __system_property_set_filename(const char* _Nullable __unused __filename); - __END_DECLS diff --git a/include/system_properties/prop_info.h b/include/system_properties/prop_info.h index e2236b0cc..efa5c8d54 100644 --- a/include/system_properties/prop_info.h +++ b/include/system_properties/prop_info.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include "platform/bionic/macros.h" diff --git a/include/system_properties/system_properties.h b/include/system_properties/system_properties.h index ea4f3392a..1f9d2c60b 100644 --- a/include/system_properties/system_properties.h +++ b/include/system_properties/system_properties.h @@ -29,7 +29,7 @@ #pragma once #include -#include +#include #include "contexts.h" #include "contexts_pre_split.h" diff --git a/prop_area.cpp b/prop_area.cpp index faa3edf00..970a55207 100644 --- a/prop_area.cpp +++ b/prop_area.cpp @@ -39,8 +39,6 @@ #include -#include - #ifdef LARGE_SYSTEM_PROPERTY_NODE constexpr size_t PA_SIZE = 1024 * 1024; #else diff --git a/system_properties.cpp b/system_properties.cpp index e0d38a822..26cb7ce97 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -29,7 +29,6 @@ #include "system_properties/system_properties.h" #include -#include #include #include #include @@ -39,9 +38,6 @@ #include -#include -#include - #include "private/ErrnoRestorer.h" #include "private/bionic_futex.h" diff --git a/system_property_api.cpp b/system_property_api.cpp index ed30fc2a6..0844905b9 100644 --- a/system_property_api.cpp +++ b/system_property_api.cpp @@ -26,9 +26,8 @@ * SUCH DAMAGE. */ -#include +#include -#include #include #include @@ -48,11 +47,6 @@ int __system_properties_init() { return system_properties.Init(PROP_DIRNAME) ? 0 : -1; } -__BIONIC_WEAK_FOR_NATIVE_BRIDGE -int __system_property_set_filename(const char*) { - return -1; -} - __BIONIC_WEAK_FOR_NATIVE_BRIDGE int __system_property_area_init() { bool fsetxattr_fail = false; diff --git a/system_property_set.cpp b/system_property_set.cpp index 9d734456d..43dd4012a 100644 --- a/system_property_set.cpp +++ b/system_property_set.cpp @@ -34,15 +34,12 @@ #include #include #include -#include +#include #include #include #include #include -#include -#include - #include "private/bionic_defs.h" #include "platform/bionic/macros.h" #include "private/ScopedFd.h" From 8dd521676131fb052cf0b518e075c549b6b695e2 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 19 May 2023 02:02:26 -0700 Subject: [PATCH 02/10] Support pre Android 9.0 property context This reverts Change-Id: I663a524670120ee19dfe785aa5f89b3981bdd378 --- contexts_split.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contexts_split.cpp b/contexts_split.cpp index a0ba21924..b35279243 100644 --- a/contexts_split.cpp +++ b/contexts_split.cpp @@ -266,6 +266,9 @@ bool ContextsSplit::InitializeProperties() { // still need the system / platform properties to function. if (access("/vendor/etc/selinux/vendor_property_contexts", R_OK) != -1) { InitializePropertiesFromFile("/vendor/etc/selinux/vendor_property_contexts"); + } else { + // Fallback to nonplat_* if vendor_* doesn't exist. + InitializePropertiesFromFile("/vendor/etc/selinux/nonplat_property_contexts"); } } else { if (!InitializePropertiesFromFile("/plat_property_contexts")) { @@ -273,6 +276,9 @@ bool ContextsSplit::InitializeProperties() { } if (access("/vendor_property_contexts", R_OK) != -1) { InitializePropertiesFromFile("/vendor_property_contexts"); + } else { + // Fallback to nonplat_* if vendor_* doesn't exist. + InitializePropertiesFromFile("/nonplat_property_contexts"); } } From fd437e2ef1673453732e28dac3a6a1365c631009 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 21 Jul 2025 00:20:20 -0700 Subject: [PATCH 03/10] Remove logic related to race condition prevention resetprop already fundamentally breaks the existing system property contract, which is "single writer, multiple reader", so it doesn't make sense to spend any effort trying to workaround race conditions. First of all, since we need to use the same code to handle pre API 30 devices, we cannot use the dirty backup area mechanism. After removing that, we cannot rely on the dirty bit of the serial to function correctly, so we skip that too. Finally, we revert property read back to the old wait implementation so that it works on all API levels. --- system_properties.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/system_properties.cpp b/system_properties.cpp index 26cb7ce97..e0a1ad184 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -186,9 +186,9 @@ uint32_t SystemProperties::ReadMutablePropertyValue(const prop_info* pi, char* v serial = new_serial; len = SERIAL_VALUE_LEN(serial); if (__predict_false(SERIAL_DIRTY(serial))) { - // See the comment in the prop_area constructor. - prop_area* pa = contexts_->GetPropAreaForName(pi->name); - memcpy(value, pa->dirty_backup_area(), len + 1); + __futex_wait(const_cast(&pi->serial), serial, nullptr); + new_serial = load_const_atomic(&pi->serial, memory_order_relaxed); + continue; } else { memcpy(value, pi->value, len + 1); } @@ -291,22 +291,9 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) auto* override_pi = const_cast(have_override ? override_pa->find(pi->name) : nullptr); uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed); - unsigned int old_len = SERIAL_VALUE_LEN(serial); - - // The contract with readers is that whenever the dirty bit is set, an undamaged copy - // of the pre-dirty value is available in the dirty backup area. The fence ensures - // that we publish our dirty area update before allowing readers to see a - // dirty serial. - memcpy(pa->dirty_backup_area(), pi->value, old_len + 1); - if (have_override) { - memcpy(override_pa->dirty_backup_area(), override_pi->value, old_len + 1); - } - atomic_thread_fence(memory_order_release); serial |= 1; - atomic_store_explicit(&pi->serial, serial, memory_order_relaxed); strlcpy(pi->value, value, len + 1); if (have_override) { - atomic_store_explicit(&override_pi->serial, serial, memory_order_relaxed); strlcpy(override_pi->value, value, len + 1); } // Now the primary value property area is up-to-date. Let readers know that they should From 84188da96ebd4a5e0b27e1f8cd6339fdbbb14bc3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 17 May 2023 00:44:08 -0700 Subject: [PATCH 04/10] Support mapping prop_area as rw --- context_node.cpp | 2 +- contexts_serialized.cpp | 2 +- contexts_split.cpp | 2 +- include/system_properties/contexts.h | 1 + .../system_properties/contexts_pre_split.h | 2 +- include/system_properties/prop_area.h | 4 ++-- prop_area.cpp | 22 ++++++++++++++----- system_properties.cpp | 8 +++++++ 8 files changed, 31 insertions(+), 12 deletions(-) diff --git a/context_node.cpp b/context_node.cpp index 9a48e8183..c5b67d065 100644 --- a/context_node.cpp +++ b/context_node.cpp @@ -51,7 +51,7 @@ bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) { if (access_rw) { pa_ = prop_area::map_prop_area_rw(filename.c_str(), context_, fsetxattr_failed); } else { - pa_ = prop_area::map_prop_area(filename.c_str()); + pa_ = prop_area::map_prop_area(filename.c_str(), nullptr); } lock_.unlock(); return pa_; diff --git a/contexts_serialized.cpp b/contexts_serialized.cpp index bfaf47a02..f810913a4 100644 --- a/contexts_serialized.cpp +++ b/contexts_serialized.cpp @@ -68,7 +68,7 @@ bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_f serial_prop_area_ = prop_area::map_prop_area_rw( serial_filename_.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed); } else { - serial_prop_area_ = prop_area::map_prop_area(serial_filename_.c_str()); + serial_prop_area_ = prop_area::map_prop_area(serial_filename_.c_str(), &rw_); } return serial_prop_area_; } diff --git a/contexts_split.cpp b/contexts_split.cpp index b35279243..064292e6b 100644 --- a/contexts_split.cpp +++ b/contexts_split.cpp @@ -195,7 +195,7 @@ bool ContextsSplit::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed serial_prop_area_ = prop_area::map_prop_area_rw( filename.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed); } else { - serial_prop_area_ = prop_area::map_prop_area(filename.c_str()); + serial_prop_area_ = prop_area::map_prop_area(filename.c_str(), &rw_); } return serial_prop_area_; } diff --git a/include/system_properties/contexts.h b/include/system_properties/contexts.h index df8c5a2cd..a59c06373 100644 --- a/include/system_properties/contexts.h +++ b/include/system_properties/contexts.h @@ -43,4 +43,5 @@ class Contexts { virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) = 0; virtual void ResetAccess() = 0; virtual void FreeAndUnmap() = 0; + bool rw_ = false; }; diff --git a/include/system_properties/contexts_pre_split.h b/include/system_properties/contexts_pre_split.h index a6cd039b0..52f08f906 100644 --- a/include/system_properties/contexts_pre_split.h +++ b/include/system_properties/contexts_pre_split.h @@ -39,7 +39,7 @@ class ContextsPreSplit : public Contexts { // We'll never initialize this legacy option as writable, so don't even check the arg. virtual bool Initialize(bool, const char* filename, bool*, bool) override { - pre_split_prop_area_ = prop_area::map_prop_area(filename); + pre_split_prop_area_ = prop_area::map_prop_area(filename, &rw_); return pre_split_prop_area_ != nullptr; } diff --git a/include/system_properties/prop_area.h b/include/system_properties/prop_area.h index 089cf5274..45c967990 100644 --- a/include/system_properties/prop_area.h +++ b/include/system_properties/prop_area.h @@ -93,7 +93,7 @@ class prop_area { public: static prop_area* map_prop_area_rw(const char* filename, const char* context, bool* fsetxattr_failed); - static prop_area* map_prop_area(const char* filename); + static prop_area* map_prop_area(const char* filename, bool *is_rw); static void unmap_prop_area(prop_area** pa) { if (*pa) { munmap(*pa, pa_size_); @@ -139,7 +139,7 @@ class prop_area { char* dirty_backup_area() { return data_ + sizeof(prop_trie_node); } private: - static prop_area* map_fd_ro(const int fd); + static prop_area* map_fd_ro(const int fd, bool rw); void* allocate_obj(const size_t size, uint_least32_t* const off); prop_trie_node* new_prop_trie_node(const char* name, uint32_t namelen, uint_least32_t* const off); diff --git a/prop_area.cpp b/prop_area.cpp index 970a55207..3b6b78cbe 100644 --- a/prop_area.cpp +++ b/prop_area.cpp @@ -106,7 +106,7 @@ prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context return pa; } -prop_area* prop_area::map_fd_ro(const int fd) { +prop_area* prop_area::map_fd_ro(const int fd, bool rw) { struct stat fd_stat; if (fstat(fd, &fd_stat) < 0) { return nullptr; @@ -121,7 +121,8 @@ prop_area* prop_area::map_fd_ro(const int fd) { pa_size_ = fd_stat.st_size; pa_data_size_ = pa_size_ - sizeof(prop_area); - void* const map_result = mmap(nullptr, pa_size_, PROT_READ, MAP_SHARED, fd, 0); + int prot = rw ? PROT_READ | PROT_WRITE : PROT_READ; + void* const map_result = mmap(nullptr, pa_size_, prot, MAP_SHARED, fd, 0); if (map_result == MAP_FAILED) { return nullptr; } @@ -135,13 +136,22 @@ prop_area* prop_area::map_fd_ro(const int fd) { return pa; } -prop_area* prop_area::map_prop_area(const char* filename) { - int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); - if (fd == -1) return nullptr; +prop_area* prop_area::map_prop_area(const char* filename, bool *is_rw) { + bool rw = false; + int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); + if (fd == -1) { + fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); + if (fd == -1) { + return nullptr; + } + } else { + rw = true; + } - prop_area* map_result = map_fd_ro(fd); + prop_area* map_result = map_fd_ro(fd, rw); close(fd); + if (is_rw) *is_rw = rw; return map_result; } diff --git a/system_properties.cpp b/system_properties.cpp index e0a1ad184..9227d4298 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -273,6 +273,10 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) } bool have_override = appcompat_override_contexts_ != nullptr; + if (!contexts_->rw_) { + return -1; + } + prop_area* serial_pa = contexts_->GetSerialPropArea(); prop_area* override_serial_pa = have_override ? appcompat_override_contexts_->GetSerialPropArea() : nullptr; @@ -339,6 +343,10 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va return -1; } + if (!contexts_->rw_) { + return -1; + } + prop_area* serial_pa = contexts_->GetSerialPropArea(); if (serial_pa == nullptr) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", From 23c79ed6f609b0c4fbdf969423df1b00a6b2e467 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 30 Nov 2025 23:40:47 -0800 Subject: [PATCH 05/10] Also setup override contexts in InitContexts Or else update won't work properly --- system_properties.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/system_properties.cpp b/system_properties.cpp index 9227d4298..2dd5b0af6 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -97,6 +97,19 @@ bool SystemProperties::InitContexts(bool load_default_path) { return false; } } + + appcompat_filename_ = PropertiesFilename(properties_filename_.c_str(), "appcompat_override"); + appcompat_override_contexts_ = nullptr; + if (access(appcompat_filename_.c_str(), F_OK) != -1) { + auto* appcompat_contexts = new (appcompat_override_contexts_data_) ContextsSerialized(); + if (!appcompat_contexts->Initialize(false, appcompat_filename_.c_str(), nullptr, load_default_path)) { + // The appcompat folder exists, but initializing it failed + return false; + } else { + appcompat_override_contexts_ = appcompat_contexts; + } + } + return true; } From e048ef0a9eadce576d07bbb9b17ed9f42b7bc918 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 5 Jun 2024 18:24:19 -0700 Subject: [PATCH 06/10] Support deleting properties --- include/api/system_properties.h | 7 ++ include/system_properties/prop_area.h | 4 + include/system_properties/system_properties.h | 1 + prop_area.cpp | 82 ++++++++++++++++++- system_properties.cpp | 47 +++++++++++ system_property_api.cpp | 5 ++ 6 files changed, 143 insertions(+), 3 deletions(-) diff --git a/include/api/system_properties.h b/include/api/system_properties.h index 44496073a..81e57ae0a 100644 --- a/include/api/system_properties.h +++ b/include/api/system_properties.h @@ -231,6 +231,13 @@ int __system_property_add(const char* _Nonnull __name, unsigned int __name_lengt */ int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __value, unsigned int __value_length); +/** + * Delete a system property. + * + * Returns 0 on success, -1 if the property area is full. + */ +int __system_property_delete(const char* _Nonnull __name, bool __prune); + /** * Reloads the system properties from disk. * Not intended for use by any apps except the Zygote. diff --git a/include/system_properties/prop_area.h b/include/system_properties/prop_area.h index 45c967990..119843d6d 100644 --- a/include/system_properties/prop_area.h +++ b/include/system_properties/prop_area.h @@ -124,6 +124,7 @@ class prop_area { const prop_info* find(const char* name); bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); + bool remove(const char* name, bool prune); bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie); @@ -153,6 +154,7 @@ class prop_area { prop_trie_node* find_prop_trie_node(prop_trie_node* const trie, const char* name, uint32_t namelen, bool alloc_if_needed); + prop_trie_node* traverse_trie(prop_trie_node* const trie, const char* name, bool alloc_if_needed); const prop_info* find_property(prop_trie_node* const trie, const char* name, uint32_t namelen, const char* value, uint32_t valuelen, bool alloc_if_needed); @@ -160,6 +162,8 @@ class prop_area { bool foreach_property(prop_trie_node* const trie, void (*propfn)(const prop_info* pi, void* cookie), void* cookie); + bool prune_trie(prop_trie_node* const node); + // The original design doesn't include pa_size or pa_data_size in the prop_area struct itself. // Since we'll need to be backwards compatible with that design, we don't gain much by adding it // now, especially since we don't have any plans to make different property areas different sizes, diff --git a/include/system_properties/system_properties.h b/include/system_properties/system_properties.h index 1f9d2c60b..5a2f27d11 100644 --- a/include/system_properties/system_properties.h +++ b/include/system_properties/system_properties.h @@ -65,6 +65,7 @@ class SystemProperties { int Get(const char* name, char* value); int Update(prop_info* pi, const char* value, unsigned int len); int Add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); + int Delete(const char* name, bool prune); uint32_t WaitAny(uint32_t old_serial); bool Wait(const prop_info* pi, uint32_t old_serial, uint32_t* new_serial_ptr, const timespec* relative_timeout); diff --git a/prop_area.cpp b/prop_area.cpp index 3b6b78cbe..3ccd46d76 100644 --- a/prop_area.cpp +++ b/prop_area.cpp @@ -283,9 +283,8 @@ prop_trie_node* prop_area::find_prop_trie_node(prop_trie_node* const trie, const } } -const prop_info* prop_area::find_property(prop_trie_node* const trie, const char* name, - uint32_t namelen, const char* value, uint32_t valuelen, - bool alloc_if_needed) { +prop_trie_node* prop_area::traverse_trie(prop_trie_node *const trie, const char *name, + bool alloc_if_needed) { if (!trie) return nullptr; const char* remaining_name = name; @@ -325,6 +324,15 @@ const prop_info* prop_area::find_property(prop_trie_node* const trie, const char remaining_name = sep + 1; } + return current; +} + +const prop_info* prop_area::find_property(prop_trie_node* const trie, const char* name, + uint32_t namelen, const char* value, uint32_t valuelen, + bool alloc_if_needed) { + prop_trie_node* current = traverse_trie(trie, name, alloc_if_needed); + if (!current) return nullptr; + uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); if (prop_offset != 0) { return to_prop_info(¤t->prop); @@ -379,3 +387,71 @@ bool prop_area::add(const char* name, unsigned int namelen, const char* value, bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { return foreach_property(root_node(), propfn, cookie); } + +#define get_offset(ptr) atomic_load_explicit(ptr, memory_order_relaxed) +#define set_offset(ptr, val) atomic_store_explicit(ptr, val, memory_order_release) + +// After removing a property, it is possible that redundant nodes will appear in the trie. +// DFS through the data structure, remove leaf nodes that do not hold properties, remove +// them from the trie, then backtrack recursively and remove redundant parent nodes. +// When this method returns true, detach the node from the parent. +bool prop_area::prune_trie(prop_trie_node *const node) { + bool is_leaf = true; + if (get_offset(&node->children) != 0) { + if (prune_trie(to_prop_trie_node(&node->children))) { + set_offset(&node->children, 0u); + } else { + is_leaf = false; + } + } + if (get_offset(&node->left) != 0) { + if (prune_trie(to_prop_trie_node(&node->left))) { + set_offset(&node->left, 0u); + } else { + is_leaf = false; + } + } + if (get_offset(&node->right) != 0) { + if (prune_trie(to_prop_trie_node(&node->right))) { + set_offset(&node->right, 0u); + } else { + is_leaf = false; + } + } + + if (is_leaf && get_offset(&node->prop) == 0) { + // Wipe the node + memset(node->name, 0, node->namelen); + memset(node, 0, sizeof(*node)); + // Then return true to detach the node from parent + return true; + } + return false; +} + +bool prop_area::remove(const char *name, bool prune) { + prop_trie_node *node = traverse_trie(root_node(), name, false); + if (!node) return false; + + uint_least32_t prop_offset = get_offset(&node->prop); + if (prop_offset == 0) return false; + + prop_info *prop = to_prop_info(&node->prop); + + // Detach the property from trie ASAP + set_offset(&node->prop, 0u); + + // Then wipe out the property from memory + if (prop->is_long()) { + char *value = const_cast(prop->long_value()); + memset(value, 0, strlen(value)); + } + memset(prop->name, 0, strlen(prop->name)); + memset(prop, 0, sizeof(*prop)); + + if (prune) { + prune_trie(root_node()); + } + + return true; +} diff --git a/system_properties.cpp b/system_properties.cpp index 2dd5b0af6..8535ed94a 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -417,6 +417,53 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va return 0; } +int SystemProperties::Delete(const char *name, bool prune) { + if (!initialized_) { + return -1; + } + + if (!contexts_->rw_) { + return -1; + } + + prop_area* serial_pa = contexts_->GetSerialPropArea(); + if (serial_pa == nullptr) { + return -1; + } + + prop_area* pa = contexts_->GetPropAreaForName(name); + if (!pa) { + return -1; + } + + if (!pa->remove(name, prune)) { + return -1; + } + + if (appcompat_override_contexts_ != nullptr) { + bool is_override = is_appcompat_override(name); + const char* override_name = name; + if (is_override) override_name += strlen(APPCOMPAT_PREFIX); + prop_area* other_pa = appcompat_override_contexts_->GetPropAreaForName(override_name); + prop_area* other_serial_pa = appcompat_override_contexts_->GetSerialPropArea(); + CHECK(other_pa && other_serial_pa); + if (other_pa->remove(override_name, prune)) { + atomic_store_explicit( + other_serial_pa->serial(), + atomic_load_explicit(other_serial_pa->serial(), memory_order_relaxed) + 1, + memory_order_release); + } + } + + // There is only a single mutator, but we want to make sure that + // updates are visible to a reader waiting for the update. + atomic_store_explicit(serial_pa->serial(), + atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(serial_pa->serial(), INT32_MAX); + return 0; +} + uint32_t SystemProperties::WaitAny(uint32_t old_serial) { uint32_t new_serial; Wait(nullptr, old_serial, &new_serial, nullptr); diff --git a/system_property_api.cpp b/system_property_api.cpp index 0844905b9..1d2796bd4 100644 --- a/system_property_api.cpp +++ b/system_property_api.cpp @@ -86,6 +86,11 @@ int __system_property_update(prop_info* pi, const char* value, unsigned int len) return system_properties.Update(pi, value, len); } +__BIONIC_WEAK_FOR_NATIVE_BRIDGE +int __system_property_delete(const char* name, bool prune) { + return system_properties.Delete(name, prune); +} + __BIONIC_WEAK_FOR_NATIVE_BRIDGE int __system_property_add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen) { From 1fb7e8a9db62cf93e6d0e6ffddeff627042bdcf9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 5 Jun 2024 18:27:13 -0700 Subject: [PATCH 07/10] Support get property context Co-authored-by: canyie Co-authored-by: vvb2060 --- contexts_serialized.cpp | 6 ++++++ contexts_split.cpp | 8 ++++++++ include/api/system_properties.h | 7 +++++++ include/system_properties/contexts.h | 1 + include/system_properties/contexts_pre_split.h | 4 ++++ include/system_properties/contexts_serialized.h | 1 + include/system_properties/contexts_split.h | 1 + include/system_properties/system_properties.h | 1 + system_properties.cpp | 8 ++++++++ system_property_api.cpp | 5 +++++ 10 files changed, 42 insertions(+) diff --git a/contexts_serialized.cpp b/contexts_serialized.cpp index f810913a4..df0a4a701 100644 --- a/contexts_serialized.cpp +++ b/contexts_serialized.cpp @@ -145,6 +145,12 @@ prop_area* ContextsSerialized::GetPropAreaForName(const char* name) { return context_node->pa(); } +const char* ContextsSerialized::GetContextForName(const char* name) { + const char* context; + property_info_area_file_->GetPropertyInfo(name, &context, nullptr); + return context; +} + 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()) { diff --git a/contexts_split.cpp b/contexts_split.cpp index 064292e6b..ba8b262d5 100644 --- a/contexts_split.cpp +++ b/contexts_split.cpp @@ -340,6 +340,14 @@ prop_area* ContextsSplit::GetPropAreaForName(const char* name) { return cnode->pa(); } +const char* ContextsSplit::GetContextForName(const char* name) { + auto entry = GetPrefixNodeForName(name); + if (!entry) { + return nullptr; + } + return entry->context->context(); +} + void ContextsSplit::ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { ListForEach(contexts_, [propfn, cookie](ContextListNode* l) { if (l->CheckAccessAndOpen()) { diff --git a/include/api/system_properties.h b/include/api/system_properties.h index 81e57ae0a..3e0fe0ddd 100644 --- a/include/api/system_properties.h +++ b/include/api/system_properties.h @@ -238,6 +238,13 @@ int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __va */ int __system_property_delete(const char* _Nonnull __name, bool __prune); +/** + * Get context of a property. + * + * Returns the context on success, nullptr if fail. + */ +const char* _Nullable __system_property_get_context(const char* _Nonnull __name); + /** * Reloads the system properties from disk. * Not intended for use by any apps except the Zygote. diff --git a/include/system_properties/contexts.h b/include/system_properties/contexts.h index a59c06373..967f09cc3 100644 --- a/include/system_properties/contexts.h +++ b/include/system_properties/contexts.h @@ -40,6 +40,7 @@ class Contexts { bool load_default_path = false) = 0; virtual prop_area* GetPropAreaForName(const char* name) = 0; virtual prop_area* GetSerialPropArea() = 0; + virtual const char* GetContextForName(const char* name) = 0; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) = 0; virtual void ResetAccess() = 0; virtual void FreeAndUnmap() = 0; diff --git a/include/system_properties/contexts_pre_split.h b/include/system_properties/contexts_pre_split.h index 52f08f906..0e90471f1 100644 --- a/include/system_properties/contexts_pre_split.h +++ b/include/system_properties/contexts_pre_split.h @@ -51,6 +51,10 @@ class ContextsPreSplit : public Contexts { return pre_split_prop_area_; } + virtual const char* GetContextForName(const char*) override { + return "u:object_r:properties_device:s0"; + } + virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override { pre_split_prop_area_->foreach (propfn, cookie); } diff --git a/include/system_properties/contexts_serialized.h b/include/system_properties/contexts_serialized.h index 8bb0b1136..27c69c476 100644 --- a/include/system_properties/contexts_serialized.h +++ b/include/system_properties/contexts_serialized.h @@ -45,6 +45,7 @@ class ContextsSerialized : public Contexts { virtual prop_area* GetSerialPropArea() override { return serial_prop_area_; } + virtual const char* GetContextForName(const char* name) override; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override; virtual void ResetAccess() override; virtual void FreeAndUnmap() override; diff --git a/include/system_properties/contexts_split.h b/include/system_properties/contexts_split.h index 321cfd25d..654c72ccb 100644 --- a/include/system_properties/contexts_split.h +++ b/include/system_properties/contexts_split.h @@ -44,6 +44,7 @@ class ContextsSplit : public Contexts { virtual prop_area* GetSerialPropArea() override { return serial_prop_area_; } + virtual const char* GetContextForName(const char* name) override; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override; virtual void ResetAccess() override; virtual void FreeAndUnmap() override; diff --git a/include/system_properties/system_properties.h b/include/system_properties/system_properties.h index 5a2f27d11..23dd7044a 100644 --- a/include/system_properties/system_properties.h +++ b/include/system_properties/system_properties.h @@ -66,6 +66,7 @@ class SystemProperties { int Update(prop_info* pi, const char* value, unsigned int len); int Add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); int Delete(const char* name, bool prune); + const char* GetContext(const char* name); uint32_t WaitAny(uint32_t old_serial); bool Wait(const prop_info* pi, uint32_t old_serial, uint32_t* new_serial_ptr, const timespec* relative_timeout); diff --git a/system_properties.cpp b/system_properties.cpp index 8535ed94a..b1b151a2d 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -464,6 +464,14 @@ int SystemProperties::Delete(const char *name, bool prune) { return 0; } +const char* SystemProperties::GetContext(const char* name) { + if (!initialized_) { + return nullptr; + } + + return contexts_->GetContextForName(name); +} + uint32_t SystemProperties::WaitAny(uint32_t old_serial) { uint32_t new_serial; Wait(nullptr, old_serial, &new_serial, nullptr); diff --git a/system_property_api.cpp b/system_property_api.cpp index 1d2796bd4..50756ac3a 100644 --- a/system_property_api.cpp +++ b/system_property_api.cpp @@ -91,6 +91,11 @@ int __system_property_delete(const char* name, bool prune) { return system_properties.Delete(name, prune); } +__BIONIC_WEAK_FOR_NATIVE_BRIDGE +const char* __system_property_get_context(const char *name) { + return system_properties.GetContext(name); +} + __BIONIC_WEAK_FOR_NATIVE_BRIDGE int __system_property_add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen) { From 5a9de8f85f4a23df7d7922cff39da94f0c06ee4e Mon Sep 17 00:00:00 2001 From: canyie Date: Fri, 12 Apr 2024 23:48:29 +0800 Subject: [PATCH 08/10] Ensure prop has zero padding This prevents undesirable behavior if apps directly reads prop info from devfs node. --- system_properties.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system_properties.cpp b/system_properties.cpp index b1b151a2d..2977b9112 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -309,9 +309,9 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed); serial |= 1; - strlcpy(pi->value, value, len + 1); + strncpy(pi->value, value, PROP_VALUE_MAX); if (have_override) { - strlcpy(override_pi->value, value, len + 1); + strncpy(override_pi->value, value, PROP_VALUE_MAX); } // Now the primary value property area is up-to-date. Let readers know that they should // look at the property value instead of the backup area. From b7c2088565fbe13d22fe074960332e89615bb4aa Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Fri, 16 May 2025 08:26:22 +0000 Subject: [PATCH 09/10] Restore serial after update --- system_properties.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/system_properties.cpp b/system_properties.cpp index 2977b9112..b49088378 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -332,6 +332,15 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) } __futex_wake(serial_pa->serial(), INT32_MAX); + // Now that the serial value has been updated so waits on that serial has been unblocked, + // we restore the serial number back to the original value to hide traces of modification. + atomic_thread_fence(memory_order_release); + new_serial = (len << 24) | ((serial & ~1) & 0xffffff); + atomic_store_explicit(&pi->serial, new_serial, memory_order_relaxed); + if (have_override) { + atomic_store_explicit(&override_pi->serial, new_serial, memory_order_relaxed); + } + return 0; } From 47eb97d6aedf20bf4656f75f98bb01482ab729b0 Mon Sep 17 00:00:00 2001 From: Wang Han <416810799@qq.com> Date: Sat, 14 Mar 2026 19:22:29 +0800 Subject: [PATCH 10/10] Support compacting prop area This allows us to keep dictionary tree structure after deleting props. --- contexts_serialized.cpp | 32 +++ contexts_split.cpp | 28 +++ include/api/system_properties.h | 14 ++ include/system_properties/contexts.h | 2 + .../system_properties/contexts_pre_split.h | 15 ++ .../system_properties/contexts_serialized.h | 2 + include/system_properties/contexts_split.h | 2 + include/system_properties/prop_area.h | 1 + include/system_properties/system_properties.h | 2 + prop_area.cpp | 182 ++++++++++++++++++ system_properties.cpp | 49 +++++ system_property_api.cpp | 10 + 12 files changed, 339 insertions(+) diff --git a/contexts_serialized.cpp b/contexts_serialized.cpp index df0a4a701..d566ad86e 100644 --- a/contexts_serialized.cpp +++ b/contexts_serialized.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -159,6 +160,37 @@ void ContextsSerialized::ForEach(void (*propfn)(const prop_info* pi, void* cooki } } +bool ContextsSerialized::Compact() { + for (size_t i = 0; i < num_context_nodes_; ++i) { + if (!context_nodes_[i].CheckAccessAndOpen() || !context_nodes_[i].pa()->compact()) { + return false; + } + } + return !serial_prop_area_ || serial_prop_area_->compact(); +} + +bool ContextsSerialized::CompactContext(const char* context, bool* found) { + bool ret = true; + *found = false; + + for (size_t i = 0; i < num_context_nodes_; ++i) { + if (!strcmp(context_nodes_[i].context(), context)) { + *found = true; + if (!context_nodes_[i].CheckAccessAndOpen() || !context_nodes_[i].pa()->compact()) { + ret = false; + } + } + } + + if (!*found && serial_prop_area_ && + !strcmp(context, "u:object_r:properties_serial:s0")) { + *found = true; + ret = serial_prop_area_->compact(); + } + + return *found && ret; +} + void ContextsSerialized::ResetAccess() { for (size_t i = 0; i < num_context_nodes_; ++i) { context_nodes_[i].ResetAccess(); diff --git a/contexts_split.cpp b/contexts_split.cpp index ba8b262d5..cc15a4b8b 100644 --- a/contexts_split.cpp +++ b/contexts_split.cpp @@ -356,6 +356,34 @@ void ContextsSplit::ForEach(void (*propfn)(const prop_info* pi, void* cookie), v }); } +bool ContextsSplit::Compact() { + bool ret = true; + ListForEach(contexts_, [&ret](ContextListNode* l) { + ret = ret && l->CheckAccessAndOpen() && l->pa()->compact(); + }); + return ret && (!serial_prop_area_ || serial_prop_area_->compact()); +} + +bool ContextsSplit::CompactContext(const char* context, bool* found) { + bool ret = true; + *found = false; + + ListForEach(contexts_, [&ret, found, context](ContextListNode* l) { + if (!strcmp(l->context(), context)) { + *found = true; + ret = ret && l->CheckAccessAndOpen() && l->pa()->compact(); + } + }); + + if (!*found && serial_prop_area_ && + !strcmp(context, "u:object_r:properties_serial:s0")) { + *found = true; + ret = serial_prop_area_->compact(); + } + + return *found && ret; +} + void ContextsSplit::ResetAccess() { ListForEach(contexts_, [](ContextListNode* l) { l->ResetAccess(); }); } diff --git a/include/api/system_properties.h b/include/api/system_properties.h index 3e0fe0ddd..e6a1afd93 100644 --- a/include/api/system_properties.h +++ b/include/api/system_properties.h @@ -238,6 +238,20 @@ int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __va */ int __system_property_delete(const char* _Nonnull __name, bool __prune); +/** + * Compact the property area to preserve dictionary tree structure after deletions. + * + * Returns 0 on success, -1 on failure. + */ +bool __system_property_compact(void); + +/** + * Compact the property area for a specific context label. + * + * Returns true on success, false on failure or if the context was not found. + */ +bool __system_property_compact_context(const char* _Nonnull __context); + /** * Get context of a property. * diff --git a/include/system_properties/contexts.h b/include/system_properties/contexts.h index 967f09cc3..3d1f26935 100644 --- a/include/system_properties/contexts.h +++ b/include/system_properties/contexts.h @@ -42,6 +42,8 @@ class Contexts { virtual prop_area* GetSerialPropArea() = 0; virtual const char* GetContextForName(const char* name) = 0; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) = 0; + virtual bool Compact() = 0; + virtual bool CompactContext(const char* context, bool* found) = 0; virtual void ResetAccess() = 0; virtual void FreeAndUnmap() = 0; bool rw_ = false; diff --git a/include/system_properties/contexts_pre_split.h b/include/system_properties/contexts_pre_split.h index 0e90471f1..fd34b8e0f 100644 --- a/include/system_properties/contexts_pre_split.h +++ b/include/system_properties/contexts_pre_split.h @@ -28,6 +28,8 @@ #pragma once +#include + #include "contexts.h" #include "prop_area.h" #include "prop_info.h" @@ -59,6 +61,19 @@ class ContextsPreSplit : public Contexts { pre_split_prop_area_->foreach (propfn, cookie); } + virtual bool Compact() override { + return pre_split_prop_area_->compact(); + } + + virtual bool CompactContext(const char* context, bool* found) override { + if (context && !strcmp(context, GetContextForName(""))) { + *found = true; + return pre_split_prop_area_->compact(); + } + *found = false; + return false; + } + // This is a no-op for pre-split properties as there is only one property file and it is // accessible by all domains virtual void ResetAccess() override { diff --git a/include/system_properties/contexts_serialized.h b/include/system_properties/contexts_serialized.h index 27c69c476..91a4420d0 100644 --- a/include/system_properties/contexts_serialized.h +++ b/include/system_properties/contexts_serialized.h @@ -47,6 +47,8 @@ class ContextsSerialized : public Contexts { } virtual const char* GetContextForName(const char* name) override; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override; + virtual bool Compact() override; + virtual bool CompactContext(const char* context, bool* found) override; virtual void ResetAccess() override; virtual void FreeAndUnmap() override; diff --git a/include/system_properties/contexts_split.h b/include/system_properties/contexts_split.h index 654c72ccb..1eb5cbc92 100644 --- a/include/system_properties/contexts_split.h +++ b/include/system_properties/contexts_split.h @@ -46,6 +46,8 @@ class ContextsSplit : public Contexts { } virtual const char* GetContextForName(const char* name) override; virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) override; + virtual bool Compact() override; + virtual bool CompactContext(const char* context, bool* found) override; virtual void ResetAccess() override; virtual void FreeAndUnmap() override; diff --git a/include/system_properties/prop_area.h b/include/system_properties/prop_area.h index 119843d6d..996ff8e57 100644 --- a/include/system_properties/prop_area.h +++ b/include/system_properties/prop_area.h @@ -125,6 +125,7 @@ class prop_area { const prop_info* find(const char* name); bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); bool remove(const char* name, bool prune); + bool compact(); bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie); diff --git a/include/system_properties/system_properties.h b/include/system_properties/system_properties.h index 23dd7044a..b3308239b 100644 --- a/include/system_properties/system_properties.h +++ b/include/system_properties/system_properties.h @@ -66,6 +66,8 @@ class SystemProperties { int Update(prop_info* pi, const char* value, unsigned int len); int Add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); int Delete(const char* name, bool prune); + bool Compact(); + bool Compact(const char* context); const char* GetContext(const char* name); uint32_t WaitAny(uint32_t old_serial); bool Wait(const prop_info* pi, uint32_t old_serial, uint32_t* new_serial_ptr, diff --git a/prop_area.cpp b/prop_area.cpp index 3ccd46d76..ef557d2e5 100644 --- a/prop_area.cpp +++ b/prop_area.cpp @@ -37,7 +37,9 @@ #include #include +#include #include +#include #ifdef LARGE_SYSTEM_PROPERTY_NODE constexpr size_t PA_SIZE = 1024 * 1024; @@ -391,6 +393,92 @@ bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* #define get_offset(ptr) atomic_load_explicit(ptr, memory_order_relaxed) #define set_offset(ptr, val) atomic_store_explicit(ptr, val, memory_order_release) +namespace { + +struct LiveObject { + uint_least32_t old_offset; + uint32_t size; + uint32_t aligned_size; + uint_least32_t new_offset; +}; + +struct LongValueRef { + uint_least32_t prop_old_offset; + uint_least32_t long_old_offset; +}; + +inline uint_least32_t ptr_to_offset(const char* base, const void* ptr) { + return static_cast(reinterpret_cast(ptr) - base); +} + +inline prop_trie_node* offset_to_node(char* base, uint_least32_t off) { + return reinterpret_cast(base + off); +} + +inline prop_info* offset_to_info(char* base, uint_least32_t off) { + return reinterpret_cast(base + off); +} + +void collect_live_objects(char* base, prop_trie_node* node, bool skip_node, + std::vector& objects, + std::vector& node_offsets, + std::vector& prop_offsets, + std::vector& long_refs) { + if (!node) return; + + if (!skip_node) { + uint_least32_t node_off = ptr_to_offset(base, node); + uint32_t node_size = sizeof(prop_trie_node) + node->namelen + 1; + objects.push_back({node_off, node_size, + static_cast( + __BIONIC_ALIGN(node_size, sizeof(uint_least32_t))), + 0u}); + node_offsets.push_back(node_off); + } + + uint_least32_t prop_off = get_offset(&node->prop); + if (prop_off != 0) { + prop_info* info = offset_to_info(base, prop_off); + uint32_t name_len = strlen(info->name); + uint32_t info_size = sizeof(prop_info) + name_len + 1; + objects.push_back({prop_off, info_size, + static_cast( + __BIONIC_ALIGN(info_size, sizeof(uint_least32_t))), + 0u}); + prop_offsets.push_back(prop_off); + + if (info->is_long()) { + const char* long_val = info->long_value(); + uint32_t long_len = strlen(long_val); + uint_least32_t long_off = ptr_to_offset(base, long_val); + uint32_t long_size = long_len + 1; + objects.push_back({long_off, long_size, + static_cast( + __BIONIC_ALIGN(long_size, sizeof(uint_least32_t))), + 0u}); + long_refs.push_back({prop_off, long_off}); + } + } + + uint_least32_t left_offset = get_offset(&node->left); + if (left_offset != 0) { + collect_live_objects(base, offset_to_node(base, left_offset), false, objects, + node_offsets, prop_offsets, long_refs); + } + uint_least32_t children_offset = get_offset(&node->children); + if (children_offset != 0) { + collect_live_objects(base, offset_to_node(base, children_offset), false, objects, + node_offsets, prop_offsets, long_refs); + } + uint_least32_t right_offset = get_offset(&node->right); + if (right_offset != 0) { + collect_live_objects(base, offset_to_node(base, right_offset), false, objects, + node_offsets, prop_offsets, long_refs); + } +} + +} // namespace + // After removing a property, it is possible that redundant nodes will appear in the trie. // DFS through the data structure, remove leaf nodes that do not hold properties, remove // them from the trie, then backtrack recursively and remove redundant parent nodes. @@ -455,3 +543,97 @@ bool prop_area::remove(const char *name, bool prune) { return true; } + +bool prop_area::compact() { + const uint32_t data_start = sizeof(prop_trie_node) + + __BIONIC_ALIGN(PROP_VALUE_MAX, sizeof(uint_least32_t)); + const uint32_t old_bytes_used = bytes_used_; + if (old_bytes_used < data_start) { + return false; + } + + std::vector objects; + std::vector node_offsets; + std::vector prop_offsets; + std::vector long_refs; + + collect_live_objects(data_, root_node(), true, objects, node_offsets, prop_offsets, long_refs); + + if (objects.empty()) { + if (old_bytes_used != data_start) { + memset(data_ + data_start, 0, old_bytes_used - data_start); + bytes_used_ = data_start; + } + return true; + } + + std::sort(objects.begin(), objects.end(), + [](const LiveObject& a, const LiveObject& b) { return a.old_offset < b.old_offset; }); + std::sort(long_refs.begin(), long_refs.end(), + [](const LongValueRef& a, const LongValueRef& b) { + return a.prop_old_offset < b.prop_old_offset; + }); + + uint32_t next = data_start; + for (auto& obj : objects) { + obj.new_offset = next; + next += obj.aligned_size; + } + + for (const auto& obj : objects) { + if (obj.new_offset != obj.old_offset) { + memmove(data_ + obj.new_offset, data_ + obj.old_offset, obj.aligned_size); + } + } + + if (next < old_bytes_used) { + memset(data_ + next, 0, old_bytes_used - next); + } + bytes_used_ = next; + + auto remap_offset = [&objects](uint_least32_t old_off) -> uint_least32_t { + if (old_off == 0) return 0; + auto it = std::lower_bound( + objects.begin(), objects.end(), old_off, + [](const LiveObject& obj, uint_least32_t off) { return obj.old_offset < off; }); + if (it == objects.end() || it->old_offset != old_off) { + return old_off; + } + return it->new_offset; + }; + + auto update_node = [&remap_offset](prop_trie_node* node) { + uint_least32_t left = get_offset(&node->left); + if (left != 0) set_offset(&node->left, remap_offset(left)); + uint_least32_t right = get_offset(&node->right); + if (right != 0) set_offset(&node->right, remap_offset(right)); + uint_least32_t children = get_offset(&node->children); + if (children != 0) set_offset(&node->children, remap_offset(children)); + uint_least32_t prop = get_offset(&node->prop); + if (prop != 0) set_offset(&node->prop, remap_offset(prop)); + }; + + update_node(root_node()); + for (uint_least32_t old_off : node_offsets) { + uint_least32_t new_off = remap_offset(old_off); + update_node(offset_to_node(data_, new_off)); + } + + for (uint_least32_t prop_old_off : prop_offsets) { + uint_least32_t prop_new_off = remap_offset(prop_old_off); + prop_info* info = offset_to_info(data_, prop_new_off); + if (!info->is_long()) continue; + + auto it = std::lower_bound( + long_refs.begin(), long_refs.end(), prop_old_off, + [](const LongValueRef& ref, uint_least32_t off) { return ref.prop_old_offset < off; }); + if (it == long_refs.end() || it->prop_old_offset != prop_old_off) { + continue; + } + + uint_least32_t long_new_off = remap_offset(it->long_old_offset); + info->long_property.offset = long_new_off - prop_new_off; + } + + return true; +} diff --git a/system_properties.cpp b/system_properties.cpp index b49088378..43eac5076 100644 --- a/system_properties.cpp +++ b/system_properties.cpp @@ -473,6 +473,55 @@ int SystemProperties::Delete(const char *name, bool prune) { return 0; } +bool SystemProperties::Compact() { + if (!initialized_) { + return false; + } + + if (!contexts_->rw_) { + return false; + } + + bool ret = contexts_->Compact(); + if (appcompat_override_contexts_ != nullptr) { + ret &= appcompat_override_contexts_->Compact(); + } + + return ret; +} + +bool SystemProperties::Compact(const char* context) { + if (!initialized_) { + return false; + } + + if (!contexts_->rw_) { + return false; + } + + if (context == nullptr || context[0] == '\0') { + return Compact(); + } + + bool found_main = false; + bool ret = contexts_->CompactContext(context, &found_main); + bool found_any = found_main; + if (!found_main) { + ret = true; + } + + if (appcompat_override_contexts_ != nullptr) { + bool found_override = false; + bool ret_override = appcompat_override_contexts_->CompactContext(context, &found_override); + if (found_override) { + found_any = true; + ret &= ret_override; + } + } + + return found_any && ret; +} + const char* SystemProperties::GetContext(const char* name) { if (!initialized_) { return nullptr; diff --git a/system_property_api.cpp b/system_property_api.cpp index 50756ac3a..e967ed209 100644 --- a/system_property_api.cpp +++ b/system_property_api.cpp @@ -91,6 +91,16 @@ int __system_property_delete(const char* name, bool prune) { return system_properties.Delete(name, prune); } +__BIONIC_WEAK_FOR_NATIVE_BRIDGE +bool __system_property_compact() { + return system_properties.Compact(); +} + +__BIONIC_WEAK_FOR_NATIVE_BRIDGE +bool __system_property_compact_context(const char* context) { + return system_properties.Compact(context); +} + __BIONIC_WEAK_FOR_NATIVE_BRIDGE const char* __system_property_get_context(const char *name) { return system_properties.GetContext(name);