@@ -154,19 +154,22 @@ struct bpf_prog;
struct bpf_verifier_ops {
/* return eBPF function prototype for verification */
- const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id);
+ const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype);
/* return true if 'size' wide access at offset 'off' within bpf_context
* with 'type' (read or write) is allowed
*/
bool (*is_valid_access)(int off, int size, enum bpf_access_type type,
- enum bpf_reg_type *reg_type);
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype);
int (*gen_prologue)(struct bpf_insn *insn, bool direct_write,
const struct bpf_prog *prog);
u32 (*convert_ctx_access)(enum bpf_access_type type,
const struct bpf_insn *src,
struct bpf_insn *dst,
struct bpf_prog *prog);
+ bool (*is_valid_subtype)(union bpf_prog_subtype *prog_subtype);
};
struct bpf_prog_type_list {
@@ -417,6 +417,7 @@ struct bpf_prog {
enum bpf_prog_type type; /* Type of BPF program */
u32 len; /* Number of filter blocks */
u8 tag[BPF_TAG_SIZE];
+ union bpf_prog_subtype subtype; /* For fine-grained verifications */
struct bpf_prog_aux *aux; /* Auxiliary fields */
struct sock_fprog_kern *orig_prog; /* Original BPF program */
unsigned int (*bpf_func)(const void *ctx,
@@ -145,6 +145,15 @@ enum bpf_attach_type {
*/
#define BPF_F_NO_COMMON_LRU (1U << 1)
+union bpf_prog_subtype {
+ struct {
+ __u32 version; /* cf. documentation */
+ __u32 event; /* enum landlock_subtype_event */
+ __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+ __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
+ } landlock_rule;
+} __attribute__((aligned(8)));
+
union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */
@@ -173,6 +182,7 @@ union bpf_attr {
__u32 log_size; /* size of user buffer */
__aligned_u64 log_buf; /* user supplied buffer */
__u32 kern_version; /* checked when prog_type=kprobe */
+ union bpf_prog_subtype prog_subtype;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -628,7 +628,7 @@ static void fixup_bpf_calls(struct bpf_prog *prog)
continue;
}
- fn = prog->aux->ops->get_func_proto(insn->imm);
+ fn = prog->aux->ops->get_func_proto(insn->imm, &prog->subtype);
/* all functions that have prototype and verifier allowed
* programs to call them, must be real in-kernel functions
*/
@@ -827,7 +827,7 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type)
EXPORT_SYMBOL_GPL(bpf_prog_get_type);
/* last field in 'union bpf_attr' used by this command */
-#define BPF_PROG_LOAD_LAST_FIELD kern_version
+#define BPF_PROG_LOAD_LAST_FIELD prog_subtype
static int bpf_prog_load(union bpf_attr *attr)
{
@@ -885,6 +885,7 @@ static int bpf_prog_load(union bpf_attr *attr)
err = find_prog_type(type, prog);
if (err < 0)
goto free_prog;
+ prog->subtype = attr->prog_subtype;
/* run eBPF verifier */
err = bpf_check(&prog, attr);
@@ -740,7 +740,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int off, int size,
return 0;
if (env->prog->aux->ops->is_valid_access &&
- env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) {
+ env->prog->aux->ops->is_valid_access(off, size, t, reg_type,
+ &env->prog->subtype)) {
/* remember the offset of last byte accessed in ctx */
if (env->prog->aux->max_ctx_offset < off + size)
env->prog->aux->max_ctx_offset = off + size;
@@ -1290,7 +1291,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id)
}
if (env->prog->aux->ops->get_func_proto)
- fn = env->prog->aux->ops->get_func_proto(func_id);
+ fn = env->prog->aux->ops->get_func_proto(func_id,
+ &env->prog->subtype);
if (!fn) {
verbose("unknown func %s#%d\n", func_id_name(func_id), func_id);
@@ -3261,6 +3263,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
struct bpf_verifier_env *env;
int ret = -EINVAL;
+ if ((*prog)->aux->ops->is_valid_subtype &&
+ !(*prog)->aux->ops->is_valid_subtype(&(*prog)->subtype))
+ return -EINVAL;
+
/* 'struct bpf_verifier_env' can be global, but since it's not small,
* allocate/free it every time bpf_check() is called
*/
@@ -469,7 +469,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id)
}
}
-static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -483,7 +484,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
/* bpf+kprobe programs can access fields of 'struct pt_regs' */
static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (off < 0 || off >= sizeof(struct pt_regs))
return false;
@@ -558,7 +560,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
.arg3_type = ARG_ANYTHING,
};
-static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
+static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -571,7 +574,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
}
static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
return false;
@@ -595,7 +599,8 @@ static struct bpf_prog_type_list tracepoint_tl __ro_after_init = {
};
static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (off < 0 || off >= sizeof(struct bpf_perf_event_data))
return false;
@@ -2600,7 +2600,8 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = {
};
static const struct bpf_func_proto *
-bpf_base_func_proto(enum bpf_func_id func_id)
+bpf_base_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
@@ -2628,18 +2629,20 @@ bpf_base_func_proto(enum bpf_func_id func_id)
}
static const struct bpf_func_proto *
-sk_filter_func_proto(enum bpf_func_id func_id)
+sk_filter_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_load_bytes:
return &bpf_skb_load_bytes_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}
static const struct bpf_func_proto *
-tc_cls_act_func_proto(enum bpf_func_id func_id)
+tc_cls_act_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
@@ -2693,12 +2696,13 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_skb_under_cgroup:
return &bpf_skb_under_cgroup_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}
static const struct bpf_func_proto *
-xdp_func_proto(enum bpf_func_id func_id)
+xdp_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_perf_event_output:
@@ -2708,23 +2712,25 @@ xdp_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_xdp_adjust_head:
return &bpf_xdp_adjust_head_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}
static const struct bpf_func_proto *
-cg_skb_func_proto(enum bpf_func_id func_id)
+cg_skb_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_load_bytes:
return &bpf_skb_load_bytes_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}
static const struct bpf_func_proto *
-lwt_inout_func_proto(enum bpf_func_id func_id)
+lwt_inout_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_load_bytes:
@@ -2746,12 +2752,13 @@ lwt_inout_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_skb_under_cgroup:
return &bpf_skb_under_cgroup_proto;
default:
- return bpf_base_func_proto(func_id);
+ return bpf_base_func_proto(func_id, prog_subtype);
}
}
static const struct bpf_func_proto *
-lwt_xmit_func_proto(enum bpf_func_id func_id)
+lwt_xmit_func_proto(enum bpf_func_id func_id,
+ union bpf_prog_subtype *prog_subtype)
{
switch (func_id) {
case BPF_FUNC_skb_get_tunnel_key:
@@ -2781,7 +2788,7 @@ lwt_xmit_func_proto(enum bpf_func_id func_id)
case BPF_FUNC_set_hash_invalid:
return &bpf_set_hash_invalid_proto;
default:
- return lwt_inout_func_proto(func_id);
+ return lwt_inout_func_proto(func_id, prog_subtype);
}
}
@@ -2811,7 +2818,8 @@ static bool __is_valid_access(int off, int size)
static bool sk_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
switch (off) {
case offsetof(struct __sk_buff, tc_classid):
@@ -2835,7 +2843,8 @@ static bool sk_filter_is_valid_access(int off, int size,
static bool lwt_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
switch (off) {
case offsetof(struct __sk_buff, tc_classid):
@@ -2868,7 +2877,8 @@ static bool lwt_is_valid_access(int off, int size,
static bool sock_filter_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -2931,7 +2941,8 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write,
static bool tc_cls_act_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE) {
switch (off) {
@@ -2973,7 +2984,8 @@ static bool __is_valid_xdp_access(int off, int size)
static bool xdp_is_valid_access(int off, int size,
enum bpf_access_type type,
- enum bpf_reg_type *reg_type)
+ enum bpf_reg_type *reg_type,
+ union bpf_prog_subtype *prog_subtype)
{
if (type == BPF_WRITE)
return false;
@@ -29,6 +29,7 @@
static char license[128];
static int kern_version;
+static union bpf_prog_subtype subtype = {};
static bool processed_sec[128];
char bpf_log_buf[BPF_LOG_BUF_SIZE];
int map_fd[MAX_MAPS];
@@ -98,7 +99,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
}
fd = bpf_load_program(prog_type, prog, insns_cnt, license, kern_version,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, &subtype);
if (fd < 0) {
printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf);
return -1;
@@ -62,7 +62,7 @@ static int bpf_prog_create(const char *object)
} else {
return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER,
insns, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}
}
@@ -60,7 +60,7 @@ static int test_sock(void)
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt,
- "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+ "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
if (prog_fd < 0) {
printf("failed to load prog '%s'\n", strerror(errno));
goto cleanup;
@@ -72,7 +72,7 @@ static int prog_load(int map_fd, int verdict)
return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
prog, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}
static int usage(const char *argv0)
@@ -45,7 +45,7 @@ static int prog_load(int verdict)
ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
prog, insns_cnt, "GPL", 0,
- bpf_log_buf, BPF_LOG_BUF_SIZE);
+ bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
if (ret < 0) {
log_err("Loading program");
@@ -38,7 +38,7 @@ static int prog_load(int idx)
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
return bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt,
- "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
+ "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE, NULL);
}
static int usage(const char *argv0)
@@ -145,6 +145,15 @@ enum bpf_attach_type {
*/
#define BPF_F_NO_COMMON_LRU (1U << 1)
+union bpf_prog_subtype {
+ struct {
+ __u32 version; /* cf. documentation */
+ __u32 event; /* enum landlock_subtype_event */
+ __aligned_u64 ability; /* LANDLOCK_SUBTYPE_ABILITY_* */
+ __aligned_u64 option; /* LANDLOCK_SUBTYPE_OPTION_* */
+ } landlock_rule;
+} __attribute__((aligned(8)));
+
union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */
@@ -173,6 +182,7 @@ union bpf_attr {
__u32 log_size; /* size of user buffer */
__aligned_u64 log_buf; /* user supplied buffer */
__u32 kern_version; /* checked when prog_type=kprobe */
+ union bpf_prog_subtype prog_subtype;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -71,10 +71,12 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
- __u32 kern_version, char *log_buf, size_t log_buf_sz)
+ __u32 kern_version, char *log_buf, size_t log_buf_sz,
+ union bpf_prog_subtype *subtype)
{
int fd;
union bpf_attr attr;
+ union bpf_prog_subtype st_none = {};
bzero(&attr, sizeof(attr));
attr.prog_type = type;
@@ -85,6 +87,7 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
attr.log_size = 0;
attr.log_level = 0;
attr.kern_version = kern_version;
+ attr.prog_subtype = subtype ? *subtype : st_none;
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd >= 0 || !log_buf || !log_buf_sz)
@@ -32,7 +32,7 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
__u32 kern_version, char *log_buf,
- size_t log_buf_sz);
+ size_t log_buf_sz, union bpf_prog_subtype *subtype);
int bpf_map_update_elem(int fd, const void *key, const void *value,
__u64 flags);
@@ -975,7 +975,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
ret = bpf_load_program(type, insns, insns_cnt, license,
- kern_version, log_buf, BPF_LOG_BUF_SIZE);
+ kern_version, log_buf, BPF_LOG_BUF_SIZE, NULL);
if (ret >= 0) {
*pfd = ret;
@@ -1002,7 +1002,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
insns_cnt, license, kern_version,
- NULL, 0);
+ NULL, 0, NULL);
if (fd >= 0) {
close(fd);
ret = -LIBBPF_ERRNO__PROGTYPE;
@@ -266,7 +266,7 @@ static int check_env(void)
err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
sizeof(insns) / sizeof(insns[0]),
- license, kver_int, NULL, 0);
+ license, kver_int, NULL, 0, NULL);
if (err < 0) {
pr_err("Missing basic BPF support, skip this test: %s\n",
strerror(errno));
@@ -57,7 +57,7 @@ static int bpf_try_load_prog(int insns, int fd_map,
bpf_filler(insns, fd_map);
fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0,
- NULL, 0);
+ NULL, 0, NULL);
assert(fd_prog > 0);
if (fd_map > 0)
bpf_filler(insns, 0);
@@ -51,6 +51,7 @@ struct bpf_test {
REJECT
} result, result_unpriv;
enum bpf_prog_type prog_type;
+ union bpf_prog_subtype prog_subtype;
};
/* Note we want this to be 64 bit aligned so that the end of our array is
@@ -4539,7 +4540,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
prog, prog_len, "GPL", 0, bpf_vlog,
- sizeof(bpf_vlog));
+ sizeof(bpf_vlog), &test->prog_subtype);
expected_ret = unpriv && test->result_unpriv != UNDEF ?
test->result_unpriv : test->result;
The goal of the program subtype is to be able to have different static fine-grained verifications for a unique program type. The struct bpf_verifier_ops gets a new optional function: is_valid_subtype(). This new verifier is called at the beginning of the eBPF program verification to check if the (optional) program subtype is valid. For now, only Landlock eBPF programs are using a program subtype (see next commit) but this could be used by other program types in the future. Changes since v4: * replace the "status" field with "version" (more generic) * replace the "access" field with "ability" (less confusing) Changes since v3: * remove the "origin" field * add an "option" field * cleanup comments Signed-off-by: Mickaël Salaün <mic@digikod.net> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: David S. Miller <davem@davemloft.net> Link: https://lkml.kernel.org/r/20160827205559.GA43880@ast-mbp.thefacebook.com --- include/linux/bpf.h | 7 +++-- include/linux/filter.h | 1 + include/uapi/linux/bpf.h | 10 ++++++ kernel/bpf/syscall.c | 5 +-- kernel/bpf/verifier.c | 10 ++++-- kernel/trace/bpf_trace.c | 15 ++++++--- net/core/filter.c | 48 ++++++++++++++++++----------- samples/bpf/bpf_load.c | 3 +- samples/bpf/fds_example.c | 2 +- samples/bpf/sock_example.c | 2 +- samples/bpf/test_cgrp2_attach.c | 2 +- samples/bpf/test_cgrp2_attach2.c | 2 +- samples/bpf/test_cgrp2_sock.c | 2 +- tools/include/uapi/linux/bpf.h | 10 ++++++ tools/lib/bpf/bpf.c | 5 ++- tools/lib/bpf/bpf.h | 2 +- tools/lib/bpf/libbpf.c | 4 +-- tools/perf/tests/bpf.c | 2 +- tools/testing/selftests/bpf/test_tag.c | 2 +- tools/testing/selftests/bpf/test_verifier.c | 3 +- 20 files changed, 95 insertions(+), 42 deletions(-)