diff mbox series

[v8,5/9] selftests/landlock: Test IOCTL support

Message ID 20231208155121.1943775-6-gnoack@google.com (mailing list archive)
State New
Headers show
Series Landlock: IOCTL support | expand

Commit Message

Günther Noack Dec. 8, 2023, 3:51 p.m. UTC
Exercises Landlock's IOCTL feature in different combinations of
handling and permitting the rights LANDLOCK_ACCESS_FS_IOCTL,
LANDLOCK_ACCESS_FS_READ_FILE, LANDLOCK_ACCESS_FS_WRITE_FILE and
LANDLOCK_ACCESS_FS_READ_DIR, and in different combinations of using
files and directories.

Signed-off-by: Günther Noack <gnoack@google.com>
---
 tools/testing/selftests/landlock/fs_test.c | 431 ++++++++++++++++++++-
 1 file changed, 428 insertions(+), 3 deletions(-)

Comments

Aishwarya TCV Dec. 15, 2023, 12:52 p.m. UTC | #1
On 08/12/2023 15:51, Günther Noack wrote:
> Exercises Landlock's IOCTL feature in different combinations of
> handling and permitting the rights LANDLOCK_ACCESS_FS_IOCTL,
> LANDLOCK_ACCESS_FS_READ_FILE, LANDLOCK_ACCESS_FS_WRITE_FILE and
> LANDLOCK_ACCESS_FS_READ_DIR, and in different combinations of using
> files and directories.
> 
> Signed-off-by: Günther Noack <gnoack@google.com>
> ---
>  tools/testing/selftests/landlock/fs_test.c | 431 ++++++++++++++++++++-

Hi Günther,

When building kselftest against next-master the below build error is
observed. A bisect (full log
below) identified this patch as introducing the failure.

Full log from a failure:
https://storage.kernelci.org/next/master/next-20231215/arm64/defconfig+kselftest/gcc-10/logs/kselftest.log

-----
make[4]: Entering directory
'/tmp/kci/linux/tools/testing/selftests/landlock'
aarch64-linux-gnu-gcc -Wall -O2 -isystem
/tmp/kci/linux/build/usr/include     base_test.c -lcap -o
/tmp/kci/linux/build/kselftest/landlock/base_test
aarch64-linux-gnu-gcc -Wall -O2 -isystem
/tmp/kci/linux/build/usr/include     fs_test.c -lcap -o
/tmp/kci/linux/build/kselftest/landlock/fs_test
In file included from /tmp/kci/linux/build/usr/include/linux/fs.h:19,
                 from fs_test.c:12:
/usr/include/aarch64-linux-gnu/sys/mount.h:35:3: error: expected
identifier before numeric constant
   35 |   MS_RDONLY = 1,  /* Mount read-only.  */
      |   ^~~~~~~~~
In file included from common.h:19,
                 from fs_test.c:27:
fs_test.c: In function ‘prepare_layout_opt’:
fs_test.c:281:42: error: ‘MS_PRIVATE’ undeclared (first use in this
function)
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                          ^~~~~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c:281:42: note: each undeclared identifier is reported only once
for each function it appears in
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                          ^~~~~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c:281:55: error: ‘MS_REC’ undeclared (first use in this function)
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                                       ^~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
  281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c: In function ‘layout1_mount_and_pivot_child’:
fs_test.c:1653:44: error: ‘MS_RDONLY’ undeclared (first use in this
function)
 1653 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
      |                                            ^~~~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:1653:2: note: in expansion of macro ‘ASSERT_EQ’
 1653 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
      |  ^~~~~~~~~
fs_test.c: In function ‘layout1_topology_changes_with_net_only_child’:
fs_test.c:1712:43: error: ‘MS_PRIVATE’ undeclared (first use in this
function)
 1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                           ^~~~~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:1712:2: note: in expansion of macro ‘ASSERT_EQ’
 1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c:1712:56: error: ‘MS_REC’ undeclared (first use in this function)
 1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                                        ^~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:1712:2: note: in expansion of macro ‘ASSERT_EQ’
 1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c: In function ‘layout1_topology_changes_with_net_and_fs_child’:
