Re: [PATCH] libselinux: restorecon_xattr: reset dir_xattr_list on every call

Stephen Smalley <[email protected]>
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ4WCk27pfZQWbxiU=SK_VNbGWeeZuD0--Ws6iqT-GZRMQ@mail.gmail.com>
On Thu, Aug 6, 2026 at 3:41 PM Vit Mojzis <[email protected]> wrote:
>
> 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 <[email protected]>
>
> Signed-off-by: Vit Mojzis <[email protected]>

Acked-by: Stephen Smalley <[email protected]>

> ---
>
> Verified with an AddressSanitizer harness that calls
> selinux_restorecon_xattr() on one directory, frees the result,
> then calls it again on a second directory: reliably crashes
> with a heap-use-after-free (when compiled with -fsanitize=address)
> before this fix (reproduced on libselinux-3.10 and libselinux-3.11)
> and runs clean after rebuilding with this change applied.
>
> https://github.com/vmojzis/selinuxproject_selinux/blob/libselinux_AISLE/reproducers/RHEL-217622/reproduce.c
>
>
>
>  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 4233ba65..49b8ff7e 100644
> --- a/libselinux/src/selinux_restorecon.c
> +++ b/libselinux/src/selinux_restorecon.c
> @@ -1968,6 +1968,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
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.