@@ -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;
@@ -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 @@ out_error:
free(spec->key);
free(spec->lr.ctx_raw);
free(spec->lr.ctx_trans);
+ __pthread_mutex_destroy(&spec->lr.lock);
}
free(catalog);
fclose(filp);
@@ -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);
@@ -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);
@@ -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;
};
@@ -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)
@@ -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)
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 <cgzones@googlemail.com> --- v2: add patch --- 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(-)