fs_test.c:1741:44: error: ‘MS_PRIVATE’ undeclared (first use in this
function)
 1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                            ^~~~~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:1741:2: note: in expansion of macro ‘ASSERT_EQ’
 1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c:1741:57: error: ‘MS_REC’ undeclared (first use in this function)
 1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |                                                         ^~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:1741:2: note: in expansion of macro ‘ASSERT_EQ’
 1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
NULL));
      |  ^~~~~~~~~
fs_test.c: In function ‘layout1_bind_setup’:
fs_test.c:4340:47: error: ‘MS_BIND’ undeclared (first use in this function)
 4340 |  ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
      |                                               ^~~~~~~
../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
  707 |  __typeof__(_seen) __seen = (_seen); \
      |             ^~~~~
fs_test.c:4340:2: note: in expansion of macro ‘ASSERT_EQ’
 4340 |  ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
      |  ^~~~~~~~~
In file included from fs_test.c:19:
fs_test.c: At top level:
fs_test.c:5155:12: error: ‘MS_BIND’ undeclared here (not in a function)
 5155 |   .flags = MS_BIND,
      |            ^~~~~~~
make[4]: *** [../lib.mk:147:
/tmp/kci/linux/build/kselftest/landlock/fs_test] Error 1
make[4]: Leaving directory '/tmp/kci/linux/tools/testing/selftests/landlock'
-----


Bisect log:

-----
git bisect start
# good: [a39b6ac3781d46ba18193c9dbb2110f31e9bffe9] Linux 6.7-rc5
git bisect good a39b6ac3781d46ba18193c9dbb2110f31e9bffe9
# bad: [11651f8cb2e88372d4ed523d909514dc9a613ea3] Add linux-next
specific files for 20231214
git bisect bad 11651f8cb2e88372d4ed523d909514dc9a613ea3
# good: [436cc0377e881784e5d12a863db037ad7d56b700] Merge branch 'main'
of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
git bisect good 436cc0377e881784e5d12a863db037ad7d56b700
# good: [4acaf686fcfee1d2ce0770a1d7505cd0e66400f0] Merge branch 'next'
of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git
git bisect good 4acaf686fcfee1d2ce0770a1d7505cd0e66400f0
# good: [81d6c0949c93b9fb46ddd53819bc1dd69b161fb5] Merge branch
'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
git bisect good 81d6c0949c93b9fb46ddd53819bc1dd69b161fb5
# good: [21298ae90dfc30823d4b3e8c28b536b94816a625] Merge branch
'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
git bisect good 21298ae90dfc30823d4b3e8c28b536b94816a625
# good: [f2cd1cb9acacb72cab0f90d2d648659fda209f75] Merge branch 'kunit'
of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git
git bisect good f2cd1cb9acacb72cab0f90d2d648659fda209f75
# good: [a3cd576f9a3d15f7697764a9439b91fd1acb603c] Merge branch
'slab/for-next' of
git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git
git bisect good a3cd576f9a3d15f7697764a9439b91fd1acb603c
# bad: [79b6e5e0cf1a746e40d87053db55dce76d1fd718] Merge branch
'for-next/kspp' of
git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git
git bisect bad 79b6e5e0cf1a746e40d87053db55dce76d1fd718
# bad: [7098a5baeb1014c676b9e86025afd274807900a7] Merge branch
'sysctl-next' of
git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git
git bisect bad 7098a5baeb1014c676b9e86025afd274807900a7
# bad: [9b4e8cb962dfcc7d5919b0ca383ff3df7f88f7cb] Merge branch 'next' of
git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
git bisect bad 9b4e8cb962dfcc7d5919b0ca383ff3df7f88f7cb
# good: [2d2016fefb8edd11a87053caab3a9044dbd7093e] landlock: Add IOCTL
access right
git bisect good 2d2016fefb8edd11a87053caab3a9044dbd7093e
# bad: [86d25e41081ec6359c75e2e873b085de03f3cd34] selftests/landlock:
Test ioctl(2) and ftruncate(2) with open(O_PATH)
git bisect bad 86d25e41081ec6359c75e2e873b085de03f3cd34
# bad: [a725134eca88b930bc2c5947297ccf72238a8149] selftests/landlock:
Test IOCTL with memfds
git bisect bad a725134eca88b930bc2c5947297ccf72238a8149
# bad: [e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2] selftests/landlock:
Test IOCTL support
git bisect bad e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2
# first bad commit: [e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2]
selftests/landlock: Test IOCTL support
-----

