From feb5069f67f1fe625ea265aaaa891822625594bc Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Wed, 29 Mar 2023 14:45:48 -0300 Subject: [PATCH] tests: add io_uring regression tests Note that the tests add the dependency of liburing. Signed-off-by: Georgia Garcia --- .gitignore | 1 + tests/regression/apparmor/Makefile | 4 + tests/regression/apparmor/io_uring.c | 204 +++++++++++++++++++++++++ tests/regression/apparmor/io_uring.sh | 83 ++++++++++ tests/regression/apparmor/mkprofile.pl | 18 +++ 5 files changed, 310 insertions(+) create mode 100644 tests/regression/apparmor/io_uring.c create mode 100755 tests/regression/apparmor/io_uring.sh diff --git a/.gitignore b/.gitignore index b7c913981..9b3ec5c04 100644 --- a/.gitignore +++ b/.gitignore @@ -256,6 +256,7 @@ tests/regression/apparmor/fd_inheritance tests/regression/apparmor/fd_inheritor tests/regression/apparmor/fork tests/regression/apparmor/introspect +tests/regression/apparmor/io_uring tests/regression/apparmor/link tests/regression/apparmor/link_subset tests/regression/apparmor/mkdir diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile index 644ed416b..bc98e94b0 100644 --- a/tests/regression/apparmor/Makefile +++ b/tests/regression/apparmor/Makefile @@ -104,6 +104,7 @@ SRC=access.c \ fd_inheritance.c \ fd_inheritor.c \ fork.c \ + io_uring.c \ link.c \ link_subset.c \ mmap.c \ @@ -354,6 +355,9 @@ userns_setns: userns_setns.c userns.h mount: mount.c ${CC} ${CFLAGS} -std=gnu99 ${LDFLAGS} $^ -o $@ ${LDLIBS} +io_uring: io_uring.c + ${CC} ${CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS} -luring + build-dep: @if [ `whoami` = "root" ] ;\ then \ diff --git a/tests/regression/apparmor/io_uring.c b/tests/regression/apparmor/io_uring.c new file mode 100644 index 000000000..b9fb7f5a7 --- /dev/null +++ b/tests/regression/apparmor/io_uring.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2023 Canonical, Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact Canonical Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_FILENAME "/tmp/io_uring_test" +#define DEFAULT_UID 1000 + +static int no_personality; + +static int open_file(struct io_uring *ring, int cred_id, char *filename) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret, i, to_submit = 1; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + fprintf(stderr, "FAIL - could not get sqe.\n"); + return 1; + } + io_uring_prep_openat(sqe, -1, filename, O_RDONLY, 0); + sqe->user_data = 1; + + if (cred_id != -1) + sqe->personality = cred_id; + + ret = io_uring_submit(ring); + if (ret != to_submit) { + fprintf(stderr, "FAIL - could not submit: %s\n", strerror(-ret)); + goto err; + } + + for (i = 0; i < to_submit; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + fprintf(stderr, "FAIL - wait cqe failed %s\n", strerror(-ret)); + goto err; + } + + ret = cqe->res; + io_uring_cqe_seen(ring, cqe); + } +err: + return ret; +} + +static int test_personality(struct io_uring *ring, char *filename, uid_t uid) +{ + int ret, cred_id; + ret = io_uring_register_personality(ring); + if (ret < 0) { + if (ret == -EINVAL) { + no_personality = 1; + goto out; + } + fprintf(stderr, "FAIL - could not register personality: %s\n", strerror(-ret)); + goto err; + } + cred_id = ret; + + /* create file only owner can open */ + ret = open(filename, O_RDONLY | O_CREAT, 0600); + if (ret < 0) { + perror("open"); + goto err; + } + close(ret); + + /* verify we can open it */ + ret = open_file(ring, -1, filename); + if (ret < 0) { + fprintf(stderr, "FAIL - root could not open file: %d\n", ret); + goto err; + } + + if (seteuid(uid) < 0) { + fprintf(stdout, "FAIL - could not switch to uid %u\n", uid); + goto out; + } + + /* verify we can't open it with current credentials */ + ret = open_file(ring, -1, filename); + if (ret != -EACCES) { + fprintf(stderr, "FAIL - opened with regular credential: %d\n", ret); + goto err; + } + + /* verify we can open with registered credentials */ + ret = open_file(ring, cred_id, filename); + if (ret < 0) { + fprintf(stderr, "FAIL - could not open with registered credentials: %d\n", ret); + goto err; + } + close(ret); + + if (seteuid(0)) + perror("FAIL - seteuid"); + + ret = io_uring_unregister_personality(ring, cred_id); + if (ret) { + fprintf(stderr, "FAIL - could not unregister personality: %s\n", + strerror(-ret)); + goto err; + } + +out: + unlink(filename); + return 0; +err: + unlink(filename); + return 1; +} + +static void usage(char *pname) +{ + fprintf(stderr, "Usage: %s [options]\n", pname); + fprintf(stderr, "Options can be:\n"); + fprintf(stderr, " -s create ring using IORING_SETUP_SQPOLL\n"); + fprintf(stderr, " -o use io_uring personality to open a file\n"); + fprintf(stderr, " -u specify UID for option -s (default is %d)\n", DEFAULT_UID); + fprintf(stderr, " -f specify file opened by option -s (default is %s)\n", DEFAULT_FILENAME); + exit(EXIT_FAILURE); +} + +enum op { + SQPOLL, + OVERRIDE_CREDS, + INVALID_OP, +}; + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int opt, ret = 0, op = INVALID_OP; + char *filename = DEFAULT_FILENAME; + uid_t uid = DEFAULT_UID; + + while ((opt = getopt(argc, argv, "sou:f:")) != -1) { + switch (opt) { + case 's': op = SQPOLL; break; + case 'o': op = OVERRIDE_CREDS; break; + case 'u': uid = atoi(optarg); break; + case 'f': filename = optarg; break; + default: usage(argv[0]); + } + } + + if (op == INVALID_OP) { + printf("FAIL - operation not selected\n"); + return 1; + } + + if (op == SQPOLL) { + ret = io_uring_queue_init(8, &ring, IORING_SETUP_SQPOLL); + if (ret) { + fprintf(stderr, "FAIL - failed to create sqpoll ring: %s\n", + strerror(-ret)); + return 1; + } + io_uring_queue_exit(&ring); + } + + if (op == OVERRIDE_CREDS) { + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "FAIL - failed to create override_creds ring: %s\n", + strerror(-ret)); + return 1; + } + + ret = test_personality(&ring, filename, uid); + if (no_personality) { + /* personality was added in kernel 5.6 */ + printf("Personalities not supported, skipping...\n"); + } else if (ret) { + fprintf(stderr, "FAIL - override_creds failed\n"); + return ret; + } + io_uring_queue_exit(&ring); + } + + printf("PASS\n"); + return 0; +} diff --git a/tests/regression/apparmor/io_uring.sh b/tests/regression/apparmor/io_uring.sh new file mode 100755 index 000000000..8f0feec9d --- /dev/null +++ b/tests/regression/apparmor/io_uring.sh @@ -0,0 +1,83 @@ +#! /bin/bash +#Copyright (C) 2023 Canonical, Ltd. +# +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License as +#published by the Free Software Foundation, version 2 of the +#License. + +#=NAME io_uring +#=DESCRIPTION +# This test verifies if mediation of io_uring is working +#=END + +pwd=`dirname $0` +pwd=`cd $pwd ; /bin/pwd` + +bin=$pwd + +. $bin/prologue.inc + +requires_kernel_features io_uring +requires_parser_support "io_uring," + +settest io_uring + +uid=1000 +file=$tmpdir/io_uring_test +label=$bin/io_uring + +required_perms="$file:rw cap:setuid cap:ipc_lock" + +do_test() +{ + local desc="IO_URING ($1)" + shift + runchecktest "$desc" "$@" +} + +do_tests() +{ + prefix=$1 + expect_sqpoll=$2 + expect_override_creds=$3 + + do_test "$prefix - test sqpoll" $expect_sqpoll -s + do_test "$prefix - test override_creds" $expect_override_creds -o -u $uid -f $file +} + +# make sure it works unconfined +do_tests "unconfined" pass pass + +genprofile $required_perms +do_tests "no perms" fail fail + +genprofile $required_perms "qual=deny:io_uring" +do_tests "deny perms" fail fail + +genprofile $required_perms "io_uring" +do_tests "generic perms" pass pass + +genprofile $required_perms "io_uring:sqpoll" +do_tests "only sqpoll perm" pass fail + +genprofile $required_perms "io_uring:override_creds" +do_tests "only override_creds perm" fail pass + +genprofile $required_perms "io_uring:(sqpoll, override_creds)" +do_tests "explicit perms" pass pass + +genprofile $required_perms "io_uring:sqpoll:label=$label" +do_tests "specify label without override_creds perm" pass fail + +genprofile $required_perms "io_uring:label=$label" +do_tests "all perms specify label" pass pass + +genprofile $required_perms "io_uring:(sqpoll, override_creds):label=$label" +do_tests "specify perms specify label" pass pass + +genprofile $required_perms "io_uring:override_creds:label=$label" +do_tests "specify label" fail pass + +genprofile $required_perms "io_uring:override_creds:label=/foo" +do_tests "invalid label" fail fail diff --git a/tests/regression/apparmor/mkprofile.pl b/tests/regression/apparmor/mkprofile.pl index 870290e99..f3ccba04b 100755 --- a/tests/regression/apparmor/mkprofile.pl +++ b/tests/regression/apparmor/mkprofile.pl @@ -443,6 +443,22 @@ sub gen_mqueue($@) { } } +sub gen_io_uring($@) { + my ($rule, $qualifier) = @_; + my @rules = split (/:/, $rule); + if (@rules == 2) { + if ($rules[1] =~ /^ALL$/) { + push (@{$output_rules{$hat}}, " ${qualifier}io_uring,\n"); + } else { + push (@{$output_rules{$hat}}, " ${qualifier}io_uring $rules[1],\n"); + } + } elsif (@rules == 3) { + push (@{$output_rules{$hat}}, " ${qualifier}io_uring $rules[1] $rules[2],\n"); + } else { + (!$nowarn) && print STDERR "Warning: invalid io_uring description '$rule', ignored\n"; + } +} + sub emit_flags($) { my $hat = shift; @@ -514,6 +530,8 @@ sub gen_from_args() { gen_path($rule); } elsif ($rule =~ /^mqueue:/) { gen_mqueue($rule, $qualifier); + } elsif ($rule =~ /^io_uring:/) { + gen_io_uring($rule, $qualifier); } else { gen_file($rule, $qualifier); }