All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] Add support for UTF-8 in file context labels
@ 2026-09-03 15:12 Petr Lautrbach
  2026-09-03 16:24 ` Stephen Smalley
  0 siblings, 1 reply; 6+ messages in thread
From: Petr Lautrbach @ 2026-09-03 15:12 UTC (permalink / raw)
  To: selinux; +Cc: Petr Lautrbach, Thiébaud Weksteen, Ondrej Mosnáček

- libselinux to be able to read UTF-8 spec entries
- libselinux to compile regexes with UTF strings
- initialize locales in setfiles, sefcontext_compile, semodule
- semanage_exec_prog to execute external programs with LC_CTYPE set to
the current environment
- disable UTF-8 support build time using DISABLE_UTF=y environment
variable

Fixes:
    # cat unicode.cil
    (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))

    # semodule -i unicode.cil
    /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
    /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
    /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
    libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
    semodule:  Failed!

    # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
    /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
    /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
    /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
    libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
    OSError: Error

Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
---

- dropped mbrtowc() and implemented utf8_char_len() based on rfc3629

 libselinux/Makefile                   |  4 ++
 libselinux/src/label_support.c        | 86 +++++++++++++++++++++++++++
 libselinux/src/regex.c                |  7 ++-
 libselinux/utils/sefcontext_compile.c |  5 ++
 libsemanage/src/semanage_store.c      | 12 +++-
 policycoreutils/semodule/semodule.c   |  5 ++
 policycoreutils/setfiles/setfiles.c   |  4 ++
 python/semanage/semanage-fcontext.8   |  8 +--
 8 files changed, 124 insertions(+), 7 deletions(-)

diff --git a/libselinux/Makefile b/libselinux/Makefile
index 48c0206a0a4b..aa8d053e359f 100644
--- a/libselinux/Makefile
+++ b/libselinux/Makefile
@@ -3,6 +3,7 @@ SUBDIRS = include src utils man
 PKG_CONFIG ?= pkg-config
 DISABLE_SETRANS ?= n
 DISABLE_RPM ?= n
+DISABLE_UTF ?= n
 ANDROID_HOST ?= n
 LABEL_BACKEND_ANDROID ?= n
 ifeq ($(ANDROID_HOST),y)
@@ -21,6 +22,9 @@ endif
 ifeq ($(DISABLE_X11),y)
 	DISABLE_FLAGS+= -DNO_X_BACKEND
 endif
+ifeq ($(DISABLE_UTF),y)
+	DISABLE_FLAGS+= -DNO_UTF
+endif
 export DISABLE_SETRANS DISABLE_RPM DISABLE_FLAGS ANDROID_HOST DISABLE_X11 LABEL_BACKEND_ANDROID
 
 USE_PCRE2 ?= y
diff --git a/libselinux/src/label_support.c b/libselinux/src/label_support.c
index cc5c279e88fa..e3c916c225e8 100644
--- a/libselinux/src/label_support.c
+++ b/libselinux/src/label_support.c
@@ -13,6 +13,79 @@
 #include <errno.h>
 #include "label_internal.h"
 
+#ifndef NO_UTF
+
+#define UTF8tail(x) (x >= 0x80 && x <= 0xBF)
+
+static size_t utf8_char_len(const unsigned char *s)
+{
+/* rfc3629
+ *  UTF8-octets = *( UTF8-char )
+ *  UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
+ *  UTF8-1      = %x00-7F
+ *  UTF8-2      = %xC2-DF UTF8-tail
+ *  UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
+ *                %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
+ *  UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
+ *                %xF4 %x80-8F 2( UTF8-tail )
+ *  UTF8-tail   = %x80-BF
+ */
+	if (s[0] == '\0')
+		return 0;
+
+	if (s[0] < 0x80)
+		return 1;
+
+	if (s[0] < 0xC2 || s[1] == '\0')
+		return 0;
+
+	if (s[0] <= 0xDF && UTF8tail(s[1]))
+		return 2;
+
+	if (s[2] == '\0')
+		return 0;
+
+	/* %xE0 %xA0-BF UTF8-tail */
+	if (s[0] == 0xE0 &&
+	    s[1] >= 0xA0 && s[1] <= 0xBF &&
+	    UTF8tail(s[2])
+	)
+		return 3;
+
+	/* %xE1-EC 2( UTF8-tail ) */
+	if (s[0] >= 0xE1 && s[0] <= 0xEC && UTF8tail(s[1]) && UTF8tail(s[2]))
+		return 3;
+
+	/* %xED %x80-9F UTF8-tail */
+	if (s[0] == 0xED && s[1] >= 0x80 && s[1] <= 0x9F && UTF8tail(s[2]))
+		return 3;
+
+	/* %xEE-EF 2( UTF8-tail ) */
+	if (s[0] >= 0xEE && s[0] <= 0xEF && UTF8tail(s[1]) && UTF8tail(s[2]))
+		return 3;
+
+	if (s[3] == '\0')
+		return 0;
+
+	/* %xF0 %x90-BF 2( UTF8-tail ) */
+	if (s[0] == 0xF0 && s[1] >= 0x90 && s[1] <= 0xBF &&
+	    UTF8tail(s[2]) && UTF8tail(s[3]))
+		return 4;
+
+	/* %xF1-F3 3( UTF8-tail ) */
+	if (s[0] >= 0xF1 && s[0] <= 0xF3 && UTF8tail(s[1]) && UTF8tail(s[2]) &&
+	    UTF8tail(s[3]))
+		return 4;
+
+	/* %xF4 %x80-8F 2( UTF8-tail ) */
+	if (s[0] == 0xF4 && s[1] >= 0x80 && s[1] <= 0x8F &&
+	    UTF8tail(s[2]) && UTF8tail(s[3]))
+		return 4;
+
+	return 0;
+}
+#endif
+
 /*
  * Read an entry from a spec file (e.g. file_contexts)
  * entry - Buffer to allocate for the entry.
@@ -36,6 +109,7 @@ static inline int read_spec_entry(char **entry, const char **ptr, size_t *len,
 	*len = 0;
 
 	while (!isspace((unsigned char)**ptr) && **ptr != '\0') {
+#ifdef NO_UTF
 		if (!isascii((unsigned char)**ptr)) {
 			errno = EINVAL;
 			*errbuf = "Non-ASCII characters found";
@@ -43,6 +117,18 @@ static inline int read_spec_entry(char **entry, const char **ptr, size_t *len,
 		}
 		(*ptr)++;
 		(*len)++;
+#else
+		size_t char_len = utf8_char_len((const unsigned char *)*ptr);
+
+		if (char_len == 0) {
+			errno = EINVAL;
+			*errbuf = "Invalid UTF-8 encoding";
+			return -1;
+		}
+
+		*ptr += char_len;
+		*len += char_len;
+#endif
 	}
 
 	if (*len) {
diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
index d6b5bf0252ba..f632a87275d4 100644
--- a/libselinux/src/regex.c
+++ b/libselinux/src/regex.c
@@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
 		return -1;
 
 	(*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
-					PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
+					PCRE2_ZERO_TERMINATED,
+#ifdef NO_UTF
+					PCRE2_DOTALL,
+#else
+					PCRE2_DOTALL | PCRE2_UTF,
+#endif
 					&errordata->error_code,
 					&errordata->error_offset, NULL);
 	if (!(*regex)->regex) {
diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
index e504b5084c8d..04c86053b2c8 100644
--- a/libselinux/utils/sefcontext_compile.c
+++ b/libselinux/utils/sefcontext_compile.c
@@ -1,6 +1,7 @@
 #include <endian.h>
 #include <errno.h>
 #include <getopt.h>
+#include <locale.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -564,6 +565,10 @@ int main(int argc, char *argv[])
 	struct spec_node *root = NULL;
 	struct sidtab stab = {};
 
+	/* Initialize locale for UTF-8 support */
+	setlocale(LC_ALL, "");
+
+
 	if (argc < 2)
 		usage(argv[0]);
 
diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c
index f6ca1afbacd4..9e478bd039dd 100644
--- a/libsemanage/src/semanage_store.c
+++ b/libsemanage/src/semanage_store.c
@@ -58,6 +58,7 @@ typedef struct dbase_policydb dbase_t;
 #include <sys/wait.h>
 #include <limits.h>
 #include <libgen.h>
+#include <locale.h>
 
 #include "debug.h"
 #include "utilities.h"
@@ -1493,6 +1494,11 @@ static int semanage_exec_prog(semanage_handle_t *sh, external_prog_t *e,
 	char **argv;
 	pid_t forkval;
 	int status = 0;
+	char *envp[] = { NULL, NULL};
+
+	/* we expect that locales are already initialized */
+	if (asprintf(&envp[0], "LC_CTYPE=%s", setlocale(LC_CTYPE, NULL)) == -1)
+		envp[0] = NULL;
 
 	argv = split_args(e->path, e->args, new_name, old_name);
 	if (argv == NULL) {
@@ -1506,11 +1512,13 @@ static int semanage_exec_prog(semanage_handle_t *sh, external_prog_t *e,
 	if (forkval == 0) {
 		/* child process.  file descriptors will be closed
 		 * because they were set as close-on-exec. */
-		execve(e->path, argv, NULL);
-		_exit(EXIT_FAILURE); /* if execve() failed */
+		execve(e->path, argv, envp);
+		_exit(EXIT_FAILURE);	/* if execve() failed */
 	}
 
 	free_argv(argv);
+	if (envp[0])
+		free(envp[0]);
 
 	if (forkval == -1) {
 		ERR(sh, "Error while forking process.");
diff --git a/policycoreutils/semodule/semodule.c b/policycoreutils/semodule/semodule.c
index 14e2f201f555..aac225c3c114 100644
--- a/policycoreutils/semodule/semodule.c
+++ b/policycoreutils/semodule/semodule.c
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <libgen.h>
 #include <limits.h>
+#include <locale.h>
 
 #include <sepol/cil/cil.h>
 #include <semanage/modules.h>
@@ -433,6 +434,10 @@ int main(int argc, char *argv[])
 	int i, commit = 0;
 	int result;
 	int status = EXIT_FAILURE;
+
+	/* Initialize locale for UTF-8 support */
+	setlocale(LC_ALL, "");
+
 	const char *genhomedirconargv[] = { "genhomedircon", "-B", "-n" };
 	create_signal_handlers();
 	if (strcmp(basename(argv[0]), "genhomedircon") == 0) {
diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c
index 7f1c0a31b7d4..1b135c4e9a79 100644
--- a/policycoreutils/setfiles/setfiles.c
+++ b/policycoreutils/setfiles/setfiles.c
@@ -8,6 +8,7 @@
 #include <regex.h>
 #include <sys/vfs.h>
 #include <libgen.h>
+#include <locale.h>
 #ifdef USE_AUDIT
 #include <libaudit.h>
 
@@ -179,6 +180,9 @@ int main(int argc, char **argv)
 	long unsigned skipped_errors;
 	long unsigned relabeled_files;
 
+	/* Initialize locale for UTF-8 support */
+	setlocale(LC_ALL, "");
+
 	/* Initialize variables */
 	memset(&r_opts, 0, sizeof(r_opts));
 	altpath = NULL;
diff --git a/python/semanage/semanage-fcontext.8 b/python/semanage/semanage-fcontext.8
index b037491e3394..8e1238fb373c 100644
--- a/python/semanage/semanage-fcontext.8
+++ b/python/semanage/semanage-fcontext.8
@@ -15,10 +15,10 @@ This command maps file paths using regular expressions to SELinux labels.
 
 FILE_SPEC may contain either a fully qualified path,
 or a Perl compatible regular expression (PCRE),
-describing fully qualified path(s). The only PCRE flag in use is PCRE2_DOTALL,
-which causes a wildcard '.' to match anything, including a new line.
-Strings representing paths are processed as bytes (as opposed to Unicode),
-meaning that non-ASCII characters are not matched by a single wildcard.
+describing fully qualified path(s). PCRE flags in use are PCRE2_DOTALL and
+PCRE2_UTF. PCRE2_DOTALL causes a wildcard '.' to match anything, including
+a new line. PCRE2_UTF treats pattern and subjects as UTF strings and can be
+disabled build time via `DISABLE_UTF=y` environment variable.
 
 Note, that file context definitions specified using 'semanage fcontext'
 (i.e. local file context modifications stored in file_contexts.local)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Add support for UTF-8 in file context labels
  2026-09-03 15:12 [PATCH v3] Add support for UTF-8 in file context labels Petr Lautrbach
@ 2026-09-03 16:24 ` Stephen Smalley
  2026-09-03 16:32   ` Stephen Smalley
  2026-09-04 10:00   ` Petr Lautrbach
  0 siblings, 2 replies; 6+ messages in thread
