diff mbox series

[v2,1/1] fanotify14: fix anonymous pipe testcases

Message ID 20240312120829.178305-2-meted@linux.ibm.com
State Superseded
Headers show
Series fix fanotify anonymous pipe testcases | expand

Commit Message

Mete Durlu March 12, 2024, 12:08 p.m. UTC
When SElinux is in enforcing state and SEpolicies disallow anonymous
pipe usage with fanotify_mark(), related fanotify14 testcases fail with
EACCES instead of EINVAL. Accept both errnos when SElinux is in
enforcing state to correctly evaluate test results.

Replace TST_EXP_FD_OR_FAIL with TST_EXP_FAIL when testing
fanotify_mark() as it returns -1 on failure and 0 on success not a file
descriptor.

Signed-off-by: Mete Durlu <meted@linux.ibm.com>
---
 .../kernel/syscalls/fanotify/fanotify14.c     | 32 +++++++++++++++++--
 1 file changed, 29 insertions(+), 3 deletions(-)

Comments

Amir Goldstein March 12, 2024, 3 p.m. UTC | #1
On Tue, Mar 12, 2024 at 2:09 PM Mete Durlu <meted@linux.ibm.com> wrote:
>
> When SElinux is in enforcing state and SEpolicies disallow anonymous
> pipe usage with fanotify_mark(), related fanotify14 testcases fail with
> EACCES instead of EINVAL. Accept both errnos when SElinux is in
> enforcing state to correctly evaluate test results.
>
> Replace TST_EXP_FD_OR_FAIL with TST_EXP_FAIL when testing
> fanotify_mark() as it returns -1 on failure and 0 on success not a file
> descriptor.
>
> Signed-off-by: Mete Durlu <meted@linux.ibm.com>
> ---
>  .../kernel/syscalls/fanotify/fanotify14.c     | 32 +++++++++++++++++--
>  1 file changed, 29 insertions(+), 3 deletions(-)
>
> diff --git a/testcases/kernel/syscalls/fanotify/fanotify14.c b/testcases/kernel/syscalls/fanotify/fanotify14.c
> index d02d81495..52c327dff 100644
> --- a/testcases/kernel/syscalls/fanotify/fanotify14.c
> +++ b/testcases/kernel/syscalls/fanotify/fanotify14.c
> @@ -27,12 +27,14 @@
>  #define _GNU_SOURCE
>  #include "tst_test.h"
>  #include <errno.h>
> +#include <stdlib.h>
>
>  #ifdef HAVE_SYS_FANOTIFY_H
>  #include "fanotify.h"
>
>  #define MNTPOINT "mntpoint"
>  #define FILE1 MNTPOINT"/file1"
> +#define SELINUX_STATUS_PATH "/sys/fs/selinux/enforce"
>
>  /*
>   * List of inode events that are only available when notification group is
> @@ -240,6 +242,19 @@ static struct test_case_t {
>         },
>  };
>
> +static int is_selinux_enforcing(void)
> +{
> +       char res;
> +       int fd;
> +
> +       fd = open(SELINUX_STATUS_PATH, O_RDONLY);
> +       if (fd <= 0)
> +               return 0;
> +       SAFE_READ(1, fd, &res, 1);
> +       SAFE_CLOSE(fd);
> +       return atoi(&res);
> +}
> +
>  static void do_test(unsigned int number)
>  {
>         struct test_case_t *tc = &test_cases[number];
> @@ -275,17 +290,28 @@ static void do_test(unsigned int number)
>         /* Set mark on non-dir only when expecting error ENOTDIR */
>         const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
>         int dirfd = AT_FDCWD;
> +       int se_enforcing = 0;
>
>         if (tc->pfd) {
>                 dirfd = tc->pfd[0];
>                 path = NULL;
> +               se_enforcing = is_selinux_enforcing();
>         }
>
>         tst_res(TINFO, "Testing %s with %s",
>                 tc->mark.desc, tc->mask.desc);
> -       TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> -                                        tc->mask.flags, dirfd, path),
> -                                        tc->expected_errno);
> +
> +       if (tc->pfd && se_enforcing) {
> +               const int exp_errs[] = {tc->expected_errno, EACCES};
> +
> +               TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> +                                tc->mask.flags, dirfd, path),
> +                                exp_errs);
> +       } else {
> +               TST_EXP_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> +                                                tc->mask.flags, dirfd, path),
> +                                                tc->expected_errno);
> +       }
>