Thanks,
Aishwarya
Günther Noack Jan. 12, 2024, 5:31 p.m. UTC | #2
Hello Aishwarya!

Thanks for the bug report!

I tried this with the aarch64-linux-gnu-gcc-13 cross compiler on Debian, on
next-20231215, but I can not reproduce it.

On Fri, Dec 15, 2023 at 12:52:19PM +0000, Aishwarya TCV wrote:
> On 08/12/2023 15:51, Günther Noack wrote:
> > Exercises Landlock's IOCTL feature in different combinations of
> > handling and permitting the rights LANDLOCK_ACCESS_FS_IOCTL,
> > LANDLOCK_ACCESS_FS_READ_FILE, LANDLOCK_ACCESS_FS_WRITE_FILE and
> > LANDLOCK_ACCESS_FS_READ_DIR, and in different combinations of using
> > files and directories.
> > 
> > Signed-off-by: Günther Noack <gnoack@google.com>
> > ---
> >  tools/testing/selftests/landlock/fs_test.c | 431 ++++++++++++++++++++-
> 
> Hi Günther,
> 
> When building kselftest against next-master the below build error is
> observed. A bisect (full log
> below) identified this patch as introducing the failure.
> 
> Full log from a failure:
> https://storage.kernelci.org/next/master/next-20231215/arm64/defconfig+kselftest/gcc-10/logs/kselftest.log
> 
> -----
> make[4]: Entering directory
> '/tmp/kci/linux/tools/testing/selftests/landlock'
> aarch64-linux-gnu-gcc -Wall -O2 -isystem
> /tmp/kci/linux/build/usr/include     base_test.c -lcap -o
> /tmp/kci/linux/build/kselftest/landlock/base_test
> aarch64-linux-gnu-gcc -Wall -O2 -isystem
> /tmp/kci/linux/build/usr/include     fs_test.c -lcap -o
> /tmp/kci/linux/build/kselftest/landlock/fs_test
> In file included from /tmp/kci/linux/build/usr/include/linux/fs.h:19,
>                  from fs_test.c:12:
> /usr/include/aarch64-linux-gnu/sys/mount.h:35:3: error: expected

The IOCTL patch set has introduced an "#include <linux/fs.h>" at the top of
selftests/landlock/fs_test.c (that file includes some IOCTL command numbers),
but that should in my mind normally be safe to include?

I'm surprised that according to the log, fs.h line 19 is including sys/mount.h
instead of linux/mount.h...?  This line says “#include <linux/mount.h>”?

> identifier before numeric constant
>    35 |   MS_RDONLY = 1,  /* Mount read-only.  */
>       |   ^~~~~~~~~
> In file included from common.h:19,
>                  from fs_test.c:27:

If you have more leads or more concrete reproduction steps, I'd be interested to
know.  Otherwise, I'd have to just hope that it'll work better on the next
attempt...?

Thanks,
—Günther
Mark Brown Jan. 12, 2024, 6:59 p.m. UTC | #3
On Fri, Jan 12, 2024 at 06:31:37PM +0100, Günther Noack wrote:

> I tried this with the aarch64-linux-gnu-gcc-13 cross compiler on Debian, on
> next-20231215, but I can not reproduce it.

The KernelCI environment for building this is in a Docker container
kernelci/gcc-10_arm64 which is on hub.docker.com - it's just Debian
oldstable with the default toolchain.

> > Full log from a failure:
> > https://storage.kernelci.org/next/master/next-20231215/arm64/defconfig+kselftest/gcc-10/logs/kselftest.log

Today's -next log is at:

   https://storage.kernelci.org/next/master/next-20240112/arm64/defconfig/gcc-10/logs/kselftest.log

which looks about the same.

> > /tmp/kci/linux/build/usr/include     fs_test.c -lcap -o
> > /tmp/kci/linux/build/kselftest/landlock/fs_test
> > In file included from /tmp/kci/linux/build/usr/include/linux/fs.h:19,
> >                  from fs_test.c:12:
> > /usr/include/aarch64-linux-gnu/sys/mount.h:35:3: error: expected

> The IOCTL patch set has introduced an "#include <linux/fs.h>" at the top of
> selftests/landlock/fs_test.c (that file includes some IOCTL command numbers),
> but that should in my mind normally be safe to include?

I'd have expected so, and nothing in the header looks like it has
implicit dependencies or anything.

