* [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.