This looks fine to me, but on second thought I am not sure how important
it is to special case se_enforcing.
We could probably always check for either error value.

Let's see what Jan and Petr think.

Thanks,
Amir.
Petr Vorel March 13, 2024, 7:26 a.m. UTC | #2
Hi all,
...

> >  static void do_test(unsigned int number)
> >  {
> >         struct test_case_t *tc = &test_cases[number];
> > @@ -275,17 +290,28 @@ static void do_test(unsigned int number)
> >         /* Set mark on non-dir only when expecting error ENOTDIR */
> >         const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
> >         int dirfd = AT_FDCWD;
> > +       int se_enforcing = 0;

> >         if (tc->pfd) {
> >                 dirfd = tc->pfd[0];
> >                 path = NULL;
> > +               se_enforcing = is_selinux_enforcing();
nit: this check should be in the setup function to be done only once.
(by default it's done twice, because we have 2 testcases with pfd, we support
-iN parameter, thus it's actually 2*N.). I'll fix it before merge.
> >         }

> >         tst_res(TINFO, "Testing %s with %s",
> >                 tc->mark.desc, tc->mask.desc);
> > -       TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > -                                        tc->mask.flags, dirfd, path),
> > -                                        tc->expected_errno);
> > +
> > +       if (tc->pfd && se_enforcing) {
> > +               const int exp_errs[] = {tc->expected_errno, EACCES};
> > +
> > +               TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > +                                tc->mask.flags, dirfd, path),
> > +                                exp_errs);
> > +       } else {
> > +               TST_EXP_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > +                                                tc->mask.flags, dirfd, path),
> > +                                                tc->expected_errno);
> > +       }


> This looks fine to me, but on second thought I am not sure how important
> it is to special case se_enforcing.
> We could probably always check for either error value.

I don't mind explicitly testing EACCES with SELinux. @Jan WDYT?

With a diff below (I can change it before merge + I would do the following work
to integrate this into the LTP C API):
Reviewed-by: Petr Vorel <pvorel@suse.cz>

Kind regards,
Petr

diff --git testcases/kernel/syscalls/fanotify/fanotify14.c testcases/kernel/syscalls/fanotify/fanotify14.c
index 52c327dff..89d59c8b2 100644
--- testcases/kernel/syscalls/fanotify/fanotify14.c
+++ testcases/kernel/syscalls/fanotify/fanotify14.c
@@ -49,6 +49,7 @@ static int pipes[2] = {-1, -1};
 static int fanotify_fd;
 static int ignore_mark_unsupported;
 static int filesystem_mark_unsupported;
