diff mbox series

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

Message ID 20240131130840.48155-10-cgzones@googlemail.com (mailing list archive)
State New
Delegated to: Petr Lautrbach
Headers show
Series libselinux: rework selabel_file(5) database | expand

Commit Message

Christian Göttsche Jan. 31, 2024, 1:08 p.m. UTC
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(-)
diff mbox series

Patch

diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index d2e703ef..10e78a9b 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 2ff10b2f..ed178d34 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 @@  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.c b/libselinux/src/label_file.c
index ac349bc5..3520fe2d 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 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 fad5ea6d..7bd85804 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 bf569ca5..49c33b3b 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)