diff --git a/0004-libselinux-restorecon_xattr-reset-dir_xattr_list-on-.patch b/0004-libselinux-restorecon_xattr-reset-dir_xattr_list-on-.patch new file mode 100644 index 0000000..bbdd980 --- /dev/null +++ b/0004-libselinux-restorecon_xattr-reset-dir_xattr_list-on-.patch @@ -0,0 +1,59 @@ +From 638abdc5e0f40d1c8c4a8c0f0a2f9e37c4204125 Mon Sep 17 00:00:00 2001 +From: Vit Mojzis +Date: Thu, 6 Aug 2026 21:37:35 +0200 +Subject: [PATCH] libselinux: restorecon_xattr: reset dir_xattr_list on every + call + +selinux_restorecon_xattr(3) documents that "xattr_list must be set to +NULL before calling selinux_restorecon_xattr(3). The caller is +responsible for freeing the returned xattr_list entries." Commit +b5a23d7f30c1 ("libselinux: restorecon_xattr: clear dir_xattr_* after +freeing") reset the dir_xattr_list/dir_xattr_last statics after +freeing them on the internal error-cleanup path of the recursive +walk, but not on the normal success path. + +After a successful call, dir_xattr_list/dir_xattr_last are left +pointing at the just-returned, now caller-owned list. Once the caller +frees it as required and calls the function again, add_xattr_entry() +finds dir_xattr_list non-NULL and appends the next entry through +dir_xattr_last->next, a dangling pointer into memory the caller has +already freed - a use-after-free write. + +Reset both pointers to NULL at the top of every call instead of only +in the error path: by the API description, any list from a prior call +is not valid. + +Co-Authored-By: Claude Sonnet 5 + +Signed-off-by: Vit Mojzis +Acked-by: Stephen Smalley +--- + libselinux/src/selinux_restorecon.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c +index d48d5a83..da61f9de 100644 +--- a/libselinux/src/selinux_restorecon.c ++++ b/libselinux/src/selinux_restorecon.c +@@ -1963,6 +1963,18 @@ int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags, + if (!fc_sehandle) + return -1; + ++ /* ++ * The API contract requires the caller to have already freed any ++ * xattr_list returned by a previous call before calling again. ++ * Forget our own head/tail pointers to that now caller-owned memory ++ * here instead of carrying them over: leaving them set would make ++ * the next add_xattr_entry() append a new entry through ++ * dir_xattr_last, which would be dangling once the caller has freed ++ * it, resulting in a use-after-free. ++ */ ++ dir_xattr_list = NULL; ++ dir_xattr_last = NULL; ++ + if (lstat(pathname, &sb) < 0) { + if (errno == ENOENT) + return 0; +-- +2.53.0 + diff --git a/changelog b/changelog index 10a9584..9f1cc4b 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ +* Tue Aug 18 2026 Vit Mojzis - 3.11-2 +- restorecon_xattr: reset dir_xattr_list on every call (RHEL-217622) + * Wed Jul 01 2026 Petr Lautrbach - 3.11-1 - SELinux userspace 3.11 release diff --git a/libselinux.spec b/libselinux.spec index da2eb8e..3fee502 100644 --- a/libselinux.spec +++ b/libselinux.spec @@ -4,7 +4,7 @@ Summary: SELinux library and simple utilities Name: libselinux Version: 3.11 -Release: 1%{?dist} +Release: 2%{?dist} License: LicenseRef-Fedora-Public-Domain # https://github.com/SELinuxProject/selinux/wiki/Releases Source0: https://github.com/SELinuxProject/selinux/releases/download/%{version}/libselinux-%{version}.tar.gz @@ -22,6 +22,7 @@ Url: https://github.com/SELinuxProject/selinux/wiki Patch0001: 0001-Use-SHA-2-instead-of-SHA-1.patch Patch0002: 0002-Revert-libselinux-python-Use-system-Python3-build-mo.patch Patch0003: 0003-Revert-libselinux-update-pywrap-targets-for-modern-p.patch +Patch0004: 0004-libselinux-restorecon_xattr-reset-dir_xattr_list-on-.patch # Patch list end BuildRequires: gcc make BuildRequires: ruby-devel ruby libsepol-static >= %{libsepolver} swig pcre2-devel