Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Daniel Borkmann says: ==================== pull-request: bpf 2017-11-23 The following pull-request contains BPF updates for your *net* tree. The main changes are: 1) Several BPF offloading fixes, from Jakub. Among others: - Limit offload to cls_bpf and XDP program types only. - Move device validation into the driver and don't make any assumptions about the device in the classifier due to shared blocks semantics. - Don't pass offloaded XDP program into the driver when it should be run in native XDP instead. Offloaded ones are not JITed for the host in such cases. - Don't destroy device offload state when moved to another namespace. - Revert dumping offload info into user space for now, since ifindex alone is not sufficient. This will be redone properly for bpf-next tree. 2) Fix test_verifier to avoid using bpf_probe_write_user() helper in test cases, since it's dumping a warning into kernel log which may confuse users when only running tests. Switch to use bpf_trace_printk() instead, from Yonghong. 3) Several fixes for correcting ARG_CONST_SIZE_OR_ZERO semantics before it becomes uabi, from Gianluca. More specifically: - Add a type ARG_PTR_TO_MEM_OR_NULL that is used only by bpf_csum_diff(), where the argument is either a valid pointer or NULL. The subsequent ARG_CONST_SIZE_OR_ZERO then enforces a valid pointer in case of non-0 size or a valid pointer or NULL in case of size 0. Given that, the semantics for ARG_PTR_TO_MEM in combination with ARG_CONST_SIZE_OR_ZERO are now such that in case of size 0, the pointer must always be valid and cannot be NULL. This fix in semantics allows for bpf_probe_read() to drop the recently added size == 0 check in the helper that would become part of uabi otherwise once released. At the same time we can then fix bpf_probe_read_str() and bpf_perf_event_output() to use ARG_CONST_SIZE_OR_ZERO instead of ARG_CONST_SIZE in order to fix recently reported issues by Arnaldo et al, where LLVM optimizes two boundary checks into a single one for unknown variables where the verifier looses track of the variable bounds and thus rejects valid programs otherwise. 4) A fix for the verifier for the case when it detects comparison of two constants where the branch is guaranteed to not be taken at runtime. Verifier will rightfully prune the exploration of such paths, but we still pass the program to JITs, where they would complain about using reserved fields, etc. Track such dead instructions and sanitize them with mov r0,r0. Rejection is not possible since LLVM may generate them for valid C code and doesn't do as much data flow analysis as verifier. For bpf-next we might implement removal of such dead code and adjust branches instead. Fix from Alexei. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
e4be7baba8
|
@ -214,8 +214,14 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,
|
|||
{
|
||||
int err;
|
||||
|
||||
if (prog && !prog->aux->offload)
|
||||
return -EINVAL;
|
||||
if (prog) {
|
||||
struct bpf_dev_offload *offload = prog->aux->offload;
|
||||
|
||||
if (!offload)
|
||||
return -EINVAL;
|
||||
if (offload->netdev != nn->dp.netdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (prog && old_prog) {
|
||||
u8 cap;
|
||||
|
|
|
@ -78,6 +78,7 @@ enum bpf_arg_type {
|
|||
* functions that access data on eBPF program stack
|
||||
*/
|
||||
ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */
|
||||
ARG_PTR_TO_MEM_OR_NULL, /* pointer to valid memory or NULL */
|
||||
ARG_PTR_TO_UNINIT_MEM, /* pointer to memory does not need to be initialized,
|
||||
* helper function must fill all bytes or clear
|
||||
* them in error case.
|
||||
|
@ -334,9 +335,8 @@ extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops;
|
|||
extern const struct bpf_verifier_ops xdp_analyzer_ops;
|
||||
|
||||
struct bpf_prog *bpf_prog_get(u32 ufd);
|
||||
struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type);
|
||||
struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type,
|
||||
struct net_device *netdev);
|
||||
bool attach_drv);
|
||||
struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, int i);
|
||||
void bpf_prog_sub(struct bpf_prog *prog, int i);
|
||||
struct bpf_prog * __must_check bpf_prog_inc(struct bpf_prog *prog);
|
||||
|
@ -425,15 +425,9 @@ static inline struct bpf_prog *bpf_prog_get(u32 ufd)
|
|||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct bpf_prog *bpf_prog_get_type(u32 ufd,
|
||||
enum bpf_prog_type type)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline struct bpf_prog *bpf_prog_get_type_dev(u32 ufd,
|
||||
enum bpf_prog_type type,
|
||||
struct net_device *netdev)
|
||||
bool attach_drv)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
@ -514,9 +508,14 @@ static inline int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu,
|
|||
}
|
||||
#endif /* CONFIG_BPF_SYSCALL */
|
||||
|
||||
static inline struct bpf_prog *bpf_prog_get_type(u32 ufd,
|
||||
enum bpf_prog_type type)
|
||||
{
|
||||
return bpf_prog_get_type_dev(ufd, type, false);
|
||||
}
|
||||
|
||||
int bpf_prog_offload_compile(struct bpf_prog *prog);
|
||||
void bpf_prog_offload_destroy(struct bpf_prog *prog);
|
||||
u32 bpf_prog_offload_ifindex(struct bpf_prog *prog);
|
||||
|
||||
#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
|
||||
int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr);
|
||||
|
|
|
@ -115,7 +115,7 @@ struct bpf_insn_aux_data {
|
|||
struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */
|
||||
};
|
||||
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
|
||||
int converted_op_size; /* the valid value width after perceived conversion */
|
||||
bool seen; /* this insn was processed by the verifier */
|
||||
};
|
||||
|
||||
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
|
||||
|
@ -171,7 +171,7 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env)
|
|||
#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL)
|
||||
int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env);
|
||||
#else
|
||||
int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
|
||||
static inline int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
@ -262,7 +262,7 @@ union bpf_attr {
|
|||
__u32 kern_version; /* checked when prog_type=kprobe */
|
||||
__u32 prog_flags;
|
||||
char prog_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 prog_target_ifindex; /* ifindex of netdev to prep for */
|
||||
__u32 prog_ifindex; /* ifindex of netdev to prep for */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||
|
@ -897,10 +897,6 @@ enum sk_action {
|
|||
|
||||
#define BPF_TAG_SIZE 8
|
||||
|
||||
enum bpf_prog_status {
|
||||
BPF_PROG_STATUS_DEV_BOUND = (1 << 0),
|
||||
};
|
||||
|
||||
struct bpf_prog_info {
|
||||
__u32 type;
|
||||
__u32 id;
|
||||
|
@ -914,8 +910,6 @@ struct bpf_prog_info {
|
|||
__u32 nr_map_ids;
|
||||
__aligned_u64 map_ids;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
__u32 ifindex;
|
||||
__u32 status;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_map_info {
|
||||
|
|
|
@ -14,8 +14,9 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
|
|||
struct net *net = current->nsproxy->net_ns;
|
||||
struct bpf_dev_offload *offload;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (attr->prog_type != BPF_PROG_TYPE_SCHED_CLS &&
|
||||
attr->prog_type != BPF_PROG_TYPE_XDP)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->prog_flags)
|
||||
return -EINVAL;
|
||||
|
@ -28,7 +29,7 @@ int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr)
|
|||
init_waitqueue_head(&offload->verifier_done);
|
||||
|
||||
rtnl_lock();
|
||||
offload->netdev = __dev_get_by_index(net, attr->prog_target_ifindex);
|
||||
offload->netdev = __dev_get_by_index(net, attr->prog_ifindex);
|
||||
if (!offload->netdev) {
|
||||
rtnl_unlock();
|
||||
kfree(offload);
|
||||
|
@ -85,6 +86,10 @@ static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
|
|||
struct bpf_dev_offload *offload = prog->aux->offload;
|
||||
struct netdev_bpf data = {};
|
||||
|
||||
/* Caution - if netdev is destroyed before the program, this function
|
||||
* will be called twice.
|
||||
*/
|
||||
|
||||
data.offload.prog = prog;
|
||||
|
||||
if (offload->verifier_running)
|
||||
|
@ -144,18 +149,6 @@ int bpf_prog_offload_compile(struct bpf_prog *prog)
|
|||
return bpf_prog_offload_translate(prog);
|
||||
}
|
||||
|
||||
u32 bpf_prog_offload_ifindex(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_dev_offload *offload = prog->aux->offload;
|
||||
u32 ifindex;
|
||||
|
||||
rtnl_lock();
|
||||
ifindex = offload->netdev ? offload->netdev->ifindex : 0;
|
||||
rtnl_unlock();
|
||||
|
||||
return ifindex;
|
||||
}
|
||||
|
||||
const struct bpf_prog_ops bpf_offload_prog_ops = {
|
||||
};
|
||||
|
||||
|
@ -169,6 +162,10 @@ static int bpf_offload_notification(struct notifier_block *notifier,
|
|||
|
||||
switch (event) {
|
||||
case NETDEV_UNREGISTER:
|
||||
/* ignore namespace changes */
|
||||
if (netdev->reg_state != NETREG_UNREGISTERING)
|
||||
break;
|
||||
|
||||
list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs,
|
||||
offloads) {
|
||||
if (offload->netdev == netdev)
|
||||
|
|
|
@ -1057,22 +1057,23 @@ struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_inc_not_zero);
|
||||
|
||||
static bool bpf_prog_can_attach(struct bpf_prog *prog,
|
||||
enum bpf_prog_type *attach_type,
|
||||
struct net_device *netdev)
|
||||
static bool bpf_prog_get_ok(struct bpf_prog *prog,
|
||||
enum bpf_prog_type *attach_type, bool attach_drv)
|
||||
{
|
||||
struct bpf_dev_offload *offload = prog->aux->offload;
|
||||
/* not an attachment, just a refcount inc, always allow */
|
||||
if (!attach_type)
|
||||
return true;
|
||||
|
||||
if (prog->type != *attach_type)
|
||||
return false;
|
||||
if (offload && offload->netdev != netdev)
|
||||
if (bpf_prog_is_dev_bound(prog->aux) && !attach_drv)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type,
|
||||
struct net_device *netdev)
|
||||
bool attach_drv)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
struct bpf_prog *prog;
|
||||
|
@ -1080,7 +1081,7 @@ static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type,
|
|||
prog = ____bpf_prog_get(f);
|
||||
if (IS_ERR(prog))
|
||||
return prog;
|
||||
if (attach_type && !bpf_prog_can_attach(prog, attach_type, netdev)) {
|
||||
if (!bpf_prog_get_ok(prog, attach_type, attach_drv)) {
|
||||
prog = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1093,23 +1094,13 @@ out:
|
|||
|
||||
struct bpf_prog *bpf_prog_get(u32 ufd)
|
||||
{
|
||||
return __bpf_prog_get(ufd, NULL, NULL);
|
||||
return __bpf_prog_get(ufd, NULL, false);
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type)
|
||||
{
|
||||
struct bpf_prog *prog = __bpf_prog_get(ufd, &type, NULL);
|
||||
|
||||
if (!IS_ERR(prog))
|
||||
trace_bpf_prog_get_type(prog);
|
||||
return prog;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_get_type);
|
||||
|
||||
struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type,
|
||||
struct net_device *netdev)
|
||||
bool attach_drv)
|
||||
{
|
||||
struct bpf_prog *prog = __bpf_prog_get(ufd, &type, netdev);
|
||||
struct bpf_prog *prog = __bpf_prog_get(ufd, &type, attach_drv);
|
||||
|
||||
if (!IS_ERR(prog))
|
||||
trace_bpf_prog_get_type(prog);
|
||||
|
@ -1118,7 +1109,7 @@ struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type,
|
|||
EXPORT_SYMBOL_GPL(bpf_prog_get_type_dev);
|
||||
|
||||
/* last field in 'union bpf_attr' used by this command */
|
||||
#define BPF_PROG_LOAD_LAST_FIELD prog_target_ifindex
|
||||
#define BPF_PROG_LOAD_LAST_FIELD prog_ifindex
|
||||
|
||||
static int bpf_prog_load(union bpf_attr *attr)
|
||||
{
|
||||
|
@ -1181,7 +1172,7 @@ static int bpf_prog_load(union bpf_attr *attr)
|
|||
atomic_set(&prog->aux->refcnt, 1);
|
||||
prog->gpl_compatible = is_gpl ? 1 : 0;
|
||||
|
||||
if (attr->prog_target_ifindex) {
|
||||
if (attr->prog_ifindex) {
|
||||
err = bpf_prog_offload_init(prog, attr);
|
||||
if (err)
|
||||
goto free_prog;
|
||||
|
@ -1625,11 +1616,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (bpf_prog_is_dev_bound(prog->aux)) {
|
||||
info.status |= BPF_PROG_STATUS_DEV_BOUND;
|
||||
info.ifindex = bpf_prog_offload_ifindex(prog);
|
||||
}
|
||||
|
||||
done:
|
||||
if (copy_to_user(uinfo, &info, info_len) ||
|
||||
put_user(info_len, &uattr->info.info_len))
|
||||
|
|
|
@ -1384,13 +1384,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
|
|||
if (type != expected_type)
|
||||
goto err_type;
|
||||
} else if (arg_type == ARG_PTR_TO_MEM ||
|
||||
arg_type == ARG_PTR_TO_MEM_OR_NULL ||
|
||||
arg_type == ARG_PTR_TO_UNINIT_MEM) {
|
||||
expected_type = PTR_TO_STACK;
|
||||
/* One exception here. In case function allows for NULL to be
|
||||
* passed in as argument, it's a SCALAR_VALUE type. Final test
|
||||
* happens during stack boundary checking.
|
||||
*/
|
||||
if (register_is_null(*reg))
|
||||
if (register_is_null(*reg) &&
|
||||
arg_type == ARG_PTR_TO_MEM_OR_NULL)
|
||||
/* final test in check_stack_boundary() */;
|
||||
else if (!type_is_pkt_pointer(type) &&
|
||||
type != PTR_TO_MAP_VALUE &&
|
||||
|
@ -3825,6 +3827,7 @@ static int do_check(struct bpf_verifier_env *env)
|
|||
return err;
|
||||
|
||||
regs = cur_regs(env);
|
||||
env->insn_aux_data[insn_idx].seen = true;
|
||||
if (class == BPF_ALU || class == BPF_ALU64) {
|
||||
err = check_alu_op(env, insn);
|
||||
if (err)
|
||||
|
@ -4020,6 +4023,7 @@ process_bpf_exit:
|
|||
return err;
|
||||
|
||||
insn_idx++;
|
||||
env->insn_aux_data[insn_idx].seen = true;
|
||||
} else {
|
||||
verbose(env, "invalid BPF_LD mode\n");
|
||||
return -EINVAL;
|
||||
|
@ -4202,6 +4206,7 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,
|
|||
u32 off, u32 cnt)
|
||||
{
|
||||
struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data;
|
||||
int i;
|
||||
|
||||
if (cnt == 1)
|
||||
return 0;
|
||||
|
@ -4211,6 +4216,8 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,
|
|||
memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off);
|
||||
memcpy(new_data + off + cnt - 1, old_data + off,
|
||||
sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1));
|
||||
for (i = off; i < off + cnt - 1; i++)
|
||||
new_data[i].seen = true;
|
||||
env->insn_aux_data = new_data;
|
||||
vfree(old_data);
|
||||
return 0;
|
||||
|
@ -4229,6 +4236,25 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
|
|||
return new_prog;
|
||||
}
|
||||
|
||||
/* The verifier does more data flow analysis than llvm and will not explore
|
||||
* branches that are dead at run time. Malicious programs can have dead code
|
||||
* too. Therefore replace all dead at-run-time code with nops.
|
||||
*/
|
||||
static void sanitize_dead_code(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
|
||||
struct bpf_insn nop = BPF_MOV64_REG(BPF_REG_0, BPF_REG_0);
|
||||
struct bpf_insn *insn = env->prog->insnsi;
|
||||
const int insn_cnt = env->prog->len;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < insn_cnt; i++) {
|
||||
if (aux_data[i].seen)
|
||||
continue;
|
||||
memcpy(insn + i, &nop, sizeof(nop));
|
||||
}
|
||||
}
|
||||
|
||||
/* convert load instructions that access fields of 'struct __sk_buff'
|
||||
* into sequence of instructions that access fields of 'struct sk_buff'
|
||||
*/
|
||||
|
@ -4555,6 +4581,9 @@ skip_full_check:
|
|||
while (!pop_stack(env, NULL, NULL));
|
||||
free_states(env);
|
||||
|
||||
if (ret == 0)
|
||||
sanitize_dead_code(env);
|
||||
|
||||
if (ret == 0)
|
||||
/* program is valid, convert *(u32*)(ctx + off) accesses */
|
||||
ret = convert_ctx_accesses(env);
|
||||
|
|
|
@ -78,16 +78,12 @@ EXPORT_SYMBOL_GPL(trace_call_bpf);
|
|||
|
||||
BPF_CALL_3(bpf_probe_read, void *, dst, u32, size, const void *, unsafe_ptr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (unlikely(size == 0))
|
||||
goto out;
|
||||
int ret;
|
||||
|
||||
ret = probe_kernel_read(dst, unsafe_ptr, size);
|
||||
if (unlikely(ret < 0))
|
||||
memset(dst, 0, size);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -407,7 +403,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto = {
|
|||
.arg2_type = ARG_CONST_MAP_PTR,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
.arg4_type = ARG_PTR_TO_MEM,
|
||||
.arg5_type = ARG_CONST_SIZE,
|
||||
.arg5_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct pt_regs, bpf_pt_regs);
|
||||
|
@ -498,7 +494,7 @@ static const struct bpf_func_proto bpf_probe_read_str_proto = {
|
|||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
|
@ -609,7 +605,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_tp = {
|
|||
.arg2_type = ARG_CONST_MAP_PTR,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
.arg4_type = ARG_PTR_TO_MEM,
|
||||
.arg5_type = ARG_CONST_SIZE,
|
||||
.arg5_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_get_stackid_tp, void *, tp_buff, struct bpf_map *, map,
|
||||
|
|
|
@ -7140,13 +7140,17 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
|
|||
__dev_xdp_attached(dev, bpf_op, NULL))
|
||||
return -EBUSY;
|
||||
|
||||
if (bpf_op == ops->ndo_bpf)
|
||||
prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP,
|
||||
dev);
|
||||
else
|
||||
prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
|
||||
prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP,
|
||||
bpf_op == ops->ndo_bpf);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
if (!(flags & XDP_FLAGS_HW_MODE) &&
|
||||
bpf_prog_is_dev_bound(prog->aux)) {
|
||||
NL_SET_ERR_MSG(extack, "using device-bound program without HW_MODE flag is not supported");
|
||||
bpf_prog_put(prog);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
err = dev_xdp_install(dev, bpf_op, extack, flags, prog);
|
||||
|
|
|
@ -1646,9 +1646,9 @@ static const struct bpf_func_proto bpf_csum_diff_proto = {
|
|||
.gpl_only = false,
|
||||
.pkt_access = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_MEM,
|
||||
.arg1_type = ARG_PTR_TO_MEM_OR_NULL,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_PTR_TO_MEM,
|
||||
.arg3_type = ARG_PTR_TO_MEM_OR_NULL,
|
||||
.arg4_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg5_type = ARG_ANYTHING,
|
||||
};
|
||||
|
|
|
@ -382,15 +382,13 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
|
|||
{
|
||||
struct bpf_prog *fp;
|
||||
char *name = NULL;
|
||||
bool skip_sw;
|
||||
u32 bpf_fd;
|
||||
|
||||
bpf_fd = nla_get_u32(tb[TCA_BPF_FD]);
|
||||
skip_sw = gen_flags & TCA_CLS_FLAGS_SKIP_SW;
|
||||
|
||||
if (gen_flags & TCA_CLS_FLAGS_SKIP_SW)
|
||||
fp = bpf_prog_get_type_dev(bpf_fd, BPF_PROG_TYPE_SCHED_CLS,
|
||||
qdisc_dev(tp->q));
|
||||
else
|
||||
fp = bpf_prog_get_type(bpf_fd, BPF_PROG_TYPE_SCHED_CLS);
|
||||
fp = bpf_prog_get_type_dev(bpf_fd, BPF_PROG_TYPE_SCHED_CLS, skip_sw);
|
||||
if (IS_ERR(fp))
|
||||
return PTR_ERR(fp);
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
@ -230,21 +229,6 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)
|
|||
info->tag[0], info->tag[1], info->tag[2], info->tag[3],
|
||||
info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
|
||||
|
||||
if (info->status & BPF_PROG_STATUS_DEV_BOUND) {
|
||||
jsonw_name(json_wtr, "dev");
|
||||
if (info->ifindex) {
|
||||
char name[IF_NAMESIZE];
|
||||
|
||||
if (!if_indextoname(info->ifindex, name))
|
||||
jsonw_printf(json_wtr, "\"ifindex:%d\"",
|
||||
info->ifindex);
|
||||
else
|
||||
jsonw_printf(json_wtr, "\"%s\"", name);
|
||||
} else {
|
||||
jsonw_printf(json_wtr, "\"unknown\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
|
@ -302,21 +286,6 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
|
|||
|
||||
printf("tag ");
|
||||
fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
|
||||
printf(" ");
|
||||
|
||||
if (info->status & BPF_PROG_STATUS_DEV_BOUND) {
|
||||
printf("dev ");
|
||||
if (info->ifindex) {
|
||||
char name[IF_NAMESIZE];
|
||||
|
||||
if (!if_indextoname(info->ifindex, name))
|
||||
printf("ifindex:%d ", info->ifindex);
|
||||
else
|
||||
printf("%s ", name);
|
||||
} else {
|
||||
printf("unknown ");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (info->load_time) {
|
||||
|
|
|
@ -262,7 +262,7 @@ union bpf_attr {
|
|||
__u32 kern_version; /* checked when prog_type=kprobe */
|
||||
__u32 prog_flags;
|
||||
char prog_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 prog_target_ifindex; /* ifindex of netdev to prep for */
|
||||
__u32 prog_ifindex; /* ifindex of netdev to prep for */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||
|
@ -897,10 +897,6 @@ enum sk_action {
|
|||
|
||||
#define BPF_TAG_SIZE 8
|
||||
|
||||
enum bpf_prog_status {
|
||||
BPF_PROG_STATUS_DEV_BOUND = (1 << 0),
|
||||
};
|
||||
|
||||
struct bpf_prog_info {
|
||||
__u32 type;
|
||||
__u32 id;
|
||||
|
@ -914,8 +910,6 @@ struct bpf_prog_info {
|
|||
__u32 nr_map_ids;
|
||||
__aligned_u64 map_ids;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
__u32 ifindex;
|
||||
__u32 status;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_map_info {
|
||||
|
|
|
@ -4377,11 +4377,10 @@ static struct bpf_test tests[] = {
|
|||
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_write_user),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_trace_printk),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map2 = { 3 },
|
||||
|
@ -4481,14 +4480,12 @@ static struct bpf_test tests[] = {
|
|||
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
|
||||
offsetof(struct test_val, foo)),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_write_user),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_trace_printk),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map2 = { 3 },
|
||||
|
@ -4618,18 +4615,16 @@ static struct bpf_test tests[] = {
|
|||
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_write_user),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_trace_printk),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map2 = { 3 },
|
||||
.errstr = "R2 min value is outside of the array range",
|
||||
.errstr = "R1 min value is outside of the array range",
|
||||
.result = REJECT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
|
@ -4760,20 +4755,18 @@ static struct bpf_test tests[] = {
|
|||
BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
|
||||
BPF_JMP_IMM(BPF_JGT, BPF_REG_3,
|
||||
offsetof(struct test_val, foo), 4),
|
||||
offsetof(struct test_val, foo), 3),
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_write_user),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_trace_printk),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map2 = { 3 },
|
||||
.errstr = "R2 min value is outside of the array range",
|
||||
.errstr = "R1 min value is outside of the array range",
|
||||
.result = REJECT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
|
@ -5638,7 +5631,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 allowed on NULL",
|
||||
"helper access to variable memory: size = 0 allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
|
@ -5652,7 +5645,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size > 0 not allowed on NULL",
|
||||
"helper access to variable memory: size > 0 not allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
|
@ -5670,7 +5663,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 allowed on != NULL stack pointer",
|
||||
"helper access to variable memory: size = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
|
||||
|
@ -5687,7 +5680,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 allowed on != NULL map pointer",
|
||||
"helper access to variable memory: size = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
|
@ -5709,7 +5702,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL stack pointer",
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
|
@ -5734,7 +5727,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL map pointer",
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
|
@ -5757,7 +5750,7 @@ static struct bpf_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL packet pointer",
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL packet pointer (ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, data)),
|
||||
|
@ -5778,6 +5771,105 @@ static struct bpf_test tests[] = {
|
|||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R1 type=inv expected=fp",
|
||||
.result = REJECT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size > 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 1),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "R1 type=inv expected=fp",
|
||||
.result = REJECT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map1 = { 3 },
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
|
||||
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map1 = { 3 },
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: size possible = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)",
|
||||
.insns = {
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
|
||||
BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_probe_read),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map1 = { 3 },
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
|
||||
},
|
||||
{
|
||||
"helper access to variable memory: 8 bytes leak",
|
||||
.insns = {
|
||||
|
|
Loading…
Reference in a new issue