Skip to content

Commit be35731

Browse files
committed
io_uring/bpf_filter: pass in expected filter payload size
It's quite possible that opcodes that have payloads attached to them, like IORING_OP_OPENAT/OPENAT2 or IORING_OP_SOCKET, that these paylods can change over time. For example, on the openat/openat2 side, the struct open_how argument is extensible, and could be extended in the future to allow further arguments to be passed in. Allow registration of a cBPF filter to give the size of the filter as seen by userspace. If that filter is for an opcode that takes extra payload data, allow it if the application payload expectation is the same size than the kernels. If that is the case, the kernel supports filtering on the payload that the application expects. If the size differs, the behavior depends on the IO_URING_BPF_FILTER_SZ_STRICT flag: 1) If IO_URING_BPF_FILTER_SZ_STRICT is set and the size expectation differs, fail the attempt to load the filter. 2) If IO_URING_BPF_FILTER_SZ_STRICT isn't set, allow the filter if the userspace pdu size is smaller than what the kernel offers. 3) Regardless if IO_URING_BPF_FILTER_SZ_STRICT, fail loading the filter if the userspace pdu size is bigger than what the kernel supports. An attempt to load a filter due to sizing will error with -EMSGSIZE. For that error, the registration struct will have filter->pdu_size populated with the pdu size that the kernel uses. Reported-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent d21c362 commit be35731

2 files changed

Lines changed: 56 additions & 17 deletions

File tree

include/uapi/linux/io_uring/bpf_filter.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@ enum {
3535
* If set, any currently unset opcode will have a deny filter attached
3636
*/
3737
IO_URING_BPF_FILTER_DENY_REST = 1,
38+
/*
39+
* If set, if kernel and application don't agree on pdu_size for
40+
* the given opcode, fail the registration of the filter.
41+
*/
42+
IO_URING_BPF_FILTER_SZ_STRICT = 2,
3843
};
3944

4045
struct io_uring_bpf_filter {
4146
__u32 opcode; /* io_uring opcode to filter */
4247
__u32 flags;
4348
__u32 filter_len; /* number of BPF instructions */
44-
__u32 resv;
49+
__u8 pdu_size; /* expected pdu size for opcode */
50+
__u8 resv[3];
4551
__u64 filter_ptr; /* pointer to BPF filter */
4652
__u64 resv2[5];
4753
};

io_uring/bpf_filter.c

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -308,36 +308,69 @@ static struct io_bpf_filters *io_bpf_filter_cow(struct io_restriction *src)
308308
return ERR_PTR(-EBUSY);
309309
}
310310

311-
#define IO_URING_BPF_FILTER_FLAGS IO_URING_BPF_FILTER_DENY_REST
311+
#define IO_URING_BPF_FILTER_FLAGS (IO_URING_BPF_FILTER_DENY_REST | \
312+
IO_URING_BPF_FILTER_SZ_STRICT)
312313

313-
int io_register_bpf_filter(struct io_restriction *res,
314-
struct io_uring_bpf __user *arg)
314+
static int io_bpf_filter_import(struct io_uring_bpf *reg,
315+
struct io_uring_bpf __user *arg)
315316
{
316-
struct io_bpf_filters *filters, *old_filters = NULL;
317-
struct io_bpf_filter *filter, *old_filter;
318-
struct io_uring_bpf reg;
319-
struct bpf_prog *prog;
320-
struct sock_fprog fprog;
317+
const struct io_issue_def *def;
321318
int ret;
322319

323-
if (copy_from_user(&reg, arg, sizeof(reg)))
320+
if (copy_from_user(reg, arg, sizeof(*reg)))
324321
return -EFAULT;
325-
if (reg.cmd_type != IO_URING_BPF_CMD_FILTER)
322+
if (reg->cmd_type != IO_URING_BPF_CMD_FILTER)
326323
return -EINVAL;
327-
if (reg.cmd_flags || reg.resv)
324+
if (reg->cmd_flags || reg->resv)
328325
return -EINVAL;
329326

330-
if (reg.filter.opcode >= IORING_OP_LAST)
327+
if (reg->filter.opcode >= IORING_OP_LAST)
331328
return -EINVAL;
332-
if (reg.filter.flags & ~IO_URING_BPF_FILTER_FLAGS)
329+
if (reg->filter.flags & ~IO_URING_BPF_FILTER_FLAGS)
333330
return -EINVAL;
334-
if (reg.filter.resv)
331+
if (!mem_is_zero(reg->filter.resv, sizeof(reg->filter.resv)))
335332
return -EINVAL;
336-
if (!mem_is_zero(reg.filter.resv2, sizeof(reg.filter.resv2)))
333+
if (!mem_is_zero(reg->filter.resv2, sizeof(reg->filter.resv2)))
337334
return -EINVAL;
338-
if (!reg.filter.filter_len || reg.filter.filter_len > BPF_MAXINSNS)
335+
if (!reg->filter.filter_len || reg->filter.filter_len > BPF_MAXINSNS)
339336
return -EINVAL;
340337

338+
/* Verify filter size */
339+
def = &io_issue_defs[array_index_nospec(reg->filter.opcode, IORING_OP_LAST)];
340+
341+
/* same size, always ok */
342+
ret = 0;
343+
if (reg->filter.pdu_size == def->filter_pdu_size)
344+
;
345+
/* size differs, fail in strict mode */
346+
else if (reg->filter.flags & IO_URING_BPF_FILTER_SZ_STRICT)
347+
ret = -EMSGSIZE;
348+
/* userspace filter is bigger, always disallow */
349+
else if (reg->filter.pdu_size > def->filter_pdu_size)
350+
ret = -EMSGSIZE;
351+
352+
/* copy back kernel filter size */
353+
reg->filter.pdu_size = def->filter_pdu_size;
354+
if (copy_to_user(&arg->filter, &reg->filter, sizeof(reg->filter)))
355+
return -EFAULT;
356+
357+
return ret;
358+
}
359+
360+
int io_register_bpf_filter(struct io_restriction *res,
361+
struct io_uring_bpf __user *arg)
362+
{
363+
struct io_bpf_filters *filters, *old_filters = NULL;
364+
struct io_bpf_filter *filter, *old_filter;
365+
struct io_uring_bpf reg;
366+
struct bpf_prog *prog;
367+
struct sock_fprog fprog;
368+
int ret;
369+
370+
ret = io_bpf_filter_import(&reg, arg);
371+
if (ret)
372+
return ret;
373+
341374
fprog.len = reg.filter.filter_len;
342375
fprog.filter = u64_to_user_ptr(reg.filter.filter_ptr);
343376

0 commit comments

Comments
 (0)