> I'm surprised that according to the log, fs.h line 19 is including sys/mount.h
> instead of linux/mount.h...?  This line says “#include <linux/mount.h>”?

I'm seeing sys/mount.h in -next 20240112 (ie, today):

  https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/tools/testing/selftests/landlock/fs_test.c

AFAICT it appears to have been that way since the original commit?

> > identifier before numeric constant
> >    35 |   MS_RDONLY = 1,  /* Mount read-only.  */
> >       |   ^~~~~~~~~
> > In file included from common.h:19,
> >                  from fs_test.c:27:

> If you have more leads or more concrete reproduction steps, I'd be interested to
> know.  Otherwise, I'd have to just hope that it'll work better on the next
> attempt...?

The make command at the top of the logs linked above:

   make KBUILD_BUILD_USER=KernelCI FORMAT=.xz ARCH=arm64 HOSTCC=gcc CROSS_COMPILE=aarch64-linux-gnu- CROSS_COMPILE_COMPAT=arm-linux-gnueabihf- CC="ccache aarch64-linux-gnu-gcc" O=/tmp/kci/linux/build -C/tmp/kci/linux -j10 kselftest-gen_tar

will hopefully DTRT in the above Docker container though I didn't
actually try locally.
Günther Noack Jan. 15, 2024, 2:20 p.m. UTC | #4
Side remark, this other patch on the LSM list seems to about a related bug:
https://lore.kernel.org/linux-security-module/20240112074059.29673-1-hu.yadi@h3c.com/T/#u

