Re: [PATCH] libselinux: selinux_restorecon: avoid triggering auto-mounts unnecessarily

Félix-Antoine Fortin <[email protected]> Wed, 29 Jul 2026 09:37:52 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CALAjPzH8EVi_f27kpONesg=KP7VqmShxJByXmg5-7j_+8zH-LA@mail.gmail.com>
Sorry for failing to provide an update earlier.

While implementing patch, I found out my initial analysis was wrong.
autofs entries do not have the seclabel attribute. This was not the
right cause for the automount.

What I found out was, there was a second mount on XFS with the same
path, and the seclabel attribute.
restorecon was therefore triggering automount while reading info on
another mount with the same path.

In summary: restorecon called statvfs() on the pathname of a hidden
seclabel bind mount listed in /proc/mounts.
Because that pathname is covered by a systemd autofs mount, the
pathname-based statvfs() resolves to
the topmost autofs mount and unexpectedly activates the corresponding
NFS filesystem.

If we would like to avoid restorecon triggering automount, the patch
solution I initially proposed has no effect.

On Wed, Jul 29, 2026 at 9:12 AM Stephen Smalley
<[email protected]> wrote:
>
> Félix-Antoine Fortin <[email protected]> reported
> the following bug report, quoted verbatim:
> ---<snip>---
> I encountered an unexpected side effect in restorecon: processing a
> single local file triggers unrelated automounts.
>
> Command (called during ipa-client-install):
>
>   $ restorecon /etc/krb5.conf.d/freeipa
>
> Although the target is under /etc, this causes all of the following
> NFS automounts to activate:
>
>   nfs-home.automount
>   nfs-project.automount
>   nfs-scratch.automount
>
> The journal identifies restorecon as the process that triggered each automount:
>
>   systemd[1]: nfs-home.automount: Got automount request for /nfs/home,
> triggered by 10521 (restorecon)
>   systemd[1]: Mounting /nfs/home...
>   nfsrahead[10530]: setting /nfs/home readahead to 128
>   systemd[1]: Mounted /nfs/home.
>   systemd[1]: nfs-project.automount: Got automount request for
> /nfs/project, triggered by 10521 (restorecon)
>   systemd[1]: Mounting /nfs/project...
>   nfsrahead[10533]: setting /nfs/project readahead to 128
>   systemd[1]: Mounted /nfs/project.
>   systemd[1]: nfs-scratch.automount: Got automount request for
> /nfs/scratch, triggered by 10521 (restorecon)
>   systemd[1]: Mounting /nfs/scratch...
>   nfsrahead[10536]: setting /nfs/scratch readahead to 128
>   systemd[1]: Mounted /nfs/scratch.
>
> The fstab entries are:
>
>   nfs_server:/home /nfs/home nfs4
> proto=tcp,nosuid,nolock,noatime,actimeo=3,nfsvers=4.2,seclabel,_netdev,x-systemd.automount,x-systemd.mount-timeout=30
> 0 0
>   nfs_server:/project /nfs/project nfs4
> proto=tcp,nosuid,nolock,noatime,actimeo=3,nfsvers=4.2,seclabel,_netdev,x-systemd.automount,x-systemd.mount-timeout=30
> 0 0
>   nfs_server:/scratch /nfs/scratch nfs4
> proto=tcp,nosuid,nolock,noatime,actimeo=3,nfsvers=4.2,seclabel,_netdev,x-systemd.automount,x-systemd.mount-timeout=30
> 0 0
>
> An strace shows restorecon probing these mountpoints:
>
>   $ strace -f restorecon /etc/krb5.conf/freeipa 2>&1 | grep /nfs
>
>   statfs("/nfs/home", {f_type=NFS_SUPER_MAGIC, f_bsize=1048576,
> f_blocks=10172, f_bfree=10069, f_bavail=10069, f_files=5240832,
> f_ffree=5240755, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=1048576,
> f_flags=ST_VALID|ST_NOSUID|ST_NOATIME}) = 0
>   statfs("/nfs/project", {f_type=NFS_SUPER_MAGIC, f_bsize=1048576,
> f_blocks=10172, f_bfree=10069, f_bavail=10069, f_files=5240832,
> f_ffree=5240816, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=1048576,
> f_flags=ST_VALID|ST_NOSUID|ST_NOATIME}) = 0
>   statfs("/nfs/scratch", {f_type=NFS_SUPER_MAGIC, f_bsize=1048576,
> f_blocks=10172, f_bfree=10069, f_bavail=10069, f_files=5240832,
> f_ffree=5240818, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=1048576,
> f_flags=ST_VALID|ST_NOSUID|ST_NOATIME}) = 0
>
> My understanding is:
>
> 1. When restorecon is called without ignore mount (-m) flag, it reads
> the mount table to construct the exclusion list for filesystems
> without SELinux labeling support.
> https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L146C14-L146C28
> 2. exclude_non_seclabel_mounts() calls file_system_count() for mounts that
> advertise the seclabel option. file_system_count() calls statvfs() on
> the mountpoint.
> https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L343
> https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L282
> 3. Calling statvfs() on a autofs mountpoint activates the underlying mount.
> 4. The resulting file count appears to be used only for progress reporting, but
> it is calculated even when neither progress nor mass-relabel reporting
> was requested.
>
> So a non-recursive restorecon operation on one local file can mount
> unrelated remote filesystems.
>
> I would expect restorecon to not activate unrelated systemd automount
> filesystems when progress reporting is not requested and the target
> does not traverse those filesystems.
>
> I think the best minimal patch would to skip calling file_system_count on
> mounts which type is autofs (mount_info[2]).
>
> ---<snip>---
>
> Fix this issue as the original reporter suggested.
>
> Reported-by: Félix-Antoine Fortin <[email protected]>
> Suggested-by: Félix-Antoine Fortin <[email protected]>
> Signed-off-by: Stephen Smalley <[email protected]>
> ---
>  libselinux/src/selinux_restorecon.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
> index 30f1b836..8bae1464 100644
> --- a/libselinux/src/selinux_restorecon.c
> +++ b/libselinux/src/selinux_restorecon.c
> @@ -301,6 +301,7 @@ static uint64_t exclude_non_seclabel_mounts(void)
>         uint64_t nfile = 0;
>         char *mount_info[4];
>         char *buf = NULL, *item, *saveptr;
> +       bool autofs = false;
>
>         /* Check to see if the kernel supports seclabel */
>         if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0)
> @@ -335,12 +336,19 @@ static uint64_t exclude_non_seclabel_mounts(void)
>                 /* Remove pre-existing entry */
>                 remove_exclude(mount_info[1]);
>
> +               autofs = !strcmp(mount_info[2], "autofs");
>                 saveptr = NULL;
>                 item = strtok_r(mount_info[3], ",", &saveptr);
>                 while (item != NULL) {
>                         if (strcmp(item, "seclabel") == 0) {
>                                 found = 1;
> -                               nfile += file_system_count(mount_info[1]);
> +                               /*
> +                                * Avoid triggering an auto-mount just to
> +                                * count files for progress tracking.
> +                                */
> +                               if (!autofs)
> +                                       nfile += file_system_count(
> +                                               mount_info[1]);
>                                 break;
>                         }
>                         item = strtok_r(NULL, ",", &saveptr);
> --
> 2.55.0
>