From: Stephen Smalley @ 2026-09-03 16:24 UTC (permalink / raw)
  To: Petr Lautrbach; +Cc: selinux, Thiébaud Weksteen, Ondrej Mosnáček

On Thu, Sep 3, 2026 at 12:00 PM Petr Lautrbach <lautrbach@redhat.com> wrote:
>
> - libselinux to be able to read UTF-8 spec entries
> - libselinux to compile regexes with UTF strings
> - initialize locales in setfiles, sefcontext_compile, semodule
> - semanage_exec_prog to execute external programs with LC_CTYPE set to
> the current environment
> - disable UTF-8 support build time using DISABLE_UTF=y environment
> variable
>
> Fixes:
>     # cat unicode.cil
>     (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))
>
>     # semodule -i unicode.cil
>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>     semodule:  Failed!
>
>     # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>     OSError: Error
>
> Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
> ---

> diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
> index d6b5bf0252ba..f632a87275d4 100644
> --- a/libselinux/src/regex.c
> +++ b/libselinux/src/regex.c
> @@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
>                 return -1;
>
>         (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
> -                                       PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
> +                                       PCRE2_ZERO_TERMINATED,
> +#ifdef NO_UTF
> +                                       PCRE2_DOTALL,
> +#else
> +                                       PCRE2_DOTALL | PCRE2_UTF,
> +#endif

This causes pcre2_match() to validate the subject as UTF-8 on every
call, which in addition to incurring
overhead on every file would also cause regex_match() to return REGEX_ERROR and
label_file.c:lookup_check_node() to fail the whole lookup with errno
ENOENT. Thus, restorecon on a file
whose name isn't UTF-8 will error out instead of falling back to
matching /.* and labeling accordingly.
If you add PCRE2_MATCH_INVALD_UTF to the flags, then pcre2_match()
will instead treat invalid bytes
as "cannot match anything" but can still match literals and character
classes and will return REGEX_NO_MATCH
instead of REGEX_ERROR.

> diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
> index e504b5084c8d..04c86053b2c8 100644
> --- a/libselinux/utils/sefcontext_compile.c
> +++ b/libselinux/utils/sefcontext_compile.c
> @@ -1,6 +1,7 @@
>  #include <endian.h>
>  #include <errno.h>
>  #include <getopt.h>
> +#include <locale.h>
>  #include <stdint.h>
>  #include <stdio.h>
>  #include <string.h>
> @@ -564,6 +565,10 @@ int main(int argc, char *argv[])
>         struct spec_node *root = NULL;
>         struct sidtab stab = {};
>
> +       /* Initialize locale for UTF-8 support */
> +       setlocale(LC_ALL, "");
> +
> +

This shouldn't be necessary with the dropping of mbrtowc() and using
utf8_char_len(); what still needs locale to be set? Ditto for the other
setlocale() changes.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Add support for UTF-8 in file context labels
  2026-09-03 16:24 ` Stephen Smalley
@ 2026-09-03 16:32   ` Stephen Smalley
  2026-09-04 10:00   ` Petr Lautrbach
  1 sibling, 0 replies; 6+ messages in thread
From: Stephen Smalley @ 2026-09-03 16:32 UTC (permalink / raw)
  To: Petr Lautrbach; +Cc: selinux, Thiébaud Weksteen, Ondrej Mosnáček

On Thu, Sep 3, 2026 at 12:24 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Thu, Sep 3, 2026 at 12:00 PM Petr Lautrbach <lautrbach@redhat.com> wrote:
> >
> > - libselinux to be able to read UTF-8 spec entries
> > - libselinux to compile regexes with UTF strings
> > - initialize locales in setfiles, sefcontext_compile, semodule
> > - semanage_exec_prog to execute external programs with LC_CTYPE set to
> > the current environment
> > - disable UTF-8 support build time using DISABLE_UTF=y environment
> > variable
> >
> > Fixes:
> >     # cat unicode.cil
> >     (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))
> >
> >     # semodule -i unicode.cil
> >     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
> >     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
> >     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
> >     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
> >     semodule:  Failed!
> >
> >     # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
> >     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
> >     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
> >     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
> >     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
> >     OSError: Error
> >
> > Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
> > ---
>
> > diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
> > index d6b5bf0252ba..f632a87275d4 100644
> > --- a/libselinux/src/regex.c
> > +++ b/libselinux/src/regex.c
> > @@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
> >                 return -1;
> >
> >         (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
> > -                                       PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
> > +                                       PCRE2_ZERO_TERMINATED,
> > +#ifdef NO_UTF
> > +                                       PCRE2_DOTALL,
> > +#else
> > +                                       PCRE2_DOTALL | PCRE2_UTF,
> > +#endif
>
> This causes pcre2_match() to validate the subject as UTF-8 on every
> call, which in addition to incurring
> overhead on every file would also cause regex_match() to return REGEX_ERROR and
> label_file.c:lookup_check_node() to fail the whole lookup with errno
> ENOENT. Thus, restorecon on a file
> whose name isn't UTF-8 will error out instead of falling back to
> matching /.* and labeling accordingly.
> If you add PCRE2_MATCH_INVALD_UTF to the flags, then pcre2_match()
> will instead treat invalid bytes
> as "cannot match anything" but can still match literals and character
> classes and will return REGEX_NO_MATCH
> instead of REGEX_ERROR.
>
> > diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
> > index e504b5084c8d..04c86053b2c8 100644
> > --- a/libselinux/utils/sefcontext_compile.c
> > +++ b/libselinux/utils/sefcontext_compile.c
> > @@ -1,6 +1,7 @@
> >  #include <endian.h>
> >  #include <errno.h>
> >  #include <getopt.h>
> > +#include <locale.h>
> >  #include <stdint.h>
> >  #include <stdio.h>
> >  #include <string.h>
> > @@ -564,6 +565,10 @@ int main(int argc, char *argv[])
> >         struct spec_node *root = NULL;
> >         struct sidtab stab = {};
> >
> > +       /* Initialize locale for UTF-8 support */
> > +       setlocale(LC_ALL, "");
> > +
> > +
>
> This shouldn't be necessary with the dropping of mbrtowc() and using
> utf8_char_len(); what still needs locale to be set? Ditto for the other
> setlocale() changes.

Also, you'll need to make format it - doesn't currently pass make check-format.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Add support for UTF-8 in file context labels
  2026-09-03 16:24 ` Stephen Smalley
  2026-09-03 16:32   ` Stephen Smalley
@ 2026-09-04 10:00   ` Petr Lautrbach
  2026-09-04 10:37     ` Petr Lautrbach
  1 sibling, 1 reply; 6+ messages in thread
From: Petr Lautrbach @ 2026-09-04 10:00 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: selinux, Thiébaud Weksteen, Ondrej Mosnáček

Stephen Smalley <stephen.smalley.work@gmail.com> writes:

> On Thu, Sep 3, 2026 at 12:00 PM Petr Lautrbach <lautrbach@redhat.com> wrote:
>>
>> - libselinux to be able to read UTF-8 spec entries
>> - libselinux to compile regexes with UTF strings
>> - initialize locales in setfiles, sefcontext_compile, semodule
>> - semanage_exec_prog to execute external programs with LC_CTYPE set to
>> the current environment
>> - disable UTF-8 support build time using DISABLE_UTF=y environment
>> variable
>>
>> Fixes:
>>     # cat unicode.cil
>>     (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))
>>
>>     # semodule -i unicode.cil
>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>>     semodule:  Failed!
>>
>>     # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>>     OSError: Error
>>
>> Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
>> ---
>
>> diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
>> index d6b5bf0252ba..f632a87275d4 100644
>> --- a/libselinux/src/regex.c
>> +++ b/libselinux/src/regex.c
>> @@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
>>                 return -1;
>>
>>         (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
>> -                                       PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
>> +                                       PCRE2_ZERO_TERMINATED,
>> +#ifdef NO_UTF
>> +                                       PCRE2_DOTALL,
>> +#else
>> +                                       PCRE2_DOTALL | PCRE2_UTF,
>> +#endif
>
> This causes pcre2_match() to validate the subject as UTF-8 on every
> call, which in addition to incurring
> overhead on every file
>

Would you prefer NO_UTF to be default so only distributions which
enabled this would be affected?


> would also cause regex_match() to return REGEX_ERROR and
> label_file.c:lookup_check_node() to fail the whole lookup with errno
> ENOENT. Thus, restorecon on a file
> whose name isn't UTF-8 will error out instead of falling back to
> matching /.* and labeling accordingly.
> If you add PCRE2_MATCH_INVALD_UTF to the flags, then pcre2_match()
> will instead treat invalid bytes
> as "cannot match anything" but can still match literals and character
> classes and will return REGEX_NO_MATCH
> instead of REGEX_ERROR.

I was not able to get it working correctly using filename with invalid
utf8 symbol even with PCRE2_MATCH_INVALID_UTF.

PCRE2_DOTALL without PCRE2_UTF matches anything on byte level for
'.'. The problematic are wildcards. e.g. '/opt/žluťoučký+'

I'm working in a patch which without PCRE2_UTF but which would allow to
use '(*UTF)' sequence directly in file spec:

# semanage fcontext -a -t etc_t '/opt/žluťoučkž+'
# matchpathcon /opt/žluťoučkžžž
/opt/žluťoučkžžž        system_u:object_r:usr_t:s0

vs

# semanage fcontext -a -t etc_t '(*UTF)/opt/žluťoučkž+'
# matchpathcon /opt/žluťoučkžžž
/opt/žluťoučkžžž        system_u:object_r:etc_t:s0


>> diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
>> index e504b5084c8d..04c86053b2c8 100644
>> --- a/libselinux/utils/sefcontext_compile.c
>> +++ b/libselinux/utils/sefcontext_compile.c
>> @@ -1,6 +1,7 @@
>>  #include <endian.h>
>>  #include <errno.h>
>>  #include <getopt.h>
>> +#include <locale.h>
>>  #include <stdint.h>
>>  #include <stdio.h>
>>  #include <string.h>
>> @@ -564,6 +565,10 @@ int main(int argc, char *argv[])
>>         struct spec_node *root = NULL;
>>         struct sidtab stab = {};
>>
>> +       /* Initialize locale for UTF-8 support */
>> +       setlocale(LC_ALL, "");
>> +
>> +
>
> This shouldn't be necessary with the dropping of mbrtowc() and using
> utf8_char_len(); what still needs locale to be set? Ditto for the other
> setlocale() changes.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Add support for UTF-8 in file context labels
  2026-09-04 10:00   ` Petr Lautrbach
@ 2026-09-04 10:37     ` Petr Lautrbach
  2026-09-04 12:06       ` Stephen Smalley
  0 siblings, 1 reply; 6+ messages in thread
From: Petr Lautrbach @ 2026-09-04 10:37 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: selinux, Thiébaud Weksteen, Ondrej Mosnáček

Petr Lautrbach <lautrbach@redhat.com> writes:

> Stephen Smalley <stephen.smalley.work@gmail.com> writes:
>
>> On Thu, Sep 3, 2026 at 12:00 PM Petr Lautrbach <lautrbach@redhat.com> wrote:
>>>
>>> - libselinux to be able to read UTF-8 spec entries
>>> - libselinux to compile regexes with UTF strings
>>> - initialize locales in setfiles, sefcontext_compile, semodule
>>> - semanage_exec_prog to execute external programs with LC_CTYPE set to
>>> the current environment
>>> - disable UTF-8 support build time using DISABLE_UTF=y environment
>>> variable
>>>
>>> Fixes:
>>>     # cat unicode.cil
>>>     (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))
>>>
>>>     # semodule -i unicode.cil
>>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
>>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>>>     semodule:  Failed!
>>>
>>>     # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
>>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
>>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
>>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
>>>     OSError: Error
>>>
>>> Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
>>> ---
>>
>>> diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
>>> index d6b5bf0252ba..f632a87275d4 100644
>>> --- a/libselinux/src/regex.c
>>> +++ b/libselinux/src/regex.c
>>> @@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
>>>                 return -1;
>>>
>>>         (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
>>> -                                       PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
>>> +                                       PCRE2_ZERO_TERMINATED,
>>> +#ifdef NO_UTF
>>> +                                       PCRE2_DOTALL,
>>> +#else
>>> +                                       PCRE2_DOTALL | PCRE2_UTF,
>>> +#endif
>>
>> This causes pcre2_match() to validate the subject as UTF-8 on every
>> call, which in addition to incurring
>> overhead on every file
>>
>
> Would you prefer NO_UTF to be default so only distributions which
> enabled this would be affected?
>
>
>> would also cause regex_match() to return REGEX_ERROR and
>> label_file.c:lookup_check_node() to fail the whole lookup with errno
>> ENOENT. Thus, restorecon on a file
>> whose name isn't UTF-8 will error out instead of falling back to
>> matching /.* and labeling accordingly.
>> If you add PCRE2_MATCH_INVALD_UTF to the flags, then pcre2_match()
>> will instead treat invalid bytes
>> as "cannot match anything" but can still match literals and character
>> classes and will return REGEX_NO_MATCH
>> instead of REGEX_ERROR.
>
> I was not able to get it working correctly using filename with invalid
> utf8 symbol even with PCRE2_MATCH_INVALID_UTF.
>
> PCRE2_DOTALL without PCRE2_UTF matches anything on byte level for
> '.'. The problematic are wildcards. e.g. '/opt/žluťoučký+'
>
> I'm working in a patch which without PCRE2_UTF but which would allow to
> use '(*UTF)' sequence directly in file spec:
>
> # semanage fcontext -a -t etc_t '/opt/žluťoučkž+'
> # matchpathcon /opt/žluťoučkžžž
> /opt/žluťoučkžžž        system_u:object_r:usr_t:s0
>
> vs
>
> # semanage fcontext -a -t etc_t '(*UTF)/opt/žluťoučkž+'
> # matchpathcon /opt/žluťoučkžžž
> /opt/žluťoučkžžž        system_u:object_r:etc_t:s0
>

Another option would be to document that in this case, it's necessary to
use '( )'

# semanage fcontext -a -t etc_t '/opt/žluťoučk(ž)+'

# matchpathcon /opt/žluťoučkžžž
/opt/žluťoučkžžž        system_u:object_r:etc_t:s0





>>> diff --git a/libselinux/utils/sefcontext_compile.c b/libselinux/utils/sefcontext_compile.c
>>> index e504b5084c8d..04c86053b2c8 100644
>>> --- a/libselinux/utils/sefcontext_compile.c
>>> +++ b/libselinux/utils/sefcontext_compile.c
>>> @@ -1,6 +1,7 @@
>>>  #include <endian.h>
>>>  #include <errno.h>
>>>  #include <getopt.h>
>>> +#include <locale.h>
>>>  #include <stdint.h>
>>>  #include <stdio.h>
>>>  #include <string.h>
>>> @@ -564,6 +565,10 @@ int main(int argc, char *argv[])
>>>         struct spec_node *root = NULL;
>>>         struct sidtab stab = {};
>>>
>>> +       /* Initialize locale for UTF-8 support */
>>> +       setlocale(LC_ALL, "");
>>> +
>>> +
>>
>> This shouldn't be necessary with the dropping of mbrtowc() and using
>> utf8_char_len(); what still needs locale to be set? Ditto for the other
>> setlocale() changes.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3] Add support for UTF-8 in file context labels
  2026-09-04 10:37     ` Petr Lautrbach
@ 2026-09-04 12:06       ` Stephen Smalley
  0 siblings, 0 replies; 6+ messages in thread
