diff --git a/libc/bionic/system_properties.cpp b/libc/bionic/system_properties.cpp index b9a373eb9..27204553d 100644 --- a/libc/bionic/system_properties.cpp +++ b/libc/bionic/system_properties.cpp @@ -200,6 +200,7 @@ static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME; static bool compat_mode = false; static size_t pa_data_size; static size_t pa_size; +static bool initialized = false; // NOTE: This isn't static because system_properties_compat.c // requires it. @@ -642,22 +643,33 @@ bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* return foreach_property(root_node(), propfn, cookie); } -struct context_node { - context_node(struct context_node* next, const char* context, prop_area* pa) - : context(strdup(context)), pa(pa), checked_access(false), next(next) { - lock.init(false); +class context_node { +public: + context_node(context_node* next, const char* context, prop_area* pa) + : next(next), context_(strdup(context)), pa_(pa), no_access_(false) { + lock_.init(false); } ~context_node() { - if (pa) { - munmap(pa, pa_size); - } - free(context); + unmap(); + free(context_); } - Lock lock; - char* context; - prop_area* pa; - bool checked_access; - struct context_node* next; + bool open(bool access_rw, bool* fsetxattr_failed); + bool check_access_and_open(); + void reset_access(); + + const char* context() const { return context_; } + prop_area* pa() { return pa_; } + + context_node* next; + +private: + bool check_access(); + void unmap(); + + Lock lock_; + char* context_; + prop_area* pa_; + bool no_access_; }; struct prefix_node { @@ -733,32 +745,50 @@ static context_node* contexts = nullptr; * allocation of memory for each filename. */ -static bool open_prop_file(context_node* cnode, bool access_rw, bool* fsetxattr_failed) { - cnode->lock.lock(); - if (cnode->pa) { - cnode->lock.unlock(); +bool context_node::open(bool access_rw, bool* fsetxattr_failed) { + lock_.lock(); + if (pa_) { + lock_.unlock(); return true; } char filename[PROP_FILENAME_MAX]; - int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context); + int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, context_); if (len < 0 || len > PROP_FILENAME_MAX) { - cnode->lock.unlock(); + lock_.unlock(); return false; } if (access_rw) { - cnode->pa = map_prop_area_rw(filename, cnode->context, fsetxattr_failed); + pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed); } else { - cnode->pa = map_prop_area(filename, false); + pa_ = map_prop_area(filename, false); } - cnode->lock.unlock(); - return cnode->pa; + lock_.unlock(); + return pa_; } -static bool check_access(context_node* cnode) { +bool context_node::check_access_and_open() { + if (!pa_ && !no_access_) { + if (!check_access() || !open(false, nullptr)) { + no_access_ = true; + } + } + return pa_; +} + +void context_node::reset_access() { + if (!check_access()) { + unmap(); + no_access_ = true; + } else { + no_access_ = false; + } +} + +bool context_node::check_access() { char filename[PROP_FILENAME_MAX]; - int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context); + int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, context_); if (len < 0 || len > PROP_FILENAME_MAX) { return false; } @@ -766,6 +796,18 @@ static bool check_access(context_node* cnode) { return access(filename, R_OK) == 0; } +void context_node::unmap() { + if (!pa_) { + return; + } + + munmap(pa_, pa_size); + if (pa_ == __system_property_area__) { + __system_property_area__ = nullptr; + } + pa_ = nullptr; +} + static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) { char filename[PROP_FILENAME_MAX]; int len = snprintf(filename, sizeof(filename), "%s/properties_serial", property_filename); @@ -792,10 +834,15 @@ static prop_area* get_prop_area_for_name(const char* name) { } auto cnode = entry->context; - if (!cnode->pa) { - open_prop_file(cnode, false, nullptr); + if (!cnode->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. + */ + cnode->open(false, nullptr); } - return cnode->pa; + return cnode->pa(); } /* @@ -892,9 +939,6 @@ static int read_spec_entries(char *line_buf, int num_args, ...) } static bool initialize_properties() { - list_free(&prefixes); - list_free(&contexts); - FILE* file = fopen("/property_contexts", "re"); if (!file) { @@ -927,7 +971,7 @@ static bool initialize_properties() { } auto old_context = list_find( - contexts, [context](context_node* l) { return !strcmp(l->context, context); }); + contexts, [context](context_node* l) { return !strcmp(l->context(), context); }); if (old_context) { list_add_after_len(&prefixes, prop_prefix, old_context); } else { @@ -951,15 +995,27 @@ static bool is_dir(const char* pathname) { return S_ISDIR(info.st_mode); } +static void free_and_unmap_contexts() { + list_free(&prefixes); + list_free(&contexts); + if (__system_property_area__) { + munmap(__system_property_area__, pa_size); + __system_property_area__ = nullptr; + } +} + int __system_properties_init() { + if (initialized) { + list_foreach(contexts, [](context_node* l) { l->reset_access(); }); + return 0; + } if (is_dir(property_filename)) { if (!initialize_properties()) { return -1; } if (!map_system_property_area(false, nullptr)) { - list_free(&prefixes); - list_free(&contexts); + free_and_unmap_contexts(); return -1; } } else { @@ -970,6 +1026,7 @@ int __system_properties_init() list_add(&contexts, "legacy_system_prop_area", __system_property_area__); list_add_after_len(&prefixes, "*", contexts); } + initialized = true; return 0; } @@ -985,22 +1042,23 @@ int __system_property_set_filename(const char *filename) int __system_property_area_init() { + free_and_unmap_contexts(); mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (!initialize_properties()) { return -1; } - bool open_prop_file_failed = false; + bool open_failed = false; bool fsetxattr_failed = false; - list_foreach(contexts, [&fsetxattr_failed, &open_prop_file_failed](context_node* l) { - if (!open_prop_file(l, true, &fsetxattr_failed)) { - open_prop_file_failed = true; + list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) { + if (!l->open(true, &fsetxattr_failed)) { + open_failed = true; } }); - if (open_prop_file_failed || !map_system_property_area(true, &fsetxattr_failed)) { - list_free(&prefixes); - list_free(&contexts); + if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) { + free_and_unmap_contexts(); return -1; } + initialized = true; return fsetxattr_failed ? -2 : 0; } @@ -1226,14 +1284,8 @@ int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), } list_foreach(contexts, [propfn, cookie](context_node* l) { - if (!l->pa && !l->checked_access) { - if (check_access(l)) { - open_prop_file(l, false, nullptr); - } - l->checked_access = true; - } - if (l->pa) { - l->pa->foreach(propfn, cookie); + if (l->check_access_and_open()) { + l->pa()->foreach(propfn, cookie); } }); return 0;