Skip to content

Commit 5bd2182

Browse files
committed
audit,io_uring,io-wq: add some basic audit support to io_uring
This patch adds basic auditing to io_uring operations, regardless of their context. This is accomplished by allocating audit_context structures for the io-wq worker and io_uring SQPOLL kernel threads as well as explicitly auditing the io_uring operations in io_issue_sqe(). Individual io_uring operations can bypass auditing through the "audit_skip" field in the struct io_op_def definition for the operation; although great care must be taken so that security relevant io_uring operations do not bypass auditing; please contact the audit mailing list (see the MAINTAINERS file) with any questions. The io_uring operations are audited using a new AUDIT_URINGOP record, an example is shown below: type=UNKNOWN[1336] msg=audit(1631800225.981:37289): uring_op=19 success=yes exit=0 items=0 ppid=15454 pid=15681 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null) Thanks to Richard Guy Briggs for review and feedback. Signed-off-by: Paul Moore <[email protected]>
1 parent 12c5e81 commit 5bd2182

File tree

6 files changed

+248
-6
lines changed

6 files changed

+248
-6
lines changed

fs/io-wq.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/rculist_nulls.h>
1515
#include <linux/cpu.h>
1616
#include <linux/tracehook.h>
17+
#include <linux/audit.h>
1718

1819
#include "io-wq.h"
1920

@@ -562,6 +563,8 @@ static int io_wqe_worker(void *data)
562563
snprintf(buf, sizeof(buf), "iou-wrk-%d", wq->task->pid);
563564
set_task_comm(current, buf);
564565

566+
audit_alloc_kernel(current);
567+
565568
while (!test_bit(IO_WQ_BIT_EXIT, &wq->state)) {
566569
long ret;
567570

@@ -601,6 +604,7 @@ static int io_wqe_worker(void *data)
601604
io_worker_handle_work(worker);
602605
}
603606

607+
audit_free(current);
604608
io_worker_exit(worker);
605609
return 0;
606610
}

fs/io_uring.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include <linux/pagemap.h>
8080
#include <linux/io_uring.h>
8181
#include <linux/tracehook.h>
82+
#include <linux/audit.h>
8283