+static int se_enforcing;
 static unsigned int supported_init_flags;
 
 struct test_case_flags_t {
@@ -290,12 +291,10 @@ static void do_test(unsigned int number)
 	/* Set mark on non-dir only when expecting error ENOTDIR */
 	const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
 	int dirfd = AT_FDCWD;
-	int se_enforcing = 0;
 
 	if (tc->pfd) {
 		dirfd = tc->pfd[0];
 		path = NULL;
-		se_enforcing = is_selinux_enforcing();
 	}
 
 	tst_res(TINFO, "Testing %s with %s",
@@ -360,6 +359,8 @@ static void do_setup(void)
 	SAFE_FILE_PRINTF(FILE1, "0");
 	/* Create anonymous pipes to place marks on */
 	SAFE_PIPE2(pipes, O_CLOEXEC);
+
+	se_enforcing = is_selinux_enforcing();
 }
 
 static void do_cleanup(void)
> Let's see what Jan and Petr think.

> Thanks,
> Amir.
Jan Kara March 13, 2024, 4:47 p.m. UTC | #3
On Wed 13-03-24 08:26:23, Petr Vorel wrote:
> Hi all,
> ...
> 
> > >  static void do_test(unsigned int number)
> > >  {
> > >         struct test_case_t *tc = &test_cases[number];
> > > @@ -275,17 +290,28 @@ static void do_test(unsigned int number)
> > >         /* Set mark on non-dir only when expecting error ENOTDIR */
> > >         const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
> > >         int dirfd = AT_FDCWD;
> > > +       int se_enforcing = 0;
> 
> > >         if (tc->pfd) {
> > >                 dirfd = tc->pfd[0];
> > >                 path = NULL;
> > > +               se_enforcing = is_selinux_enforcing();
> nit: this check should be in the setup function to be done only once.
> (by default it's done twice, because we have 2 testcases with pfd, we support
> -iN parameter, thus it's actually 2*N.). I'll fix it before merge.
> > >         }
> 
> > >         tst_res(TINFO, "Testing %s with %s",
> > >                 tc->mark.desc, tc->mask.desc);
> > > -       TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > > -                                        tc->mask.flags, dirfd, path),
> > > -                                        tc->expected_errno);
> > > +
> > > +       if (tc->pfd && se_enforcing) {
> > > +               const int exp_errs[] = {tc->expected_errno, EACCES};
> > > +
> > > +               TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > > +                                tc->mask.flags, dirfd, path),
> > > +                                exp_errs);
> > > +       } else {
> > > +               TST_EXP_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
> > > +                                                tc->mask.flags, dirfd, path),
> > > +                                                tc->expected_errno);
> > > +       }
> 
> 
> > This looks fine to me, but on second thought I am not sure how important
> > it is to special case se_enforcing.
> > We could probably always check for either error value.
> 
> I don't mind explicitly testing EACCES with SELinux. @Jan WDYT?
> 
> With a diff below (I can change it before merge + I would do the following work
> to integrate this into the LTP C API):
> Reviewed-by: Petr Vorel <pvorel@suse.cz>

Yes, looks fine to me as well. I don't feel strongly whether we should
accept EACCESS unconditionally or only with SELinux. I suspect eventually
we might need to accept it unconditionally as there may be other security
modules that would block addition of the mark. But let's see what the
future brings. So feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza
Mete Durlu March 14, 2024, 8:34 a.m. UTC | #4
On 3/13/24 08:26, Petr Vorel wrote:

Hi,

thanks for the review. I can send a v3 with the suggested changes if
that will make things easier. Just let me know.