On Fri, Dec 15, 2023 at 12:52:19PM +0000, Aishwarya TCV wrote:
> 
> 
> On 08/12/2023 15:51, Günther Noack wrote:
> > Exercises Landlock's IOCTL feature in different combinations of
> > handling and permitting the rights LANDLOCK_ACCESS_FS_IOCTL,
> > LANDLOCK_ACCESS_FS_READ_FILE, LANDLOCK_ACCESS_FS_WRITE_FILE and
> > LANDLOCK_ACCESS_FS_READ_DIR, and in different combinations of using
> > files and directories.
> > 
> > Signed-off-by: Günther Noack <gnoack@google.com>
> > ---
> >  tools/testing/selftests/landlock/fs_test.c | 431 ++++++++++++++++++++-
> 
> Hi Günther,
> 
> When building kselftest against next-master the below build error is
> observed. A bisect (full log
> below) identified this patch as introducing the failure.
> 
> Full log from a failure:
> https://storage.kernelci.org/next/master/next-20231215/arm64/defconfig+kselftest/gcc-10/logs/kselftest.log
> 
> -----
> make[4]: Entering directory
> '/tmp/kci/linux/tools/testing/selftests/landlock'
> aarch64-linux-gnu-gcc -Wall -O2 -isystem
> /tmp/kci/linux/build/usr/include     base_test.c -lcap -o
> /tmp/kci/linux/build/kselftest/landlock/base_test
> aarch64-linux-gnu-gcc -Wall -O2 -isystem
> /tmp/kci/linux/build/usr/include     fs_test.c -lcap -o
> /tmp/kci/linux/build/kselftest/landlock/fs_test
> In file included from /tmp/kci/linux/build/usr/include/linux/fs.h:19,
>                  from fs_test.c:12:
> /usr/include/aarch64-linux-gnu/sys/mount.h:35:3: error: expected
> identifier before numeric constant
>    35 |   MS_RDONLY = 1,  /* Mount read-only.  */
>       |   ^~~~~~~~~
> In file included from common.h:19,
>                  from fs_test.c:27:
> fs_test.c: In function ‘prepare_layout_opt’:
> fs_test.c:281:42: error: ‘MS_PRIVATE’ undeclared (first use in this
> function)
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                          ^~~~~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c:281:42: note: each undeclared identifier is reported only once
> for each function it appears in
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                          ^~~~~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c:281:55: error: ‘MS_REC’ undeclared (first use in this function)
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                                       ^~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:281:2: note: in expansion of macro ‘ASSERT_EQ’
>   281 |  ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c: In function ‘layout1_mount_and_pivot_child’:
> fs_test.c:1653:44: error: ‘MS_RDONLY’ undeclared (first use in this
> function)
>  1653 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
>       |                                            ^~~~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:1653:2: note: in expansion of macro ‘ASSERT_EQ’
>  1653 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
>       |  ^~~~~~~~~
> fs_test.c: In function ‘layout1_topology_changes_with_net_only_child’:
> fs_test.c:1712:43: error: ‘MS_PRIVATE’ undeclared (first use in this
> function)
>  1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                           ^~~~~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:1712:2: note: in expansion of macro ‘ASSERT_EQ’
>  1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c:1712:56: error: ‘MS_REC’ undeclared (first use in this function)
>  1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                                        ^~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:1712:2: note: in expansion of macro ‘ASSERT_EQ’
>  1712 |  ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c: In function ‘layout1_topology_changes_with_net_and_fs_child’:
> fs_test.c:1741:44: error: ‘MS_PRIVATE’ undeclared (first use in this
> function)
>  1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                            ^~~~~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:1741:2: note: in expansion of macro ‘ASSERT_EQ’
>  1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c:1741:57: error: ‘MS_REC’ undeclared (first use in this function)
>  1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |                                                         ^~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:1741:2: note: in expansion of macro ‘ASSERT_EQ’
>  1741 |  ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC,
> NULL));
>       |  ^~~~~~~~~
> fs_test.c: In function ‘layout1_bind_setup’:
> fs_test.c:4340:47: error: ‘MS_BIND’ undeclared (first use in this function)
>  4340 |  ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
>       |                                               ^~~~~~~
> ../kselftest_harness.h:707:13: note: in definition of macro ‘__EXPECT’
>   707 |  __typeof__(_seen) __seen = (_seen); \
>       |             ^~~~~
> fs_test.c:4340:2: note: in expansion of macro ‘ASSERT_EQ’
>  4340 |  ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
>       |  ^~~~~~~~~
> In file included from fs_test.c:19:
> fs_test.c: At top level:
> fs_test.c:5155:12: error: ‘MS_BIND’ undeclared here (not in a function)
>  5155 |   .flags = MS_BIND,
>       |            ^~~~~~~
> make[4]: *** [../lib.mk:147:
> /tmp/kci/linux/build/kselftest/landlock/fs_test] Error 1
> make[4]: Leaving directory '/tmp/kci/linux/tools/testing/selftests/landlock'
> -----
> 
> 
> Bisect log:
> 
> -----
> git bisect start
> # good: [a39b6ac3781d46ba18193c9dbb2110f31e9bffe9] Linux 6.7-rc5
> git bisect good a39b6ac3781d46ba18193c9dbb2110f31e9bffe9
> # bad: [11651f8cb2e88372d4ed523d909514dc9a613ea3] Add linux-next
> specific files for 20231214
> git bisect bad 11651f8cb2e88372d4ed523d909514dc9a613ea3
> # good: [436cc0377e881784e5d12a863db037ad7d56b700] Merge branch 'main'
> of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
> git bisect good 436cc0377e881784e5d12a863db037ad7d56b700
> # good: [4acaf686fcfee1d2ce0770a1d7505cd0e66400f0] Merge branch 'next'
> of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git
> git bisect good 4acaf686fcfee1d2ce0770a1d7505cd0e66400f0
> # good: [81d6c0949c93b9fb46ddd53819bc1dd69b161fb5] Merge branch
> 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> git bisect good 81d6c0949c93b9fb46ddd53819bc1dd69b161fb5
> # good: [21298ae90dfc30823d4b3e8c28b536b94816a625] Merge branch
> 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
> git bisect good 21298ae90dfc30823d4b3e8c28b536b94816a625
> # good: [f2cd1cb9acacb72cab0f90d2d648659fda209f75] Merge branch 'kunit'
> of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git
> git bisect good f2cd1cb9acacb72cab0f90d2d648659fda209f75
> # good: [a3cd576f9a3d15f7697764a9439b91fd1acb603c] Merge branch
> 'slab/for-next' of
> git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git
> git bisect good a3cd576f9a3d15f7697764a9439b91fd1acb603c
> # bad: [79b6e5e0cf1a746e40d87053db55dce76d1fd718] Merge branch
> 'for-next/kspp' of
> git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git
> git bisect bad 79b6e5e0cf1a746e40d87053db55dce76d1fd718
> # bad: [7098a5baeb1014c676b9e86025afd274807900a7] Merge branch
> 'sysctl-next' of
> git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux.git
> git bisect bad 7098a5baeb1014c676b9e86025afd274807900a7
> # bad: [9b4e8cb962dfcc7d5919b0ca383ff3df7f88f7cb] Merge branch 'next' of
> git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
> git bisect bad 9b4e8cb962dfcc7d5919b0ca383ff3df7f88f7cb
> # good: [2d2016fefb8edd11a87053caab3a9044dbd7093e] landlock: Add IOCTL
> access right
> git bisect good 2d2016fefb8edd11a87053caab3a9044dbd7093e
> # bad: [86d25e41081ec6359c75e2e873b085de03f3cd34] selftests/landlock:
> Test ioctl(2) and ftruncate(2) with open(O_PATH)
> git bisect bad 86d25e41081ec6359c75e2e873b085de03f3cd34
> # bad: [a725134eca88b930bc2c5947297ccf72238a8149] selftests/landlock:
> Test IOCTL with memfds
> git bisect bad a725134eca88b930bc2c5947297ccf72238a8149
> # bad: [e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2] selftests/landlock:
> Test IOCTL support
> git bisect bad e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2
> # first bad commit: [e0bf2e60f9c35ab3fa13ff33fb3e0088fe2248c2]
> selftests/landlock: Test IOCTL support
> -----
> 
> Thanks,
> Aishwarya
diff mbox series

