From 3d57563d160c96296dfac8993cdb339b51d832ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 16 Dec 2023 15:36:55 +0100 Subject: [PATCH] libselinux: support parallel selabel_lookup(3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the parallel usage of the translated label lookup via selabel_lookup(3) in multi threaded applications by locking the step of computing the translated context and the validation state. A potential use case might can usage from a Rust application via FFI. Signed-off-by: Christian Göttsche --- libselinux/src/label.c | 56 +++++++++++++++++++++++++++------ libselinux/src/label_db.c | 2 ++ libselinux/src/label_file.c | 4 ++- libselinux/src/label_file.h | 4 +++ libselinux/src/label_internal.h | 1 + libselinux/src/label_media.c | 1 + libselinux/src/label_x.c | 1 + 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/libselinux/src/label.c b/libselinux/src/label.c index d2e703efd..10e78a9b1 100644 --- a/libselinux/src/label.c +++ b/libselinux/src/label.c @@ -124,18 +124,32 @@ static inline int selabel_is_validate_set(const struct selinux_opt *opts, int selabel_validate(struct selabel_lookup_rec *contexts) { - int rc = 0; + bool validated; + int rc; - if (contexts->validated) - goto out; + validated = __atomic_load_n(&contexts->validated, __ATOMIC_ACQUIRE); + if (validated) + return 0; + + __pthread_mutex_lock(&contexts->lock); + + /* Check if another thread validated the context while we waited on the mutex */ + validated = __atomic_load_n(&contexts->validated, __ATOMIC_ACQUIRE); + if (validated) { + __pthread_mutex_unlock(&contexts->lock); + return 0; + } rc = selinux_validate(&contexts->ctx_raw); + if (rc == 0) + __atomic_store_n(&contexts->validated, true, __ATOMIC_RELEASE); + + __pthread_mutex_unlock(&contexts->lock); + if (rc < 0) - goto out; + return -1; - contexts->validated = true; -out: - return rc; + return 0; } /* Public API helpers */ @@ -143,11 +157,35 @@ static int selabel_fini(const struct selabel_handle *rec, struct selabel_lookup_rec *lr, bool translating) { + char *ctx_trans; + int rc; + if (compat_validate(rec, lr, rec->spec_file, lr->lineno)) return -1; - if (translating && !lr->ctx_trans && - selinux_raw_to_trans_context(lr->ctx_raw, &lr->ctx_trans)) + if (!translating) + return 0; + + ctx_trans = __atomic_load_n(&lr->ctx_trans, __ATOMIC_ACQUIRE); + if (ctx_trans) + return 0; + + __pthread_mutex_lock(&lr->lock); + + /* Check if another thread translated the context while we waited on the mutex */ + ctx_trans = __atomic_load_n(&lr->ctx_trans, __ATOMIC_ACQUIRE); + if (ctx_trans) { + __pthread_mutex_unlock(&lr->lock); + return 0; + } + + rc = selinux_raw_to_trans_context(lr->ctx_raw, &ctx_trans); + if (rc == 0) + __atomic_store_n(&lr->ctx_trans, ctx_trans, __ATOMIC_RELEASE); + + __pthread_mutex_unlock(&lr->lock); + + if (rc) return -1; return 0; diff --git a/libselinux/src/label_db.c b/libselinux/src/label_db.c index 2ff10b2f4..ed178d346 100644 --- a/libselinux/src/label_db.c +++ b/libselinux/src/label_db.c @@ -183,6 +183,7 @@ db_close(struct selabel_handle *rec) free(spec->key); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); + __pthread_mutex_destroy(&spec->lr.lock); } free(catalog); } @@ -355,6 +356,7 @@ db_init(const struct selinux_opt *opts, unsigned nopts, free(spec->key); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); + __pthread_mutex_destroy(&spec->lr.lock); } free(catalog); fclose(filp); diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c index ac349bc5b..3520fe2de 100644 --- a/libselinux/src/label_file.c +++ b/libselinux/src/label_file.c @@ -175,6 +175,7 @@ static int merge_mmap_spec_nodes(struct spec_node *restrict dest, struct spec_no for (uint32_t i = 0; i < source->literal_specs_num; i++) { source->literal_specs[i].lr.ctx_raw = NULL; source->literal_specs[i].lr.ctx_trans = NULL; + __pthread_mutex_destroy(&source->literal_specs[i].lr.lock); } } else { @@ -214,9 +215,10 @@ static int merge_mmap_spec_nodes(struct spec_node *restrict dest, struct spec_no for (uint32_t i = 0; i < source->regex_specs_num; i++) { source->regex_specs[i].lr.ctx_raw = NULL; source->regex_specs[i].lr.ctx_trans = NULL; + __pthread_mutex_destroy(&source->regex_specs[i].lr.lock); source->regex_specs[i].regex = NULL; source->regex_specs[i].regex_compiled = false; - __pthread_mutex_init(&source->regex_specs[i].regex_lock, NULL); + __pthread_mutex_destroy(&source->regex_specs[i].regex_lock); } } else { assert(dest->regex_specs == NULL); diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h index 529a1bd26..de8190f9d 100644 --- a/libselinux/src/label_file.h +++ b/libselinux/src/label_file.h @@ -661,6 +661,7 @@ static int insert_spec(const struct selabel_handle *rec, struct saved_data *data .lr.ctx_trans = NULL, .lr.lineno = lineno, .lr.validated = false, + .lr.lock = PTHREAD_MUTEX_INITIALIZER, }; data->num_specs++; @@ -794,6 +795,7 @@ static int insert_spec(const struct selabel_handle *rec, struct saved_data *data .lr.ctx_trans = NULL, .lr.lineno = lineno, .lr.validated = false, + .lr.lock = PTHREAD_MUTEX_INITIALIZER, }; data->num_specs++; @@ -818,6 +820,7 @@ static inline void free_spec_node(struct spec_node *node) free(lspec->lr.ctx_raw); free(lspec->lr.ctx_trans); + __pthread_mutex_destroy(&lspec->lr.lock); if (lspec->from_mmap) continue; @@ -832,6 +835,7 @@ static inline void free_spec_node(struct spec_node *node) free(rspec->lr.ctx_raw); free(rspec->lr.ctx_trans); + __pthread_mutex_destroy(&rspec->lr.lock); regex_data_free(rspec->regex); __pthread_mutex_destroy(&rspec->regex_lock); diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h index 854f92faa..743dbf947 100644 --- a/libselinux/src/label_internal.h +++ b/libselinux/src/label_internal.h @@ -71,6 +71,7 @@ extern void digest_gen_hash(struct selabel_digest *digest); struct selabel_lookup_rec { char * ctx_raw; char * ctx_trans; + pthread_mutex_t lock; /* lock for validation and translation */ unsigned int lineno; bool validated; }; diff --git a/libselinux/src/label_media.c b/libselinux/src/label_media.c index fad5ea6d0..7bd85804d 100644 --- a/libselinux/src/label_media.c +++ b/libselinux/src/label_media.c @@ -172,6 +172,7 @@ static void close(struct selabel_handle *rec) free(spec->key); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); + __pthread_mutex_destroy(&spec->lr.lock); } if (spec_arr) diff --git a/libselinux/src/label_x.c b/libselinux/src/label_x.c index bf569ca56..49c33b3b8 100644 --- a/libselinux/src/label_x.c +++ b/libselinux/src/label_x.c @@ -199,6 +199,7 @@ static void close(struct selabel_handle *rec) free(spec->key); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); + __pthread_mutex_destroy(&spec->lr.lock); } if (spec_arr)