From: Stephen Smalley @ 2026-09-04 12:06 UTC (permalink / raw)
  To: Petr Lautrbach; +Cc: selinux, Thiébaud Weksteen, Ondrej Mosnáček

On Fri, Sep 4, 2026 at 6:37 AM Petr Lautrbach <lautrbach@redhat.com> wrote:
>
> Petr Lautrbach <lautrbach@redhat.com> writes:
>
> > Stephen Smalley <stephen.smalley.work@gmail.com> writes:
> >
> >> On Thu, Sep 3, 2026 at 12:00 PM Petr Lautrbach <lautrbach@redhat.com> wrote:
> >>>
> >>> - libselinux to be able to read UTF-8 spec entries
> >>> - libselinux to compile regexes with UTF strings
> >>> - initialize locales in setfiles, sefcontext_compile, semodule
> >>> - semanage_exec_prog to execute external programs with LC_CTYPE set to
> >>> the current environment
> >>> - disable UTF-8 support build time using DISABLE_UTF=y environment
> >>> variable
> >>>
> >>> Fixes:
> >>>     # cat unicode.cil
> >>>     (filecon "/opt/žluťoučký(/.*)?" any (system_u object_r user_home_t ((s0) (s0))))
> >>>
> >>>     # semodule -i unicode.cil
> >>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
> >>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts:  line 2145 error due to: Non-ASCII characters found
> >>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
> >>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
> >>>     semodule:  Failed!
> >>>
> >>>     # semanage fcontext --add -t user_home_t "/opt/žluťoučký(/.*)?"
> >>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
> >>>     /sbin/setfiles: /var/lib/selinux/final/targeted/contexts/files/file_contexts.local:  line 4 error due to: Non-ASCII characters found
> >>>     /var/lib/selinux/final/targeted/contexts/files/file_contexts: Invalid argument
> >>>     libsemanage.semanage_validate_and_compile_fcontexts: setfiles returned error code 1.
> >>>     OSError: Error
> >>>
> >>> Signed-off-by: Petr Lautrbach <lautrbach@redhat.com>
> >>> ---
> >>
> >>> diff --git a/libselinux/src/regex.c b/libselinux/src/regex.c
> >>> index d6b5bf0252ba..f632a87275d4 100644
> >>> --- a/libselinux/src/regex.c
> >>> +++ b/libselinux/src/regex.c
> >>> @@ -96,7 +96,12 @@ int regex_prepare_data(struct regex_data **regex, char const *pattern_string,
> >>>                 return -1;
> >>>
> >>>         (*regex)->regex = pcre2_compile((PCRE2_SPTR)pattern_string,
> >>> -                                       PCRE2_ZERO_TERMINATED, PCRE2_DOTALL,
> >>> +                                       PCRE2_ZERO_TERMINATED,
> >>> +#ifdef NO_UTF
> >>> +                                       PCRE2_DOTALL,
> >>> +#else
> >>> +                                       PCRE2_DOTALL | PCRE2_UTF,
> >>> +#endif
> >>
> >> This causes pcre2_match() to validate the subject as UTF-8 on every
> >> call, which in addition to incurring
> >> overhead on every file
> >>
> >
> > Would you prefer NO_UTF to be default so only distributions which
> > enabled this would be affected?
> >
> >
> >> would also cause regex_match() to return REGEX_ERROR and
> >> label_file.c:lookup_check_node() to fail the whole lookup with errno
> >> ENOENT. Thus, restorecon on a file
> >> whose name isn't UTF-8 will error out instead of falling back to
> >> matching /.* and labeling accordingly.
> >> If you add PCRE2_MATCH_INVALD_UTF to the flags, then pcre2_match()
> >> will instead treat invalid bytes
> >> as "cannot match anything" but can still match literals and character
> >> classes and will return REGEX_NO_MATCH
> >> instead of REGEX_ERROR.
> >
> > I was not able to get it working correctly using filename with invalid
> > utf8 symbol even with PCRE2_MATCH_INVALID_UTF.
> >
> > PCRE2_DOTALL without PCRE2_UTF matches anything on byte level for
> > '.'. The problematic are wildcards. e.g. '/opt/žluťoučký+'
> >
> > I'm working in a patch which without PCRE2_UTF but which would allow to
> > use '(*UTF)' sequence directly in file spec:
> >
> > # semanage fcontext -a -t etc_t '/opt/žluťoučkž+'
> > # matchpathcon /opt/žluťoučkžžž
> > /opt/žluťoučkžžž        system_u:object_r:usr_t:s0
> >
> > vs
> >
> > # semanage fcontext -a -t etc_t '(*UTF)/opt/žluťoučkž+'
> > # matchpathcon /opt/žluťoučkžžž
> > /opt/žluťoučkžžž        system_u:object_r:etc_t:s0
> >
>
> Another option would be to document that in this case, it's necessary to
> use '( )'
>
> # semanage fcontext -a -t etc_t '/opt/žluťoučk(ž)+'
>
> # matchpathcon /opt/žluťoučkžžž
> /opt/žluťoučkžžž        system_u:object_r:etc_t:s0

That seems fine to me and cleaner than the first option.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-04 12:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 15:12 [PATCH v3] Add support for UTF-8 in file context labels Petr Lautrbach
2026-09-03 16:24 ` Stephen Smalley
2026-09-03 16:32   ` Stephen Smalley
2026-09-04 10:00   ` Petr Lautrbach
2026-09-04 10:37     ` Petr Lautrbach
2026-09-04 12:06       ` Stephen Smalley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.