* If set, any currently unset opcode will have a deny filter attached
*/
IO_URING_BPF_FILTER_DENY_REST = 1,
+ /*
+ * If set, if kernel and application don't agree on pdu_size for
+ * the given opcode, fail the registration of the filter.
+ */
+ IO_URING_BPF_FILTER_SZ_STRICT = 2,
};
struct io_uring_bpf_filter {
__u32 opcode; /* io_uring opcode to filter */
__u32 flags;
__u32 filter_len; /* number of BPF instructions */
- __u32 resv;
+ __u8 pdu_size; /* expected pdu size for opcode */
+ __u8 resv[3];
__u64 filter_ptr; /* pointer to BPF filter */
__u64 resv2[5];
};
return ERR_PTR(-EBUSY);
}
-#define IO_URING_BPF_FILTER_FLAGS IO_URING_BPF_FILTER_DENY_REST
+#define IO_URING_BPF_FILTER_FLAGS (IO_URING_BPF_FILTER_DENY_REST | \
+ IO_URING_BPF_FILTER_SZ_STRICT)
-int io_register_bpf_filter(struct io_restriction *res,
- struct io_uring_bpf __user *arg)
+static int io_bpf_filter_import(struct io_uring_bpf *reg,
+ struct io_uring_bpf __user *arg)
{
- struct io_bpf_filters *filters, *old_filters = NULL;
- struct io_bpf_filter *filter, *old_filter;
- struct io_uring_bpf reg;
- struct bpf_prog *prog;
- struct sock_fprog fprog;
+ const struct io_issue_def *def;
int ret;
- if (copy_from_user(®, arg, sizeof(reg)))
+ if (copy_from_user(reg, arg, sizeof(*reg)))
return -EFAULT;
- if (reg.cmd_type != IO_URING_BPF_CMD_FILTER)
+ if (reg->cmd_type != IO_URING_BPF_CMD_FILTER)
return -EINVAL;
- if (reg.cmd_flags || reg.resv)
+ if (reg->cmd_flags || reg->resv)
return -EINVAL;
- if (reg.filter.opcode >= IORING_OP_LAST)
+ if (reg->filter.opcode >= IORING_OP_LAST)
return -EINVAL;
- if (reg.filter.flags & ~IO_URING_BPF_FILTER_FLAGS)
+ if (reg->filter.flags & ~IO_URING_BPF_FILTER_FLAGS)
return -EINVAL;
- if (reg.filter.resv)
+ if (!mem_is_zero(reg->filter.resv, sizeof(reg->filter.resv)))
return -EINVAL;
- if (!mem_is_zero(reg.filter.resv2, sizeof(reg.filter.resv2)))
+ if (!mem_is_zero(reg->filter.resv2, sizeof(reg->filter.resv2)))
return -EINVAL;
- if (!reg.filter.filter_len || reg.filter.filter_len > BPF_MAXINSNS)
+ if (!reg->filter.filter_len || reg->filter.filter_len > BPF_MAXINSNS)
return -EINVAL;
+ /* Verify filter size */
+ def = &io_issue_defs[array_index_nospec(reg->filter.opcode, IORING_OP_LAST)];
+
+ /* same size, always ok */
+ ret = 0;
+ if (reg->filter.pdu_size == def->filter_pdu_size)
+ ;
+ /* size differs, fail in strict mode */
+ else if (reg->filter.flags & IO_URING_BPF_FILTER_SZ_STRICT)
+ ret = -EMSGSIZE;
+ /* userspace filter is bigger, always disallow */
+ else if (reg->filter.pdu_size > def->filter_pdu_size)
+ ret = -EMSGSIZE;
+
+ /* copy back kernel filter size */
+ reg->filter.pdu_size = def->filter_pdu_size;
+ if (copy_to_user(&arg->filter, ®->filter, sizeof(reg->filter)))
+ return -EFAULT;
+
+ return ret;
+}
+
+int io_register_bpf_filter(struct io_restriction *res,
+ struct io_uring_bpf __user *arg)
+{
+ struct io_bpf_filters *filters, *old_filters = NULL;
+ struct io_bpf_filter *filter, *old_filter;
+ struct io_uring_bpf reg;
+ struct bpf_prog *prog;
+ struct sock_fprog fprog;
+ int ret;
+
+ ret = io_bpf_filter_import(®, arg);
+ if (ret)
+ return ret;
+
fprog.len = reg.filter.filter_len;
fprog.filter = u64_to_user_ptr(reg.filter.filter_ptr);