From 414c040c01e2c6bd3c8016a6575b91a95103a30b 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 4a7c6e6da..4f007cbe6 100644 --- a/libselinux/src/label.c +++ b/libselinux/src/label.c @@ -121,18 +121,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 */ @@ -140,11 +154,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 2daf17705..8bee80650 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); } @@ -354,6 +355,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 61529ab75..5fcee8491 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 *dest, struct spec_node *sourc 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 *dest, struct spec_node *sourc 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 52d07cd36..8f301dc9e 100644 --- a/libselinux/src/label_file.h +++ b/libselinux/src/label_file.h @@ -648,6 +648,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++; @@ -781,6 +782,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++; @@ -805,6 +807,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; @@ -819,6 +822,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 4c9879885..7deb85788 100644 --- a/libselinux/src/label_media.c +++ b/libselinux/src/label_media.c @@ -170,6 +170,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 f332dcb69..13fefdf0b 100644 --- a/libselinux/src/label_x.c +++ b/libselinux/src/label_x.c @@ -197,6 +197,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)