diff mbox series

[v3,9/9] libselinux: support parallel selabel_lookup(3)

Message ID 20241105183319.250410-10-cgoettsche@seltendoof.de (mailing list archive)
State Accepted
Commit 20175564fcc6
Delegated to: Petr Lautrbach
Headers show
Series libselinux: rework selabel_file(5) database | expand

Commit Message

Christian Göttsche Nov. 5, 2024, 6:33 p.m. UTC
From: Christian Göttsche <cgzones@googlemail.com>

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>
---
 libselinux/src/label.c          | 56 +++++++++++++++++++++++++++------
 libselinux/src/label_db.c       |  2 ++
 libselinux/src/label_file.h     |  4 +++
 libselinux/src/label_internal.h |  1 +
 libselinux/src/label_media.c    |  1 +
 libselinux/src/label_x.c        |  1 +
 6 files changed, 56 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index 06d743ec..2c510290 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 40d5fc4a..eb060ede 100644
--- a/libselinux/src/label_db.c
+++ b/libselinux/src/label_db.c
@@ -186,6 +186,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);
 }
@@ -358,6 +359,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);
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index 529a1bd2..de8190f9 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 854f92fa..743dbf94 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 d535ef86..0510b5b1 100644
--- a/libselinux/src/label_media.c
+++ b/libselinux/src/label_media.c
@@ -177,6 +177,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 c0d1d475..1a5b9268 100644
--- a/libselinux/src/label_x.c
+++ b/libselinux/src/label_x.c
@@ -204,6 +204,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)