>>>          if (tc->pfd) {
>>>                  dirfd = tc->pfd[0];
>>>                  path = NULL;
>>> +               se_enforcing = is_selinux_enforcing();
> nit: this check should be in the setup function to be done only once.
> (by default it's done twice, because we have 2 testcases with pfd, we support
> -iN parameter, thus it's actually 2*N.). I'll fix it before merge.
>>>          }
>
Nice catch! I fully forgot that there was a setup function while I
was trying to find the best TST_ macro to use.

>>>          tst_res(TINFO, "Testing %s with %s",
>>>                  tc->mark.desc, tc->mask.desc);
>>> -       TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
>>> -                                        tc->mask.flags, dirfd, path),
>>> -                                        tc->expected_errno);
>>> +
>>> +       if (tc->pfd && se_enforcing) {
>>> +               const int exp_errs[] = {tc->expected_errno, EACCES};
>>> +
>>> +               TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
>>> +                                tc->mask.flags, dirfd, path),
>>> +                                exp_errs);
>>> +       } else {
>>> +               TST_EXP_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
>>> +                                                tc->mask.flags, dirfd, path),
>>> +                                                tc->expected_errno);
>>> +       }
> 
> 
>> This looks fine to me, but on second thought I am not sure how important
>> it is to special case se_enforcing.
>> We could probably always check for either error value.
> 
> I don't mind explicitly testing EACCES with SELinux. @Jan WDYT?
> 
> With a diff below (I can change it before merge + I would do the following work
> to integrate this into the LTP C API):
> Reviewed-by: Petr Vorel <pvorel@suse.cz>
> 
> Kind regards,
> Petr
> 
> diff --git testcases/kernel/syscalls/fanotify/fanotify14.c testcases/kernel/syscalls/fanotify/fanotify14.c
> index 52c327dff..89d59c8b2 100644
> --- testcases/kernel/syscalls/fanotify/fanotify14.c
> +++ testcases/kernel/syscalls/fanotify/fanotify14.c
> @@ -49,6 +49,7 @@ static int pipes[2] = {-1, -1};
>   static int fanotify_fd;
>   static int ignore_mark_unsupported;
>   static int filesystem_mark_unsupported;
> +static int se_enforcing;
>   static unsigned int supported_init_flags;
>   
>   struct test_case_flags_t {
> @@ -290,12 +291,10 @@ static void do_test(unsigned int number)
>   	/* Set mark on non-dir only when expecting error ENOTDIR */
>   	const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
>   	int dirfd = AT_FDCWD;
> -	int se_enforcing = 0;
>   
>   	if (tc->pfd) {
>   		dirfd = tc->pfd[0];
>   		path = NULL;
> -		se_enforcing = is_selinux_enforcing();
>   	}
>   
>   	tst_res(TINFO, "Testing %s with %s",
> @@ -360,6 +359,8 @@ static void do_setup(void)
>   	SAFE_FILE_PRINTF(FILE1, "0");
>   	/* Create anonymous pipes to place marks on */
>   	SAFE_PIPE2(pipes, O_CLOEXEC);
> +
> +	se_enforcing = is_selinux_enforcing();
>   }
>   
>   static void do_cleanup(void)
>> Let's see what Jan and Petr think.
> 
>> Thanks,
>> Amir.
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/fanotify/fanotify14.c b/testcases/kernel/syscalls/fanotify/fanotify14.c
index d02d81495..52c327dff 100644
--- a/testcases/kernel/syscalls/fanotify/fanotify14.c
+++ b/testcases/kernel/syscalls/fanotify/fanotify14.c
@@ -27,12 +27,14 @@ 
 #define _GNU_SOURCE
 #include "tst_test.h"
 #include <errno.h>
+#include <stdlib.h>
 
 #ifdef HAVE_SYS_FANOTIFY_H
 #include "fanotify.h"
 
 #define MNTPOINT "mntpoint"
 #define FILE1 MNTPOINT"/file1"
+#define SELINUX_STATUS_PATH "/sys/fs/selinux/enforce"
 
 /*
  * List of inode events that are only available when notification group is
@@ -240,6 +242,19 @@  static struct test_case_t {
 	},
 };
 
+static int is_selinux_enforcing(void)
+{
+	char res;
+	int fd;
+
+	fd = open(SELINUX_STATUS_PATH, O_RDONLY);
+	if (fd <= 0)
+		return 0;
+	SAFE_READ(1, fd, &res, 1);
+	SAFE_CLOSE(fd);
+	return atoi(&res);
+}
+
 static void do_test(unsigned int number)
 {
 	struct test_case_t *tc = &test_cases[number];
@@ -275,17 +290,28 @@  static void do_test(unsigned int number)
 	/* Set mark on non-dir only when expecting error ENOTDIR */
 	const char *path = tc->expected_errno == ENOTDIR ? FILE1 : MNTPOINT;
 	int dirfd = AT_FDCWD;
+	int se_enforcing = 0;
 
 	if (tc->pfd) {
 		dirfd = tc->pfd[0];
 		path = NULL;
+		se_enforcing = is_selinux_enforcing();
 	}
 
 	tst_res(TINFO, "Testing %s with %s",
 		tc->mark.desc, tc->mask.desc);
-	TST_EXP_FD_OR_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
-					 tc->mask.flags, dirfd, path),
-					 tc->expected_errno);
+
+	if (tc->pfd && se_enforcing) {
+		const int exp_errs[] = {tc->expected_errno, EACCES};
+
+		TST_EXP_FAIL_ARR(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
+				 tc->mask.flags, dirfd, path),
+				 exp_errs);
+	} else {
+		TST_EXP_FAIL(fanotify_mark(fanotify_fd, FAN_MARK_ADD | tc->mark.flags,
+						 tc->mask.flags, dirfd, path),
+						 tc->expected_errno);
+	}
 
 	/*
 	 * ENOTDIR are errors for events/flags not allowed on a non-dir inode.