Patch

diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 192608c3e840..054779ef4527 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -9,6 +9,7 @@ 
 
 #define _GNU_SOURCE
 #include <fcntl.h>
+#include <linux/fs.h>
 #include <linux/landlock.h>
 #include <linux/magic.h>
 #include <sched.h>
@@ -733,6 +734,9 @@  static int create_ruleset(struct __test_metadata *const _metadata,
 	}
 
 	for (i = 0; rules[i].path; i++) {
+		if (!rules[i].access)
+			continue;
+
 		add_path_beneath(_metadata, ruleset_fd, rules[i].access,
 				 rules[i].path);
 	}
@@ -3441,7 +3445,7 @@  TEST_F_FORK(layout1, truncate_unhandled)
 			      LANDLOCK_ACCESS_FS_WRITE_FILE;
 	int ruleset_fd;
 
-	/* Enable Landlock. */
+	/* Enables Landlock. */
 	ruleset_fd = create_ruleset(_metadata, handled, rules);
 
 	ASSERT_LE(0, ruleset_fd);
@@ -3524,7 +3528,7 @@  TEST_F_FORK(layout1, truncate)
 			      LANDLOCK_ACCESS_FS_TRUNCATE;
 	int ruleset_fd;
 
-	/* Enable Landlock. */
+	/* Enables Landlock. */
 	ruleset_fd = create_ruleset(_metadata, handled, rules);
 
 	ASSERT_LE(0, ruleset_fd);
@@ -3750,7 +3754,7 @@  TEST_F_FORK(ftruncate, open_and_ftruncate)
 	};
 	int fd, ruleset_fd;
 
-	/* Enable Landlock. */
+	/* Enables Landlock. */
 	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
 	ASSERT_LE(0, ruleset_fd);
 	enforce_ruleset(_metadata, ruleset_fd);
@@ -3827,6 +3831,16 @@  TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
 	ASSERT_EQ(0, close(socket_fds[1]));
 }
 
+/* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */
+static int test_fs_ioc_getflags_ioctl(int fd)
+{
+	uint32_t flags;
+
+	if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
+		return errno;
+	return 0;
+}
+
 TEST(memfd_ftruncate)
 {
 	int fd;
@@ -3843,6 +3857,417 @@  TEST(memfd_ftruncate)
 	ASSERT_EQ(0, close(fd));
 }
 
