* Re: [PATCH v3] seunshare: fix the frail tmpdir cleanup
[not found] <CAEjxPJ6QGZcAO zjdajOFTaDc tuh56Vao3niO37udEr=UfAgg@mail.gmail.com>
@ 2025-08-01 18:21 ` Rahul Sandhu
2025-08-01 18:29 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Rahul Sandhu @ 2025-08-01 18:21 UTC (permalink / raw)
To: stephen.smalley.work; +Cc: nvraxn, selinux
> I would think the idiomatic C way of doing this would be to use fts(3)
> or nftw(3).
> setfiles/restorecon originally used nftw(3) but later switched to
> fts(3) for reasons I don't recall.
It's worth noting that fts (3) is not actually mandated by POSIX from a
portability perspective, however we do already depend on it for other
parts of the userspace[1] (as you mentioned), and shims exist for other
libcs, for example musl[2], so I don't think this is a huge deal. I
can't speak for setfiles and restorecon, but nftw's api is pretty crap
compared to fts, so, like them, I'd rather use fts for this than nftw
even though nftw is part of posix[3].
I think dirfds are fine to use here, however I'm happy to send a v2
using fts if you wish - would you like me to do so?
[1] https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L17
[2] https://github.com/void-linux/musl-fts
[3] https://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3] seunshare: fix the frail tmpdir cleanup
2025-08-01 18:21 ` [PATCH v3] seunshare: fix the frail tmpdir cleanup Rahul Sandhu
@ 2025-08-01 18:29 ` Stephen Smalley
2025-08-14 17:47 ` James Carter
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-08-01 18:29 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: selinux
On Fri, Aug 1, 2025 at 2:21 PM Rahul Sandhu <nvraxn@gmail.com> wrote:
>
> > I would think the idiomatic C way of doing this would be to use fts(3)
> > or nftw(3).
> > setfiles/restorecon originally used nftw(3) but later switched to
> > fts(3) for reasons I don't recall.
>
> It's worth noting that fts (3) is not actually mandated by POSIX from a
> portability perspective, however we do already depend on it for other
> parts of the userspace[1] (as you mentioned), and shims exist for other
> libcs, for example musl[2], so I don't think this is a huge deal. I
> can't speak for setfiles and restorecon, but nftw's api is pretty crap
> compared to fts, so, like them, I'd rather use fts for this than nftw
> even though nftw is part of posix[3].
>
> I think dirfds are fine to use here, however I'm happy to send a v2
> using fts if you wish - would you like me to do so?
Maybe wait and see what other SELinux userspace maintainers think - I
am just one opinion here.
>
> [1] https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L17
> [2] https://github.com/void-linux/musl-fts
> [3] https://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3] seunshare: fix the frail tmpdir cleanup
2025-08-01 18:29 ` Stephen Smalley
@ 2025-08-14 17:47 ` James Carter
2025-09-25 5:38 ` [PATCH v4] " Rahul Sandhu
0 siblings, 1 reply; 11+ messages in thread
From: James Carter @ 2025-08-14 17:47 UTC (permalink / raw)
To: Stephen Smalley; +Cc: Rahul Sandhu, selinux
On Fri, Aug 1, 2025 at 2:29 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Fri, Aug 1, 2025 at 2:21 PM Rahul Sandhu <nvraxn@gmail.com> wrote:
> >
> > > I would think the idiomatic C way of doing this would be to use fts(3)
> > > or nftw(3).
> > > setfiles/restorecon originally used nftw(3) but later switched to
> > > fts(3) for reasons I don't recall.
> >
> > It's worth noting that fts (3) is not actually mandated by POSIX from a
> > portability perspective, however we do already depend on it for other
> > parts of the userspace[1] (as you mentioned), and shims exist for other
> > libcs, for example musl[2], so I don't think this is a huge deal. I
> > can't speak for setfiles and restorecon, but nftw's api is pretty crap
> > compared to fts, so, like them, I'd rather use fts for this than nftw
> > even though nftw is part of posix[3].
> >
> > I think dirfds are fine to use here, however I'm happy to send a v2
> > using fts if you wish - would you like me to do so?
>
> Maybe wait and see what other SELinux userspace maintainers think - I
> am just one opinion here.
>
Since we use both fts and dirfds in other places, I am ok with either
one. I don't think you need to redo the patch just to use fts.
Jim
> >
> > [1] https://github.com/SELinuxProject/selinux/blob/main/libselinux/src/selinux_restorecon.c#L17
> > [2] https://github.com/void-linux/musl-fts
> > [3] https://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4] seunshare: fix the frail tmpdir cleanup
2025-08-14 17:47 ` James Carter
@ 2025-09-25 5:38 ` Rahul Sandhu
2025-10-06 15:57 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Rahul Sandhu @ 2025-09-25 5:38 UTC (permalink / raw)
To: jwcart2; +Cc: nvraxn, selinux, stephen.smalley.work
For some reason, rm is invoked via system (3) to cleanup the runtime
temp directory. This really isn't all that robust, *especially* given
that seunshare is supposed to be a security boundary. Instead do this
using libc, the API designed to be used within C programs.
Also, don't bother trying to delete the contents of the tmpdir and then
trying to delete the parent directory with rmdir later - for some...
undocumented reason currently we attempt to delete the contents of the
dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
This doesn't really make all that much sense as far as I can tell. We
should be the only ones using the tmpdir, so we may as well just delete
the entire thing using the rm_rf () function with elevated permissions.
Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
---
sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 65 insertions(+), 12 deletions(-)
v2: don't use else after return
v3: don't follow symlinks in rm_rf (). This is pretty important as we
we are operating on an untrusted directory, which may have symlinks
pointed to privileged content. However, as we only really need to
operate on the contents of the tmpdir, we can ignore symlinks.
v4: fix spelling in commit message
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 106f625f..c46db90f 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -403,6 +403,66 @@ err:
return rc;
}
+/*
+ * Recursively delete a directory.
+ * SAFTEY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
+ * As a result, this function can be run safely on a directory owned by
+ * a non-root user: symbolic links to root paths (such as /root) will
+ * not be followed.
+ */
+static bool rm_rf(int targetfd, const char *path) {
+ struct stat statbuf;
+
+ if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (errno == ENOENT) {
+ return true;
+ }
+ perror("fstatat");
+ return false;
+ }
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (newfd < 0) {
+ perror("openat");
+ return false;
+ }
+
+ DIR *dir = fdopendir(newfd);
+ if (!dir) {
+ perror("fdopendir");
+ close(newfd);
+ return false;
+ }
+
+ struct dirent *entry;
+ int rc = true;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+
+ if (rm_rf(dirfd(dir), entry->d_name) != 0) {
+ rc = false;
+ }
+ }
+
+ closedir(dir);
+
+ if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
+ perror("unlinkat");
+ rc = false;
+ }
+
+ return rc;
+ }
+ if (unlinkat(targetfd, path, 0) < 0) {
+ perror("unlinkat");
+ return false;
+ }
+ return true;
+}
+
/**
* Clean up runtime temporary directory. Returns 0 if no problem was detected,
* >0 if some error was detected, but errors here are treated as non-fatal and
@@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
free(cmdbuf); cmdbuf = NULL;
}
- /* remove files from the runtime temporary directory */
- if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- cmdbuf = NULL;
+ if ((uid_t)setfsuid(0) != 0) {
+ /* setfsuid does not return error, but this check makes code checkers happy */
rc++;
}
- /* this may fail if there's root-owned file left in the runtime tmpdir */
- if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
- free(cmdbuf); cmdbuf = NULL;
- /* remove runtime temporary directory */
- if ((uid_t)setfsuid(0) != 0) {
- /* setfsuid does not return error, but this check makes code checkers happy */
+ /* Recursively remove the runtime temp directory. */
+ if (!rm_rf(AT_FDCWD, tmpdir)) {
+ fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
rc++;
}
- if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
- fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
rc++;
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v4] seunshare: fix the frail tmpdir cleanup
2025-09-25 5:38 ` [PATCH v4] " Rahul Sandhu
@ 2025-10-06 15:57 ` Stephen Smalley
2025-10-06 16:31 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-10-06 15:57 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: jwcart2, selinux
On Thu, Sep 25, 2025 at 1:38 AM Rahul Sandhu <nvraxn@gmail.com> wrote:
>
> For some reason, rm is invoked via system (3) to cleanup the runtime
> temp directory. This really isn't all that robust, *especially* given
> that seunshare is supposed to be a security boundary. Instead do this
> using libc, the API designed to be used within C programs.
>
> Also, don't bother trying to delete the contents of the tmpdir and then
> trying to delete the parent directory with rmdir later - for some...
> undocumented reason currently we attempt to delete the contents of the
> dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
I think this was for security/safety reasons, to prevent unlinking any
files linked from the tmpdir that weren't actually owned by the user.
However, in theory, your rewrite makes that obsolete.
>
> This doesn't really make all that much sense as far as I can tell. We
> should be the only ones using the tmpdir, so we may as well just delete
> the entire thing using the rm_rf () function with elevated permissions.
>
> Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
> ---
> sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 65 insertions(+), 12 deletions(-)
>
> v2: don't use else after return
> v3: don't follow symlinks in rm_rf (). This is pretty important as we
> we are operating on an untrusted directory, which may have symlinks
> pointed to privileged content. However, as we only really need to
> operate on the contents of the tmpdir, we can ignore symlinks.
> v4: fix spelling in commit message
>
> diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> index 106f625f..c46db90f 100644
> --- a/sandbox/seunshare.c
> +++ b/sandbox/seunshare.c
> @@ -403,6 +403,66 @@ err:
> return rc;
> }
>
> +/*
> + * Recursively delete a directory.
> + * SAFTEY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
Spelling here too: SAFETY
> + * As a result, this function can be run safely on a directory owned by
> + * a non-root user: symbolic links to root paths (such as /root) will
> + * not be followed.
> + */
> +static bool rm_rf(int targetfd, const char *path) {
> + struct stat statbuf;
> +
> + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> + if (errno == ENOENT) {
> + return true;
> + }
> + perror("fstatat");
> + return false;
> + }
> +
> + if (S_ISDIR(statbuf.st_mode)) {
> + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> + if (newfd < 0) {
> + perror("openat");
> + return false;
> + }
> +
> + DIR *dir = fdopendir(newfd);
> + if (!dir) {
> + perror("fdopendir");
> + close(newfd);
> + return false;
> + }
> +
> + struct dirent *entry;
> + int rc = true;
> + while ((entry = readdir(dir)) != NULL) {
> + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> + continue;
> + }
> +
> + if (rm_rf(dirfd(dir), entry->d_name) != 0) {
> + rc = false;
> + }
> + }
> +
> + closedir(dir);
> +
> + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> + perror("unlinkat");
> + rc = false;
> + }
> +
> + return rc;
> + }
> + if (unlinkat(targetfd, path, 0) < 0) {
> + perror("unlinkat");
> + return false;
> + }
> + return true;
> +}
> +
> /**
> * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> * >0 if some error was detected, but errors here are treated as non-fatal and
> @@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> free(cmdbuf); cmdbuf = NULL;
> }
>
> - /* remove files from the runtime temporary directory */
> - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> - fprintf(stderr, _("Out of memory\n"));
> - cmdbuf = NULL;
> + if ((uid_t)setfsuid(0) != 0) {
> + /* setfsuid does not return error, but this check makes code checkers happy */
> rc++;
> }
> - /* this may fail if there's root-owned file left in the runtime tmpdir */
> - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> - free(cmdbuf); cmdbuf = NULL;
>
> - /* remove runtime temporary directory */
> - if ((uid_t)setfsuid(0) != 0) {
> - /* setfsuid does not return error, but this check makes code checkers happy */
> + /* Recursively remove the runtime temp directory. */
> + if (!rm_rf(AT_FDCWD, tmpdir)) {
> + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> rc++;
> }
>
> - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> rc++;
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4] seunshare: fix the frail tmpdir cleanup
2025-10-06 15:57 ` Stephen Smalley
@ 2025-10-06 16:31 ` Stephen Smalley
2025-10-06 18:40 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-10-06 16:31 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: jwcart2, selinux
On Mon, Oct 6, 2025 at 11:57 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Thu, Sep 25, 2025 at 1:38 AM Rahul Sandhu <nvraxn@gmail.com> wrote:
> >
> > For some reason, rm is invoked via system (3) to cleanup the runtime
> > temp directory. This really isn't all that robust, *especially* given
> > that seunshare is supposed to be a security boundary. Instead do this
> > using libc, the API designed to be used within C programs.
> >
> > Also, don't bother trying to delete the contents of the tmpdir and then
> > trying to delete the parent directory with rmdir later - for some...
> > undocumented reason currently we attempt to delete the contents of the
> > dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
>
> I think this was for security/safety reasons, to prevent unlinking any
> files linked from the tmpdir that weren't actually owned by the user.
> However, in theory, your rewrite makes that obsolete.
>
> >
> > This doesn't really make all that much sense as far as I can tell. We
> > should be the only ones using the tmpdir, so we may as well just delete
> > the entire thing using the rm_rf () function with elevated permissions.
> >
> > Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
> > ---
> > sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
> > 1 file changed, 65 insertions(+), 12 deletions(-)
> >
> > v2: don't use else after return
> > v3: don't follow symlinks in rm_rf (). This is pretty important as we
> > we are operating on an untrusted directory, which may have symlinks
> > pointed to privileged content. However, as we only really need to
> > operate on the contents of the tmpdir, we can ignore symlinks.
> > v4: fix spelling in commit message
> >
> > diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> > index 106f625f..c46db90f 100644
> > --- a/sandbox/seunshare.c
> > +++ b/sandbox/seunshare.c
> > @@ -403,6 +403,66 @@ err:
> > return rc;
> > }
> >
> > +/*
> > + * Recursively delete a directory.
> > + * SAFTEY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
>
> Spelling here too: SAFETY
>
> > + * As a result, this function can be run safely on a directory owned by
> > + * a non-root user: symbolic links to root paths (such as /root) will
> > + * not be followed.
> > + */
> > +static bool rm_rf(int targetfd, const char *path) {
> > + struct stat statbuf;
> > +
> > + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> > + if (errno == ENOENT) {
> > + return true;
> > + }
> > + perror("fstatat");
> > + return false;
> > + }
> > +
> > + if (S_ISDIR(statbuf.st_mode)) {
> > + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> > + if (newfd < 0) {
> > + perror("openat");
> > + return false;
> > + }
> > +
> > + DIR *dir = fdopendir(newfd);
> > + if (!dir) {
> > + perror("fdopendir");
> > + close(newfd);
> > + return false;
> > + }
> > +
> > + struct dirent *entry;
> > + int rc = true;
> > + while ((entry = readdir(dir)) != NULL) {
> > + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> > + continue;
> > + }
> > +
> > + if (rm_rf(dirfd(dir), entry->d_name) != 0) {
> > + rc = false;
> > + }
> > + }
> > +
> > + closedir(dir);
> > +
> > + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> > + perror("unlinkat");
> > + rc = false;
> > + }
> > +
> > + return rc;
> > + }
> > + if (unlinkat(targetfd, path, 0) < 0) {
> > + perror("unlinkat");
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > /**
> > * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> > * >0 if some error was detected, but errors here are treated as non-fatal and
> > @@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> > free(cmdbuf); cmdbuf = NULL;
> > }
> >
> > - /* remove files from the runtime temporary directory */
> > - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> > - fprintf(stderr, _("Out of memory\n"));
> > - cmdbuf = NULL;
> > + if ((uid_t)setfsuid(0) != 0) {
> > + /* setfsuid does not return error, but this check makes code checkers happy */
I'm a little confused by this - further down there is a
setfsuid(pwd->pw_uid) call which seems to suggest that we are already
running as uid-0 at this point? If so, why do we need this setfsuid(0)
call?
> > rc++;
> > }
> > - /* this may fail if there's root-owned file left in the runtime tmpdir */
> > - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> > - free(cmdbuf); cmdbuf = NULL;
> >
> > - /* remove runtime temporary directory */
> > - if ((uid_t)setfsuid(0) != 0) {
> > - /* setfsuid does not return error, but this check makes code checkers happy */
> > + /* Recursively remove the runtime temp directory. */
> > + if (!rm_rf(AT_FDCWD, tmpdir)) {
> > + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> > rc++;
> > }
> >
> > - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> > - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> > if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> > fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> > rc++;
> > --
> > 2.50.1
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4] seunshare: fix the frail tmpdir cleanup
2025-10-06 16:31 ` Stephen Smalley
@ 2025-10-06 18:40 ` Stephen Smalley
2025-10-07 9:29 ` [PATCH v5] " Rahul Sandhu
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-10-06 18:40 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: jwcart2, selinux
On Mon, Oct 6, 2025 at 12:31 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Mon, Oct 6, 2025 at 11:57 AM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
> >
> > On Thu, Sep 25, 2025 at 1:38 AM Rahul Sandhu <nvraxn@gmail.com> wrote:
> > >
> > > For some reason, rm is invoked via system (3) to cleanup the runtime
> > > temp directory. This really isn't all that robust, *especially* given
> > > that seunshare is supposed to be a security boundary. Instead do this
> > > using libc, the API designed to be used within C programs.
> > >
> > > Also, don't bother trying to delete the contents of the tmpdir and then
> > > trying to delete the parent directory with rmdir later - for some...
> > > undocumented reason currently we attempt to delete the contents of the
> > > dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
> >
> > I think this was for security/safety reasons, to prevent unlinking any
> > files linked from the tmpdir that weren't actually owned by the user.
> > However, in theory, your rewrite makes that obsolete.
> >
> > >
> > > This doesn't really make all that much sense as far as I can tell. We
> > > should be the only ones using the tmpdir, so we may as well just delete
> > > the entire thing using the rm_rf () function with elevated permissions.
> > >
> > > Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
> > > ---
> > > sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
> > > 1 file changed, 65 insertions(+), 12 deletions(-)
> > >
> > > v2: don't use else after return
> > > v3: don't follow symlinks in rm_rf (). This is pretty important as we
> > > we are operating on an untrusted directory, which may have symlinks
> > > pointed to privileged content. However, as we only really need to
> > > operate on the contents of the tmpdir, we can ignore symlinks.
> > > v4: fix spelling in commit message
> > >
> > > diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> > > index 106f625f..c46db90f 100644
> > > --- a/sandbox/seunshare.c
> > > +++ b/sandbox/seunshare.c
> > > @@ -403,6 +403,66 @@ err:
> > > return rc;
> > > }
> > >
> > > +/*
> > > + * Recursively delete a directory.
> > > + * SAFTEY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
> >
> > Spelling here too: SAFETY
> >
> > > + * As a result, this function can be run safely on a directory owned by
> > > + * a non-root user: symbolic links to root paths (such as /root) will
> > > + * not be followed.
> > > + */
> > > +static bool rm_rf(int targetfd, const char *path) {
> > > + struct stat statbuf;
> > > +
> > > + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> > > + if (errno == ENOENT) {
> > > + return true;
> > > + }
> > > + perror("fstatat");
> > > + return false;
> > > + }
> > > +
> > > + if (S_ISDIR(statbuf.st_mode)) {
> > > + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> > > + if (newfd < 0) {
> > > + perror("openat");
> > > + return false;
> > > + }
> > > +
> > > + DIR *dir = fdopendir(newfd);
> > > + if (!dir) {
> > > + perror("fdopendir");
> > > + close(newfd);
> > > + return false;
> > > + }
> > > +
> > > + struct dirent *entry;
> > > + int rc = true;
> > > + while ((entry = readdir(dir)) != NULL) {
> > > + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> > > + continue;
> > > + }
> > > +
> > > + if (rm_rf(dirfd(dir), entry->d_name) != 0) {
> > > + rc = false;
> > > + }
> > > + }
> > > +
> > > + closedir(dir);
> > > +
> > > + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> > > + perror("unlinkat");
> > > + rc = false;
> > > + }
> > > +
> > > + return rc;
> > > + }
> > > + if (unlinkat(targetfd, path, 0) < 0) {
> > > + perror("unlinkat");
> > > + return false;
> > > + }
> > > + return true;
> > > +}
> > > +
> > > /**
> > > * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> > > * >0 if some error was detected, but errors here are treated as non-fatal and
> > > @@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> > > free(cmdbuf); cmdbuf = NULL;
> > > }
> > >
> > > - /* remove files from the runtime temporary directory */
> > > - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> > > - fprintf(stderr, _("Out of memory\n"));
> > > - cmdbuf = NULL;
> > > + if ((uid_t)setfsuid(0) != 0) {
> > > + /* setfsuid does not return error, but this check makes code checkers happy */
>
> I'm a little confused by this - further down there is a
> setfsuid(pwd->pw_uid) call which seems to suggest that we are already
> running as uid-0 at this point? If so, why do we need this setfsuid(0)
> call?
Never mind, I see it now. So just fix the earlier spelling mistake and
we'll call it done.
>
> > > rc++;
> > > }
> > > - /* this may fail if there's root-owned file left in the runtime tmpdir */
> > > - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> > > - free(cmdbuf); cmdbuf = NULL;
> > >
> > > - /* remove runtime temporary directory */
> > > - if ((uid_t)setfsuid(0) != 0) {
> > > - /* setfsuid does not return error, but this check makes code checkers happy */
> > > + /* Recursively remove the runtime temp directory. */
> > > + if (!rm_rf(AT_FDCWD, tmpdir)) {
> > > + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> > > rc++;
> > > }
> > >
> > > - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> > > - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> > > if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> > > fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> > > rc++;
> > > --
> > > 2.50.1
> > >
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5] seunshare: fix the frail tmpdir cleanup
2025-10-06 18:40 ` Stephen Smalley
@ 2025-10-07 9:29 ` Rahul Sandhu
2025-10-07 12:46 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Rahul Sandhu @ 2025-10-07 9:29 UTC (permalink / raw)
To: stephen.smalley.work; +Cc: jwcart2, nvraxn, selinux
For some reason, rm is invoked via system (3) to cleanup the runtime
temp directory. This really isn't all that robust, *especially* given
that seunshare is supposed to be a security boundary. Instead do this
using libc, the API designed to be used within C programs.
Also, don't bother trying to delete the contents of the tmpdir and then
trying to delete the parent directory with rmdir later - for some...
undocumented reason currently we attempt to delete the contents of the
dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
This doesn't really make all that much sense as far as I can tell. We
should be the only ones using the tmpdir, so we may as well just delete
the entire thing using the rm_rf () function with elevated permissions.
Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
---
sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 65 insertions(+), 12 deletions(-)
v2: don't use else after return
v3: don't follow symlinks in rm_rf (). This is pretty important as we
we are operating on an untrusted directory, which may have symlinks
pointed to privileged content. However, as we only really need to
operate on the contents of the tmpdir, we can ignore symlinks.
v4: fix spelling in commit message
v5: fix spelling in comment
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index 106f625f..55e62620 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -403,6 +403,66 @@ err:
return rc;
}
+/*
+ * Recursively delete a directory.
+ * SAFETY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
+ * As a result, this function can be run safely on a directory owned by
+ * a non-root user: symbolic links to root paths (such as /root) will
+ * not be followed.
+ */
+static bool rm_rf(int targetfd, const char *path) {
+ struct stat statbuf;
+
+ if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (errno == ENOENT) {
+ return true;
+ }
+ perror("fstatat");
+ return false;
+ }
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (newfd < 0) {
+ perror("openat");
+ return false;
+ }
+
+ DIR *dir = fdopendir(newfd);
+ if (!dir) {
+ perror("fdopendir");
+ close(newfd);
+ return false;
+ }
+
+ struct dirent *entry;
+ int rc = true;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+ continue;
+ }
+
+ if (rm_rf(dirfd(dir), entry->d_name) != 0) {
+ rc = false;
+ }
+ }
+
+ closedir(dir);
+
+ if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
+ perror("unlinkat");
+ rc = false;
+ }
+
+ return rc;
+ }
+ if (unlinkat(targetfd, path, 0) < 0) {
+ perror("unlinkat");
+ return false;
+ }
+ return true;
+}
+
/**
* Clean up runtime temporary directory. Returns 0 if no problem was detected,
* >0 if some error was detected, but errors here are treated as non-fatal and
@@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
free(cmdbuf); cmdbuf = NULL;
}
- /* remove files from the runtime temporary directory */
- if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
- fprintf(stderr, _("Out of memory\n"));
- cmdbuf = NULL;
+ if ((uid_t)setfsuid(0) != 0) {
+ /* setfsuid does not return error, but this check makes code checkers happy */
rc++;
}
- /* this may fail if there's root-owned file left in the runtime tmpdir */
- if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
- free(cmdbuf); cmdbuf = NULL;
- /* remove runtime temporary directory */
- if ((uid_t)setfsuid(0) != 0) {
- /* setfsuid does not return error, but this check makes code checkers happy */
+ /* Recursively remove the runtime temp directory. */
+ if (!rm_rf(AT_FDCWD, tmpdir)) {
+ fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
rc++;
}
- if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
- fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
rc++;
--
2.50.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v5] seunshare: fix the frail tmpdir cleanup
2025-10-07 9:29 ` [PATCH v5] " Rahul Sandhu
@ 2025-10-07 12:46 ` Stephen Smalley
2025-10-07 14:09 ` Stephen Smalley
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-10-07 12:46 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: jwcart2, selinux
On Tue, Oct 7, 2025 at 5:30 AM Rahul Sandhu <nvraxn@gmail.com> wrote:
>
> For some reason, rm is invoked via system (3) to cleanup the runtime
> temp directory. This really isn't all that robust, *especially* given
> that seunshare is supposed to be a security boundary. Instead do this
> using libc, the API designed to be used within C programs.
>
> Also, don't bother trying to delete the contents of the tmpdir and then
> trying to delete the parent directory with rmdir later - for some...
> undocumented reason currently we attempt to delete the contents of the
> dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
>
> This doesn't really make all that much sense as far as I can tell. We
> should be the only ones using the tmpdir, so we may as well just delete
> the entire thing using the rm_rf () function with elevated permissions.
>
> Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
With this patch applied, I see the following output when I use sandbox
-M and create anything in the tmpdir.
$ sandbox -M bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2$ mkdir /tmp/foobar
bash-5.2$ exit
Failed to recursively remove directory /tmp/.sandbox-sdsmall-chjRXi
> ---
> sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 65 insertions(+), 12 deletions(-)
>
> v2: don't use else after return
> v3: don't follow symlinks in rm_rf (). This is pretty important as we
> we are operating on an untrusted directory, which may have symlinks
> pointed to privileged content. However, as we only really need to
> operate on the contents of the tmpdir, we can ignore symlinks.
> v4: fix spelling in commit message
> v5: fix spelling in comment
>
> diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> index 106f625f..55e62620 100644
> --- a/sandbox/seunshare.c
> +++ b/sandbox/seunshare.c
> @@ -403,6 +403,66 @@ err:
> return rc;
> }
>
> +/*
> + * Recursively delete a directory.
> + * SAFETY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
> + * As a result, this function can be run safely on a directory owned by
> + * a non-root user: symbolic links to root paths (such as /root) will
> + * not be followed.
> + */
> +static bool rm_rf(int targetfd, const char *path) {
> + struct stat statbuf;
> +
> + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> + if (errno == ENOENT) {
> + return true;
> + }
> + perror("fstatat");
> + return false;
> + }
> +
> + if (S_ISDIR(statbuf.st_mode)) {
> + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> + if (newfd < 0) {
> + perror("openat");
> + return false;
> + }
> +
> + DIR *dir = fdopendir(newfd);
> + if (!dir) {
> + perror("fdopendir");
> + close(newfd);
> + return false;
> + }
> +
> + struct dirent *entry;
> + int rc = true;
> + while ((entry = readdir(dir)) != NULL) {
> + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> + continue;
> + }
> +
> + if (rm_rf(dirfd(dir), entry->d_name) != 0) {
> + rc = false;
> + }
> + }
> +
> + closedir(dir);
> +
> + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> + perror("unlinkat");
> + rc = false;
> + }
> +
> + return rc;
> + }
> + if (unlinkat(targetfd, path, 0) < 0) {
> + perror("unlinkat");
> + return false;
> + }
> + return true;
> +}
> +
> /**
> * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> * >0 if some error was detected, but errors here are treated as non-fatal and
> @@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> free(cmdbuf); cmdbuf = NULL;
> }
>
> - /* remove files from the runtime temporary directory */
> - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> - fprintf(stderr, _("Out of memory\n"));
> - cmdbuf = NULL;
> + if ((uid_t)setfsuid(0) != 0) {
> + /* setfsuid does not return error, but this check makes code checkers happy */
> rc++;
> }
> - /* this may fail if there's root-owned file left in the runtime tmpdir */
> - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> - free(cmdbuf); cmdbuf = NULL;
>
> - /* remove runtime temporary directory */
> - if ((uid_t)setfsuid(0) != 0) {
> - /* setfsuid does not return error, but this check makes code checkers happy */
> + /* Recursively remove the runtime temp directory. */
> + if (!rm_rf(AT_FDCWD, tmpdir)) {
> + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> rc++;
> }
>
> - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> rc++;
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5] seunshare: fix the frail tmpdir cleanup
2025-10-07 12:46 ` Stephen Smalley
@ 2025-10-07 14:09 ` Stephen Smalley
2025-10-07 16:59 ` Rahul Sandhu
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Smalley @ 2025-10-07 14:09 UTC (permalink / raw)
To: Rahul Sandhu; +Cc: jwcart2, selinux
On Tue, Oct 7, 2025 at 8:46 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Tue, Oct 7, 2025 at 5:30 AM Rahul Sandhu <nvraxn@gmail.com> wrote:
> >
> > For some reason, rm is invoked via system (3) to cleanup the runtime
> > temp directory. This really isn't all that robust, *especially* given
> > that seunshare is supposed to be a security boundary. Instead do this
> > using libc, the API designed to be used within C programs.
> >
> > Also, don't bother trying to delete the contents of the tmpdir and then
> > trying to delete the parent directory with rmdir later - for some...
> > undocumented reason currently we attempt to delete the contents of the
> > dir with dropped perms, *then* elevate perms, *then* delete the tmpdir.
> >
> > This doesn't really make all that much sense as far as I can tell. We
> > should be the only ones using the tmpdir, so we may as well just delete
Also, to be clear, "should be the only ones using the tmpdir above" is
not a safe assumption against the user spawning a detached process
that can try to create symlinks in parallel with the recursive
removal, but that should be mitigated by your rm_rf implementation
IIUC.
> > the entire thing using the rm_rf () function with elevated permissions.
> >
> > Signed-off-by: Rahul Sandhu <nvraxn@gmail.com>
>
> With this patch applied, I see the following output when I use sandbox
> -M and create anything in the tmpdir.
>
> $ sandbox -M bash
> bash: cannot set terminal process group (-1): Inappropriate ioctl for device
> bash: no job control in this shell
> bash-5.2$ mkdir /tmp/foobar
> bash-5.2$ exit
> Failed to recursively remove directory /tmp/.sandbox-sdsmall-chjRXi
>
> > ---
> > sandbox/seunshare.c | 77 ++++++++++++++++++++++++++++++++++++++-------
> > 1 file changed, 65 insertions(+), 12 deletions(-)
> >
> > v2: don't use else after return
> > v3: don't follow symlinks in rm_rf (). This is pretty important as we
> > we are operating on an untrusted directory, which may have symlinks
> > pointed to privileged content. However, as we only really need to
> > operate on the contents of the tmpdir, we can ignore symlinks.
> > v4: fix spelling in commit message
> > v5: fix spelling in comment
> >
> > diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
> > index 106f625f..55e62620 100644
> > --- a/sandbox/seunshare.c
> > +++ b/sandbox/seunshare.c
> > @@ -403,6 +403,66 @@ err:
> > return rc;
> > }
> >
> > +/*
> > + * Recursively delete a directory.
> > + * SAFETY: This function will NOT follow symbolic links (AT_SYMLINK_NOFOLLOW).
> > + * As a result, this function can be run safely on a directory owned by
> > + * a non-root user: symbolic links to root paths (such as /root) will
> > + * not be followed.
> > + */
> > +static bool rm_rf(int targetfd, const char *path) {
> > + struct stat statbuf;
> > +
> > + if (fstatat(targetfd, path, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) {
> > + if (errno == ENOENT) {
> > + return true;
> > + }
> > + perror("fstatat");
> > + return false;
> > + }
> > +
> > + if (S_ISDIR(statbuf.st_mode)) {
> > + const int newfd = openat(targetfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
> > + if (newfd < 0) {
> > + perror("openat");
> > + return false;
> > + }
> > +
> > + DIR *dir = fdopendir(newfd);
> > + if (!dir) {
> > + perror("fdopendir");
> > + close(newfd);
> > + return false;
> > + }
> > +
> > + struct dirent *entry;
> > + int rc = true;
> > + while ((entry = readdir(dir)) != NULL) {
> > + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
> > + continue;
> > + }
> > +
> > + if (rm_rf(dirfd(dir), entry->d_name) != 0) {
> > + rc = false;
> > + }
> > + }
> > +
> > + closedir(dir);
> > +
> > + if (unlinkat(targetfd, path, AT_REMOVEDIR) < 0) {
> > + perror("unlinkat");
> > + rc = false;
> > + }
> > +
> > + return rc;
> > + }
> > + if (unlinkat(targetfd, path, 0) < 0) {
> > + perror("unlinkat");
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > /**
> > * Clean up runtime temporary directory. Returns 0 if no problem was detected,
> > * >0 if some error was detected, but errors here are treated as non-fatal and
> > @@ -428,24 +488,17 @@ static int cleanup_tmpdir(const char *tmpdir, const char *src,
> > free(cmdbuf); cmdbuf = NULL;
> > }
> >
> > - /* remove files from the runtime temporary directory */
> > - if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
> > - fprintf(stderr, _("Out of memory\n"));
> > - cmdbuf = NULL;
> > + if ((uid_t)setfsuid(0) != 0) {
> > + /* setfsuid does not return error, but this check makes code checkers happy */
> > rc++;
> > }
> > - /* this may fail if there's root-owned file left in the runtime tmpdir */
> > - if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
> > - free(cmdbuf); cmdbuf = NULL;
> >
> > - /* remove runtime temporary directory */
> > - if ((uid_t)setfsuid(0) != 0) {
> > - /* setfsuid does not return error, but this check makes code checkers happy */
> > + /* Recursively remove the runtime temp directory. */
> > + if (!rm_rf(AT_FDCWD, tmpdir)) {
> > + fprintf(stderr, _("Failed to recursively remove directory %s\n"), tmpdir);
> > rc++;
> > }
> >
> > - if (pwd->pw_uid != 0 && rmdir(tmpdir) == -1)
> > - fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
> > if ((uid_t)setfsuid(pwd->pw_uid) != 0) {
> > fprintf(stderr, _("unable to switch back to user after clearing tmp dir\n"));
> > rc++;
> > --
> > 2.50.1
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5] seunshare: fix the frail tmpdir cleanup
2025-10-07 14:09 ` Stephen Smalley
@ 2025-10-07 16:59 ` Rahul Sandhu
0 siblings, 0 replies; 11+ messages in thread
From: Rahul Sandhu @ 2025-10-07 16:59 UTC (permalink / raw)
To: stephen.smalley.work; +Cc: jwcart2, nvraxn, selinux
> Also, to be clear, "should be the only ones using the tmpdir above" is
> not a safe assumption against the user spawning a detached process
> that can try to create symlinks in parallel with the recursive
> removal, but that should be mitigated by your rm_rf implementation
> IIUC.
Yes, sorry I forgot to update the commit message.
Rahul
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-10-07 16:59 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CAEjxPJ6QGZcAO zjdajOFTaDc tuh56Vao3niO37udEr=UfAgg@mail.gmail.com>
2025-08-01 18:21 ` [PATCH v3] seunshare: fix the frail tmpdir cleanup Rahul Sandhu
2025-08-01 18:29 ` Stephen Smalley
2025-08-14 17:47 ` James Carter
2025-09-25 5:38 ` [PATCH v4] " Rahul Sandhu
2025-10-06 15:57 ` Stephen Smalley
2025-10-06 16:31 ` Stephen Smalley
2025-10-06 18:40 ` Stephen Smalley
2025-10-07 9:29 ` [PATCH v5] " Rahul Sandhu
2025-10-07 12:46 ` Stephen Smalley
2025-10-07 14:09 ` Stephen Smalley
2025-10-07 16:59 ` Rahul Sandhu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).