8384
#define CREATE_TRACE_POINTS
8485
#include <trace/events/io_uring.h>
@@ -917,6 +918,8 @@ struct io_op_def {
917918
unsigned needs_async_setup : 1;
918919
/* should block plug */
919920
unsigned plug : 1;
921+
/* skip auditing */
922+
unsigned audit_skip : 1;
920923
/* size of async data needed, if any */
921924
unsigned short async_size;
922925
};
@@ -930,6 +933,7 @@ static const struct io_op_def io_op_defs[] = {
930933
.buffer_select = 1,
931934
.needs_async_setup = 1,
932935
.plug = 1,
936+
.audit_skip = 1,
933937
.async_size = sizeof(struct io_async_rw),
934938
},
935939
[IORING_OP_WRITEV] = {
@@ -939,16 +943,19 @@ static const struct io_op_def io_op_defs[] = {
939943
.pollout = 1,
940944
.needs_async_setup = 1,
941945
.plug = 1,
946+
.audit_skip = 1,
942947
.async_size = sizeof(struct io_async_rw),
943948
},
944949
[IORING_OP_FSYNC] = {
945950
.needs_file = 1,
951+
.audit_skip = 1,
946952
},
947953
[IORING_OP_READ_FIXED] = {
948954
.needs_file = 1,
949955
.unbound_nonreg_file = 1,
950956
.pollin = 1,
951957
.plug = 1,
958+
.audit_skip = 1,
952959
.async_size = sizeof(struct io_async_rw),
953960
},
954961
[IORING_OP_WRITE_FIXED] = {
@@ -957,15 +964,20 @@ static const struct io_op_def io_op_defs[] = {
957964
.unbound_nonreg_file = 1,
958965
.pollout = 1,
959966
.plug = 1,
967+
.audit_skip = 1,
960968
.async_size = sizeof(struct io_async_rw),
961969
},
962970
[IORING_OP_POLL_ADD] = {
963971
.needs_file = 1,
964972
.unbound_nonreg_file = 1,
973+
.audit_skip = 1,
974+
},
975+
[IORING_OP_POLL_REMOVE] = {
976+
.audit_skip = 1,
965977
},
966-
[IORING_OP_POLL_REMOVE] = {},
967978
[IORING_OP_SYNC_FILE_RANGE] = {
968979
.needs_file = 1,
980+
.audit_skip = 1,
969981
},
970982
[IORING_OP_SENDMSG] = {
971983
.needs_file = 1,
@@ -983,18 +995,23 @@ static const struct io_op_def io_op_defs[] = {
983995
.async_size = sizeof(struct io_async_msghdr),
984996
},
985997
[IORING_OP_TIMEOUT] = {
998+
.audit_skip = 1,
986999
.async_size = sizeof(struct io_timeout_data),
9871000
},
9881001
[IORING_OP_TIMEOUT_REMOVE] = {
9891002
/* used by timeout updates' prep() */
1003+
.audit_skip = 1,
9901004
},
9911005
[IORING_OP_ACCEPT] = {
9921006
.needs_file = 1,
9931007
.unbound_nonreg_file = 1,
9941008
.pollin = 1,
9951009
},
996-
[IORING_OP_ASYNC_CANCEL] = {},
1010+
[IORING_OP_ASYNC_CANCEL] = {
1011+
.audit_skip = 1,
1012+
},
9971013
[IORING_OP_LINK_TIMEOUT] = {
1014+
.audit_skip = 1,
9981015
.async_size = sizeof(struct io_timeout_data),
9991016
},
10001017
[IORING_OP_CONNECT] = {
@@ -1009,14 +1026,19 @@ static const struct io_op_def io_op_defs[] = {
10091026
},
10101027
[IORING_OP_OPENAT] = {},
10111028
[IORING_OP_CLOSE] = {},
1012-
[IORING_OP_FILES_UPDATE] = {},
1013-
[IORING_OP_STATX] = {},
1029+
[IORING_OP_FILES_UPDATE] = {
1030+
.audit_skip = 1,
1031+
},
1032+
[IORING_OP_STATX] = {
1033+
.audit_skip = 1,
1034+
},
10141035
[IORING_OP_READ] = {
10151036
.needs_file = 1,
10161037
.unbound_nonreg_file = 1,
10171038
.pollin = 1,
10181039
.buffer_select = 1,
10191040
.plug = 1,
1041+
.audit_skip = 1,
10201042
.async_size = sizeof(struct io_async_rw),
10211043
},
10221044
[IORING_OP_WRITE] = {
@@ -1025,39 +1047,50 @@ static const struct io_op_def io_op_defs[] = {
10251047
.unbound_nonreg_file = 1,
10261048
.pollout = 1,
10271049
.plug = 1,
1050+
.audit_skip = 1,
10281051
.async_size = sizeof(struct io_async_rw),
10291052
},
10301053
[IORING_OP_FADVISE] = {
10311054
.needs_file = 1,
1055+
.audit_skip = 1,
10321056
},
10331057
[IORING_OP_MADVISE] = {},
10341058
[IORING_OP_SEND] = {
10351059
.needs_file = 1,
10361060
.unbound_nonreg_file = 1,
10371061
.pollout = 1,
1062+
.audit_skip = 1,
10381063
},
10391064
[IORING_OP_RECV] = {
10401065
.needs_file = 1,
10411066
.unbound_nonreg_file = 1,
10421067
.pollin = 1,
10431068
.buffer_select = 1,
1069+
.audit_skip = 1,
10441070
},
10451071
[IORING_OP_OPENAT2] = {
10461072
},
10471073
[IORING_OP_EPOLL_CTL] = {
10481074
.unbound_nonreg_file = 1,
1075+
.audit_skip = 1,
10491076
},
10501077
[IORING_OP_SPLICE] = {
10511078
.needs_file = 1,
10521079
.hash_reg_file = 1,
10531080
.unbound_nonreg_file = 1,
1081+
.audit_skip = 1,
1082+
},
1083+
[IORING_OP_PROVIDE_BUFFERS] = {
1084+
.audit_skip = 1,
1085+
},
1086+
[IORING_OP_REMOVE_BUFFERS] = {
1087+
.audit_skip = 1,
10541088
},
1055-
[IORING_OP_PROVIDE_BUFFERS] = {},
1056-
[IORING_OP_REMOVE_BUFFERS] = {},
10571089
[IORING_OP_TEE] = {
10581090
.needs_file = 1,
10591091
.hash_reg_file = 1,
10601092
.unbound_nonreg_file = 1,
1093+
.audit_skip = 1,
10611094
},
10621095
[IORING_OP_SHUTDOWN] = {
10631096
.needs_file = 1,
@@ -6591,6 +6624,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
65916624
if ((req->flags & REQ_F_CREDS) && req->creds != current_cred())
65926625
creds = override_creds(req->creds);
65936626

6627+
if (!io_op_defs[req->opcode].audit_skip)
6628+
audit_uring_entry(req->opcode);
6629+
65946630
switch (req->opcode) {
65956631
case IORING_OP_NOP:
65966632
ret = io_nop(req, issue_flags);
@@ -6706,6 +6742,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
67066742
break;
67076743
}
67086744

6745+
if (!io_op_defs[req->opcode].audit_skip)
6746+
audit_uring_exit(!ret, ret);
6747+
67096748
if (creds)
67106749
revert_creds(creds);
67116750
if (ret)
@@ -7360,6 +7399,8 @@ static int io_sq_thread(void *data)
73607399
set_cpus_allowed_ptr(current, cpu_online_mask);
73617400
current->flags |= PF_NO_SETAFFINITY;
73627401

7402+
audit_alloc_kernel(current);
7403+
73637404
mutex_lock(&sqd->lock);
73647405
while (1) {
73657406
bool cap_entries, sqt_spin = false;
@@ -7425,6 +7466,8 @@ static int io_sq_thread(void *data)
74257466
io_run_task_work();
74267467
mutex_unlock(&sqd->lock);
74277468

7469+
audit_free(current);
7470+
74287471
complete(&sqd->exited);
74297472
do_exit(0);
74307473
}

include/linux/audit.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
286286
/* These are defined in auditsc.c */
287287
/* Public API */
288288
extern int audit_alloc(struct task_struct *task);
289+
extern int audit_alloc_kernel(struct task_struct *task);
289290
extern void __audit_free(struct task_struct *task);
291+
extern void __audit_uring_entry(u8 op);
292+
extern void __audit_uring_exit(int success, long code);
290293
extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
291294
unsigned long a2, unsigned long a3);
292295
extern void __audit_syscall_exit(int ret_success, long ret_value);
@@ -323,6 +326,21 @@ static inline void audit_free(struct task_struct *task)
323326
if (unlikely(task->audit_context))
324327
__audit_free(task);
325328
}
329+
static inline void audit_uring_entry(u8 op)
330+
{
331+
/*
332+
* We intentionally check audit_context() before audit_enabled as most
333+
* Linux systems (as of ~2021) rely on systemd which forces audit to
334+
* be enabled regardless of the user's audit configuration.
335+
*/
336+
if (unlikely(audit_context() && audit_enabled))
337+
__audit_uring_entry(op);
338+
}
339+
static inline void audit_uring_exit(int success, long code)
340+
{
341+
if (unlikely(!audit_dummy_context()))
342+
__audit_uring_exit(success, code);
343+
}
326344
static inline void audit_syscall_entry(int major, unsigned long a0,
327345
unsigned long a1, unsigned long a2,
328346
unsigned long a3)
@@ -554,8 +572,16 @@ static inline int audit_alloc(struct task_struct *task)
554572
{
555573
return 0;
556574
}
575+
static inline int audit_alloc_kernel(struct task_struct *task)
576+
{
577+
return 0;
578+
}
557579
static inline void audit_free(struct task_struct *task)
558580
{ }
581+
static inline void audit_uring_entry(u8 op)
582+
{ }
583+
static inline void audit_uring_exit(int success, long code)
584+
{ }
559585
static inline void audit_syscall_entry(int major, unsigned long a0,
560586
unsigned long a1, unsigned long a2,
561587
unsigned long a3)

include/uapi/linux/audit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
#define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */
119119
#define AUDIT_BPF 1334 /* BPF subsystem */
120120
#define AUDIT_EVENT_LISTENER 1335 /* Task joined multicast read socket */
121+
#define AUDIT_URINGOP 1336 /* io_uring operation */
121122

122123
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
123124
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */

kernel/audit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ struct audit_context {
103103
enum {
104104
AUDIT_CTX_UNUSED, /* audit_context is currently unused */
105105
AUDIT_CTX_SYSCALL, /* in use by syscall */
106+
AUDIT_CTX_URING, /* in use by io_uring */
106107
} context;
107108
enum audit_state state, current_state;
108109
unsigned int serial; /* serial number for record */
109110
int major; /* syscall number */
111+
int uring_op; /* uring operation */
110112
struct timespec64 ctime; /* time of syscall entry */
111113
unsigned long argv[4]; /* syscall arguments */
112114
long return_code;/* syscall return code */

0 commit comments

Comments
 (0)