Re: [PATCH testsuite v2 2/2] tests/file_contexts: add tests for multiple SELABEL_OPT_PATH
Stephen Smalley <[email protected]> Thu, 23 Jul 2026 12:24:29 -0400
| Newsgroups | org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAEjxPJ4YWcccS-rFme4LxqBQbZff-ZafkKQzskrpaqVy+2N1cQ@mail.gmail.com> |
On Wed, Jul 22, 2026 at 11:13 PM Thiébaud Weksteen <[email protected]> wrote: > > Add unit tests in test_multiple.c to exercise opening the file contexts > backend with multiple SELABEL_OPT_PATH options under various validation > and path configuration scenarios. > > The following test functions were added: > - Verifies opening multiple file contexts without validation. > - Verifies validation failure when contexts contain undefined types. > - Verifies extra files (.subs, .local, .homedirs) are processed only for > the primary path. > - Verifies multiple files with the same definition, no error is raised. > - Verifies context lookups when providing three distinct > SELABEL_OPT_PATH options. > > Signed-off-by: Thiébaud Weksteen <[email protected]> We need to avoid breaking the testsuite when it is run with older libselinux before your patch too. Should just skip the multi-file tests when the system libselinux doesn't support it. > --- > tests/file_contexts/Makefile | 2 +- > tests/file_contexts/test | 5 +- > tests/file_contexts/test_multiple.c | 233 ++++++++++++++++++++++++++++ > 3 files changed, 238 insertions(+), 2 deletions(-) > create mode 100644 tests/file_contexts/test_multiple.c > > diff --git a/tests/file_contexts/Makefile b/tests/file_contexts/Makefile > index 592a65f..083ef25 100644 > --- a/tests/file_contexts/Makefile > +++ b/tests/file_contexts/Makefile > @@ -1,4 +1,4 @@ > -TARGETS=test_open test_lookup test_open_base test_validate > +TARGETS=test_open test_lookup test_open_base test_validate test_multiple > CFLAGS += -O2 -Werror -Wall > LDLIBS += -lselinux > > diff --git a/tests/file_contexts/test b/tests/file_contexts/test > index 1534925..cd7849b 100755 > --- a/tests/file_contexts/test > +++ b/tests/file_contexts/test > @@ -5,7 +5,7 @@ > > use Test; > > -BEGIN { plan tests => 4; } > +BEGIN { plan tests => 5; } > > $basedir = $0; > $basedir =~ s|(.*)/[^/]*|$1|; > @@ -22,4 +22,7 @@ ok( $result, 0 ); > $result = system "$basedir/test_validate $basedir 2>&1"; > ok( $result, 0 ); > > +$result = system "$basedir/test_multiple $basedir 2>&1"; > +ok( $result, 0 ); > + > exit; > diff --git a/tests/file_contexts/test_multiple.c b/tests/file_contexts/test_multiple.c > new file mode 100644 > index 0000000..65fb726 > --- /dev/null > +++ b/tests/file_contexts/test_multiple.c > @@ -0,0 +1,233 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <sys/stat.h> > +#include <sys/types.h> > +#include <unistd.h> > + > +#include <selinux/label.h> > +#include <selinux/selinux.h> > + > +#include "internal.h" > + > +void test_multiple_no_validation(const char *basedir) > +{ > + struct selabel_handle *hnd; > + > + /* f1.fc and f2.fc file */ > + char *f1_path, *f2_path; > + asprintf(&f1_path, "%s/f1.fc", basedir); > + asprintf(&f2_path, "%s/f2.fc", basedir); > + struct selinux_opt opts[] = { > + { .type = SELABEL_OPT_PATH, .value = f1_path }, > + { .type = SELABEL_OPT_PATH, .value = f2_path } > + }; > + > + hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts)); > + free(f1_path); > + free(f2_path); > + > + if (!hnd) { > + log_errno("Unable to open file backend"); > + exit(2); > + } > + > + struct test_t tests[] = { > + { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + }; > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > + > + selabel_close(hnd); > +} > + > +void test_multiple_with_validation(const char *basedir) > +{ > + struct selabel_handle *hnd; > + > + /* f1.fc and f2.fc file - f1 has undefined type rootfs in test policy */ > + char *f1_path, *f2_path; > + asprintf(&f1_path, "%s/f1.fc", basedir); > + asprintf(&f2_path, "%s/f2.fc", basedir); > + struct selinux_opt opts[] = { > + { .type = SELABEL_OPT_PATH, .value = f1_path }, > + { .type = SELABEL_OPT_PATH, .value = f2_path }, > + { .type = SELABEL_OPT_VALIDATE, .value = "1" } > + }; > + > + hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts)); > + free(f1_path); > + free(f2_path); > + > + if (hnd) { > + log_err("Validation of f1 and f2 should have failed"); > + selabel_close(hnd); > + exit(2); > + } > +} > + > +void test_multiple_with_extras(const char *basedir) > +{ > + struct selabel_handle *hnd; > + char *f2_path, *f3_path; > + asprintf(&f2_path, "%s/f2.fc", basedir); > + asprintf(&f3_path, "%s/f3.fc", basedir); > + > + /* 1. f3.fc is the first path: extras (.subs, .local, .homedirs) of f3 ARE processed */ > + struct selinux_opt opts_f3_first[] = { > + { .type = SELABEL_OPT_PATH, .value = f3_path }, > + { .type = SELABEL_OPT_PATH, .value = f2_path } > + }; > + hnd = selabel_open(SELABEL_CTX_FILE, opts_f3_first, > + ARRAY_SIZE(opts_f3_first)); > + if (!hnd) { > + log_errno("Unable to open file backend"); > + exit(2); > + } > + > + struct test_t tests_f3_first[] = { > + { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > + { > + .path = "/sub", > + .context = "system_u:object_r:test_subbed:s0" > + }, > + { > + .path = "/local", > + .context = "system_u:object_r:test_local:s0" > + }, > + { > + .path = "/homedirs", > + .context = "system_u:object_r:test_homedirs:s0" > + }, > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + }; > + assertContextsMatch(hnd, __func__, tests_f3_first, > + ARRAY_SIZE(tests_f3_first)); > + selabel_close(hnd); > + > + /* 2. f2.fc is the first path, f3.fc is second: extras from f3 are NOT processed */ > + struct selinux_opt opts_f2_first[] = { > + { .type = SELABEL_OPT_PATH, .value = f2_path }, > + { .type = SELABEL_OPT_PATH, .value = f3_path } > + }; > + hnd = selabel_open(SELABEL_CTX_FILE, opts_f2_first, > + ARRAY_SIZE(opts_f2_first)); > + if (!hnd) { > + log_errno("Unable to open file backend"); > + exit(2); > + } > + > + struct test_t tests_f2_first[] = { > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > + { > + .path = "/subbed", > + .context = "system_u:object_r:test_subbed:s0" > + }, > + /* /sub, /local, /homedirs should NOT match the f3 extra contexts */ > + { .path = "/sub", .context = NULL }, > + { .path = "/local", .context = NULL }, > + { .path = "/homedirs", .context = NULL }, > + }; > + assertContextsMatch(hnd, __func__, tests_f2_first, > + ARRAY_SIZE(tests_f2_first)); > + selabel_close(hnd); > + > + free(f2_path); > + free(f3_path); > +} > + > +void test_multiple_three_paths(const char *basedir) > +{ > + struct selabel_handle *hnd; > + char *f1_path, *f2_path, *f3_path; > + asprintf(&f1_path, "%s/f1.fc", basedir); > + asprintf(&f2_path, "%s/f2.fc", basedir); > + asprintf(&f3_path, "%s/f3.fc", basedir); > + > + struct selinux_opt opts[] = { > + { .type = SELABEL_OPT_PATH, .value = f1_path }, > + { .type = SELABEL_OPT_PATH, .value = f2_path }, > + { .type = SELABEL_OPT_PATH, .value = f3_path } > + }; > + > + hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts)); > + free(f1_path); > + free(f2_path); > + free(f3_path); > + > + if (!hnd) { > + log_errno("Unable to open file backend"); > + exit(2); > + } > + > + struct test_t tests[] = { > + { .path = "/", .context = "system_u:object_r:rootfs:s0" }, > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + { > + .path = "/subbed", > + .context = "system_u:object_r:test_subbed:s0" > + }, > + }; > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > + selabel_close(hnd); > +} > + > +void test_multiple_duplicate_validation(const char *basedir) > +{ > + struct selabel_handle *hnd; > + > + /* Two copies of f2.fc to provide duplicate specifications */ > + char *f2_path; > + asprintf(&f2_path, "%s/f2.fc", basedir); > + struct selinux_opt opts[] = { > + { .type = SELABEL_OPT_PATH, .value = f2_path }, > + { .type = SELABEL_OPT_PATH, .value = f2_path }, > + { .type = SELABEL_OPT_VALIDATE, .value = "1" } > + }; > + > + hnd = selabel_open(SELABEL_CTX_FILE, opts, ARRAY_SIZE(opts)); > + free(f2_path); > + > + if (!hnd) { > + log_errno("Unable to open file backend"); > + exit(2); > + } > + > + struct test_t tests[] = { > + { > + .path = "/base", > + .context = "system_u:object_r:test_base_t:s0" > + }, > + }; > + assertContextsMatch(hnd, __func__, tests, ARRAY_SIZE(tests)); > + > + selabel_close(hnd); > +} > + > +int main(int argc, char **argv) > +{ > + if (argc != 2) { > + log_err("basedir not provided"); > + exit(1); > + } > + > + test_multiple_no_validation(argv[1]); > + test_multiple_with_validation(argv[1]); > + test_multiple_duplicate_validation(argv[1]); > + test_multiple_with_extras(argv[1]); > + test_multiple_three_paths(argv[1]); > + > + return 0; > +} > -- > 2.55.0.229.g6434b31f56-goog >