+/* clang-format off */
+FIXTURE(ioctl) {};
+/* clang-format on */
+
+FIXTURE_SETUP(ioctl)
+{
+	prepare_layout(_metadata);
+	create_file(_metadata, file1_s1d1);
+}
+
+FIXTURE_TEARDOWN(ioctl)
+{
+	EXPECT_EQ(0, remove_path(file1_s1d1));
+	cleanup_layout(_metadata);
+}
+
+FIXTURE_VARIANT(ioctl)
+{
+	const __u64 handled;
+	const __u64 allowed;
+	const mode_t open_mode;
+	/*
+	 * These are the expected IOCTL results for a representative IOCTL from
+	 * each of the IOCTL groups.  We only distinguish the 0 and EACCES
+	 * results here, and treat other errors as 0.
+	 */
+	const int expected_fioqsize_result; /* G1 */
+	const int expected_fibmap_result; /* G2 */
+	const int expected_fionread_result; /* G3 */
+	const int expected_fs_ioc_zero_range_result; /* G4 */
+	const int expected_fs_ioc_getflags_result; /* other */
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = 0,
+	.open_mode = O_RDWR,
+	.expected_fioqsize_result = EACCES,
+	.expected_fibmap_result = EACCES,
+	.expected_fionread_result = EACCES,
+	.expected_fs_ioc_zero_range_result = EACCES,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_IOCTL,
+	.open_mode = O_RDWR,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, unhandled) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_EXECUTE,
+	.allowed = LANDLOCK_ACCESS_FS_EXECUTE,
+	.open_mode = O_RDWR,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwd_allowed_r) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_READ_DIR,
+	.allowed = LANDLOCK_ACCESS_FS_READ_FILE,
+	.open_mode = O_RDONLY,
+	/* If LANDLOCK_ACCESS_FS_IOCTL is not handled, all IOCTLs work. */
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwd_allowed_w) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_READ_DIR,
+	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
+	.open_mode = O_WRONLY,
+	/* If LANDLOCK_ACCESS_FS_IOCTL is not handled, all IOCTLs work. */
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_ri_allowed_r) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_READ_FILE,
+	.open_mode = O_RDONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = EACCES,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_wi_allowed_w) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
+	.open_mode = O_WRONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = EACCES,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_di_allowed_d) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_READ_DIR,
+	.open_mode = O_RDWR,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = EACCES,
+	.expected_fionread_result = EACCES,
+	.expected_fs_ioc_zero_range_result = EACCES,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwi_allowed_rw) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
+	.open_mode = O_RDWR,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwi_allowed_r) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_READ_FILE,
+	.open_mode = O_RDONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = EACCES,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwi_allowed_ri) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.open_mode = O_RDONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = 0,
+	.expected_fs_ioc_zero_range_result = EACCES,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwi_allowed_w) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
+	.open_mode = O_WRONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = EACCES,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = EACCES,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(ioctl, handled_rwi_allowed_wi) {
+	/* clang-format on */
+	.handled = LANDLOCK_ACCESS_FS_READ_FILE |
+		   LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_IOCTL,
+	.open_mode = O_WRONLY,
+	.expected_fioqsize_result = 0,
+	.expected_fibmap_result = 0,
+	.expected_fionread_result = EACCES,
+	.expected_fs_ioc_zero_range_result = 0,
+	.expected_fs_ioc_getflags_result = 0,
+};
+
+static int test_fioqsize_ioctl(int fd)
+{
+	size_t sz;
+
+	if (ioctl(fd, FIOQSIZE, &sz) < 0)
+		return errno;
+	return 0;
+}
+
+static int test_fibmap_ioctl(int fd)
+{
+	int blk = 0;
+
+	/*
+	 * We only want to distinguish here whether Landlock already caught it,
+	 * so we treat anything but EACCESS as success.  (It commonly returns
+	 * EPERM when missing CAP_SYS_RAWIO.)
+	 */
+	if (ioctl(fd, FIBMAP, &blk) < 0 && errno == EACCES)
+		return errno;
+	return 0;
+}
+
+static int test_fionread_ioctl(int fd)
+{
+	size_t sz = 0;
+
+	if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)
+		return errno;
+	return 0;
+}
+
+#define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)
+
+static int test_fs_ioc_zero_range_ioctl(int fd)
+{
+	struct space_resv {
+		__s16 l_type;
+		__s16 l_whence;
+		__s64 l_start;
+		__s64 l_len; /* len == 0 means until end of file */
+		__s32 l_sysid;
+		__u32 l_pid;
+		__s32 l_pad[4]; /* reserved area */
+	} reservation = {};
+	/*
+	 * This can fail for various reasons, but we only want to distinguish
+	 * here whether Landlock already caught it, so we treat anything but
+	 * EACCES as success.
+	 */
+	if (ioctl(fd, FS_IOC_ZERO_RANGE, &reservation) < 0 && errno == EACCES)
+		return errno;
+	return 0;
+}
+
+TEST_F_FORK(ioctl, handle_dir_access_file)
+{
+	const int flag = 0;
+	const struct rule rules[] = {
+		{
+			.path = dir_s1d1,
+			.access = variant->allowed,
+		},
+		{},
+	};
+	int file_fd, ruleset_fd;
+
+	/* Enables Landlock. */
+	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
+	ASSERT_LE(0, ruleset_fd);
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	file_fd = open(file1_s1d1, variant->open_mode);
+	ASSERT_LE(0, file_fd);
+
+	/*
+	 * Checks that IOCTL commands in each IOCTL group return the expected
+	 * errors.
+	 */
+	EXPECT_EQ(variant->expected_fioqsize_result,
+		  test_fioqsize_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fibmap_result, test_fibmap_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fionread_result,
+		  test_fionread_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_zero_range_result,
+		  test_fs_ioc_zero_range_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_getflags_result,
+		  test_fs_ioc_getflags_ioctl(file_fd));
+
+	/* Checks that unrestrictable commands are unrestricted. */
+	EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
+	EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
+	EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
+	EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
+
+	ASSERT_EQ(0, close(file_fd));
+}
+
+TEST_F_FORK(ioctl, handle_dir_access_dir)
+{
+	const char *const path = dir_s1d1;
+	const int flag = 0;
+	const struct rule rules[] = {
+		{
+			.path = path,
+			.access = variant->allowed,
+		},
+		{},
+	};
+	int dir_fd, ruleset_fd;
+
+	/* Enables Landlock. */
+	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
+	ASSERT_LE(0, ruleset_fd);
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/*
+	 * Ignore variant->open_mode for this test, as we intend to open a
+	 * directory.  If the directory can not be opened, the variant is
+	 * infeasible to test with an opened directory.
+	 */
+	dir_fd = open(path, O_RDONLY);
+	if (dir_fd < 0)
+		return;
+
+	/*
+	 * Checks that IOCTL commands in each IOCTL group return the expected
+	 * errors.
+	 */
+	EXPECT_EQ(variant->expected_fioqsize_result,
+		  test_fioqsize_ioctl(dir_fd));
+	EXPECT_EQ(variant->expected_fibmap_result, test_fibmap_ioctl(dir_fd));
+	EXPECT_EQ(variant->expected_fionread_result,
+		  test_fionread_ioctl(dir_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_zero_range_result,
+		  test_fs_ioc_zero_range_ioctl(dir_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_getflags_result,
+		  test_fs_ioc_getflags_ioctl(dir_fd));
+
+	/* Checks that unrestrictable commands are unrestricted. */
+	EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));
+	EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));
+	EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));
+	EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));
+
+	ASSERT_EQ(0, close(dir_fd));
+}
+
+TEST_F_FORK(ioctl, handle_file_access_file)
+{
+	const char *const path = file1_s1d1;
+	const int flag = 0;
+	const struct rule rules[] = {
+		{
+			.path = path,
+			.access = variant->allowed,
+		},
+		{},
+	};
+	int file_fd, ruleset_fd;
+
+	if (variant->allowed & LANDLOCK_ACCESS_FS_READ_DIR) {
+		SKIP(return, "LANDLOCK_ACCESS_FS_READ_DIR "
+			     "can not be granted on files");
+	}
+
+	/* Enables Landlock. */
+	ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
+	ASSERT_LE(0, ruleset_fd);
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	file_fd = open(path, variant->open_mode);
+	ASSERT_LE(0, file_fd);
+
+	/*
+	 * Checks that IOCTL commands in each IOCTL group return the expected
+	 * errors.
+	 */
+	EXPECT_EQ(variant->expected_fioqsize_result,
+		  test_fioqsize_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fibmap_result, test_fibmap_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fionread_result,
+		  test_fionread_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_zero_range_result,
+		  test_fs_ioc_zero_range_ioctl(file_fd));
+	EXPECT_EQ(variant->expected_fs_ioc_getflags_result,
+		  test_fs_ioc_getflags_ioctl(file_fd));
+
+	/* Checks that unrestrictable commands are unrestricted. */
+	EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
+	EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
+	EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
+	EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
+
+	ASSERT_EQ(0, close(file_fd));
+}
+
 /* clang-format off */
 FIXTURE(layout1_bind) {};
 /* clang-format on */