Commit 63d7c880 authored by David S. Miller's avatar David S. Miller

Merge branch 'bpf-misc-helper-verifier-improvements'

Daniel Borkmann says:

====================
Misc BPF helper/verifier improvements

Miscellanous improvements I still had in my queue, it adds a new
bpf_skb_adjust_room() helper for cls_bpf, exports to fdinfo whether
tail call array owner is JITed, so iproute2 error reporting can be
improved on that regard, a small cleanup and extension to trace
printk, two verifier patches, one to make the code around narrower
ctx access a bit more straight forward and one to allow for imm += x
operations, that we've seen LLVM generating and the verifier currently
rejecting. We've included the patch 6 given it's rather small and
we ran into it from LLVM side, it would be great if it could be
queued for stable as well after the merge window. Last but not least,
test cases are added also related to imm alu improvement.

Thanks a lot!
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a68491f8 6d191ed4
...@@ -156,9 +156,14 @@ struct bpf_prog; ...@@ -156,9 +156,14 @@ struct bpf_prog;
struct bpf_insn_access_aux { struct bpf_insn_access_aux {
enum bpf_reg_type reg_type; enum bpf_reg_type reg_type;
int ctx_field_size; int ctx_field_size;
int converted_op_size;
}; };
static inline void
bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)
{
aux->ctx_field_size = size;
}
struct bpf_verifier_ops { struct bpf_verifier_ops {
/* return eBPF function prototype for verification */ /* 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);
...@@ -173,7 +178,7 @@ struct bpf_verifier_ops { ...@@ -173,7 +178,7 @@ struct bpf_verifier_ops {
u32 (*convert_ctx_access)(enum bpf_access_type type, u32 (*convert_ctx_access)(enum bpf_access_type type,
const struct bpf_insn *src, const struct bpf_insn *src,
struct bpf_insn *dst, struct bpf_insn *dst,
struct bpf_prog *prog); struct bpf_prog *prog, u32 *target_size);
int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr, int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr,
union bpf_attr __user *uattr); union bpf_attr __user *uattr);
}; };
......
...@@ -337,6 +337,22 @@ struct bpf_prog_aux; ...@@ -337,6 +337,22 @@ struct bpf_prog_aux;
bpf_size; \ bpf_size; \
}) })
#define bpf_size_to_bytes(bpf_size) \
({ \
int bytes = -EINVAL; \
\
if (bpf_size == BPF_B) \
bytes = sizeof(u8); \
else if (bpf_size == BPF_H) \
bytes = sizeof(u16); \
else if (bpf_size == BPF_W) \
bytes = sizeof(u32); \
else if (bpf_size == BPF_DW) \
bytes = sizeof(u64); \
\
bytes; \
})
#define BPF_SIZEOF(type) \ #define BPF_SIZEOF(type) \
({ \ ({ \
const int __size = bytes_to_bpf_size(sizeof(type)); \ const int __size = bytes_to_bpf_size(sizeof(type)); \
...@@ -351,6 +367,13 @@ struct bpf_prog_aux; ...@@ -351,6 +367,13 @@ struct bpf_prog_aux;
__size; \ __size; \
}) })
#define BPF_LDST_BYTES(insn) \
({ \
const int __size = bpf_size_to_bytes(BPF_SIZE(insn->code)); \
WARN_ON(__size < 0); \
__size; \
})
#define __BPF_MAP_0(m, v, ...) v #define __BPF_MAP_0(m, v, ...) v
#define __BPF_MAP_1(m, v, t, a, ...) m(t, a) #define __BPF_MAP_1(m, v, t, a, ...) m(t, a)
#define __BPF_MAP_2(m, v, t, a, ...) m(t, a), __BPF_MAP_1(m, v, __VA_ARGS__) #define __BPF_MAP_2(m, v, t, a, ...) m(t, a), __BPF_MAP_1(m, v, __VA_ARGS__)
...@@ -401,6 +424,18 @@ struct bpf_prog_aux; ...@@ -401,6 +424,18 @@ struct bpf_prog_aux;
#define BPF_CALL_4(name, ...) BPF_CALL_x(4, name, __VA_ARGS__) #define BPF_CALL_4(name, ...) BPF_CALL_x(4, name, __VA_ARGS__)
#define BPF_CALL_5(name, ...) BPF_CALL_x(5, name, __VA_ARGS__) #define BPF_CALL_5(name, ...) BPF_CALL_x(5, name, __VA_ARGS__)
#define bpf_ctx_range(TYPE, MEMBER) \
offsetof(TYPE, MEMBER) ... offsetofend(TYPE, MEMBER) - 1
#define bpf_ctx_range_till(TYPE, MEMBER1, MEMBER2) \
offsetof(TYPE, MEMBER1) ... offsetofend(TYPE, MEMBER2) - 1
#define bpf_target_off(TYPE, MEMBER, SIZE, PTR_SIZE) \
({ \
BUILD_BUG_ON(FIELD_SIZEOF(TYPE, MEMBER) != (SIZE)); \
*(PTR_SIZE) = (SIZE); \
offsetof(TYPE, MEMBER); \
})
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
/* A struct sock_filter is architecture independent. */ /* A struct sock_filter is architecture independent. */
struct compat_sock_fprog { struct compat_sock_fprog {
...@@ -564,6 +599,18 @@ static inline bool bpf_prog_was_classic(const struct bpf_prog *prog) ...@@ -564,6 +599,18 @@ static inline bool bpf_prog_was_classic(const struct bpf_prog *prog)
return prog->type == BPF_PROG_TYPE_UNSPEC; return prog->type == BPF_PROG_TYPE_UNSPEC;
} }
static inline bool
bpf_ctx_narrow_access_ok(u32 off, u32 size, const u32 size_default)
{
bool off_ok;
#ifdef __LITTLE_ENDIAN
off_ok = (off & (size_default - 1)) == 0;
#else
off_ok = (off & (size_default - 1)) + size == size_default;
#endif
return off_ok && size <= size_default && (size & (size - 1)) == 0;
}
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0])) #define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
#ifdef CONFIG_ARCH_HAS_SET_MEMORY #ifdef CONFIG_ARCH_HAS_SET_MEMORY
......
...@@ -2206,6 +2206,11 @@ static inline int skb_mac_offset(const struct sk_buff *skb) ...@@ -2206,6 +2206,11 @@ static inline int skb_mac_offset(const struct sk_buff *skb)
return skb_mac_header(skb) - skb->data; return skb_mac_header(skb) - skb->data;
} }
static inline u32 skb_mac_header_len(const struct sk_buff *skb)
{
return skb->network_header - skb->mac_header;
}
static inline int skb_mac_header_was_set(const struct sk_buff *skb) static inline int skb_mac_header_was_set(const struct sk_buff *skb)
{ {
return skb->mac_header != (typeof(skb->mac_header))~0U; return skb->mac_header != (typeof(skb->mac_header))~0U;
......
...@@ -531,6 +531,14 @@ union bpf_attr { ...@@ -531,6 +531,14 @@ union bpf_attr {
* @optval: pointer to option value * @optval: pointer to option value
* @optlen: length of optval in byes * @optlen: length of optval in byes
* Return: 0 or negative error * Return: 0 or negative error
*
* int bpf_skb_adjust_room(skb, len_diff, mode, flags)
* Grow or shrink room in sk_buff.
* @skb: pointer to skb
* @len_diff: (signed) amount of room to grow/shrink
* @mode: operation mode (enum bpf_adj_room_mode)
* @flags: reserved for future use
* Return: 0 on success or negative error code
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -582,7 +590,8 @@ union bpf_attr { ...@@ -582,7 +590,8 @@ union bpf_attr {
FN(get_socket_cookie), \ FN(get_socket_cookie), \
FN(get_socket_uid), \ FN(get_socket_uid), \
FN(set_hash), \ FN(set_hash), \
FN(setsockopt), FN(setsockopt), \
FN(skb_adjust_room),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call * function eBPF program intends to call
...@@ -632,6 +641,11 @@ enum bpf_func_id { ...@@ -632,6 +641,11 @@ enum bpf_func_id {
/* BPF_FUNC_perf_event_output for sk_buff input context. */ /* BPF_FUNC_perf_event_output for sk_buff input context. */
#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) #define BPF_F_CTXLEN_MASK (0xfffffULL << 32)
/* Mode for BPF_FUNC_skb_adjust_room helper. */
enum bpf_adj_room_mode {
BPF_ADJ_ROOM_NET,
};
/* user accessible mirror of in-kernel sk_buff. /* user accessible mirror of in-kernel sk_buff.
* new fields can only be added to the end of this structure * new fields can only be added to the end of this structure
*/ */
......
...@@ -216,10 +216,12 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) ...@@ -216,10 +216,12 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
const struct bpf_map *map = filp->private_data; const struct bpf_map *map = filp->private_data;
const struct bpf_array *array; const struct bpf_array *array;
u32 owner_prog_type = 0; u32 owner_prog_type = 0;
u32 owner_jited = 0;
if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) {
array = container_of(map, struct bpf_array, map); array = container_of(map, struct bpf_array, map);
owner_prog_type = array->owner_prog_type; owner_prog_type = array->owner_prog_type;
owner_jited = array->owner_jited;
} }
seq_printf(m, seq_printf(m,
...@@ -236,9 +238,12 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) ...@@ -236,9 +238,12 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
map->map_flags, map->map_flags,
map->pages * 1ULL << PAGE_SHIFT); map->pages * 1ULL << PAGE_SHIFT);
if (owner_prog_type) if (owner_prog_type) {
seq_printf(m, "owner_prog_type:\t%u\n", seq_printf(m, "owner_prog_type:\t%u\n",
owner_prog_type); owner_prog_type);
seq_printf(m, "owner_jited:\t%u\n",
owner_jited);
}
} }
#endif #endif
......
...@@ -546,20 +546,6 @@ static int check_reg_arg(struct bpf_reg_state *regs, u32 regno, ...@@ -546,20 +546,6 @@ static int check_reg_arg(struct bpf_reg_state *regs, u32 regno,
return 0; return 0;
} }
static int bpf_size_to_bytes(int bpf_size)
{
if (bpf_size == BPF_W)
return 4;
else if (bpf_size == BPF_H)
return 2;
else if (bpf_size == BPF_B)
return 1;
else if (bpf_size == BPF_DW)
return 8;
else
return -EINVAL;
}
static bool is_spillable_regtype(enum bpf_reg_type type) static bool is_spillable_regtype(enum bpf_reg_type type)
{ {
switch (type) { switch (type) {
...@@ -761,7 +747,9 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, ...@@ -761,7 +747,9 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
enum bpf_access_type t, enum bpf_reg_type *reg_type) enum bpf_access_type t, enum bpf_reg_type *reg_type)
{ {
struct bpf_insn_access_aux info = { .reg_type = *reg_type }; struct bpf_insn_access_aux info = {
.reg_type = *reg_type,
};
/* for analyzer ctx accesses are already validated and converted */ /* for analyzer ctx accesses are already validated and converted */
if (env->analyzer_ops) if (env->analyzer_ops)
...@@ -769,25 +757,14 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, ...@@ -769,25 +757,14 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
if (env->prog->aux->ops->is_valid_access && if (env->prog->aux->ops->is_valid_access &&
env->prog->aux->ops->is_valid_access(off, size, t, &info)) { env->prog->aux->ops->is_valid_access(off, size, t, &info)) {
/* a non zero info.ctx_field_size indicates: /* A non zero info.ctx_field_size indicates that this field is a
* . For this field, the prog type specific ctx conversion algorithm * candidate for later verifier transformation to load the whole
* only supports whole field access. * field and then apply a mask when accessed with a narrower
* . This ctx access is a candiate for later verifier transformation * access than actual ctx access size. A zero info.ctx_field_size
* to load the whole field and then apply a mask to get correct result. * will only allow for whole field access and rejects any other
* a non zero info.converted_op_size indicates perceived actual converted * type of narrower access.
* value width in convert_ctx_access.
*/ */
if ((info.ctx_field_size && !info.converted_op_size) || env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
(!info.ctx_field_size && info.converted_op_size)) {
verbose("verifier bug in is_valid_access prog type=%u off=%d size=%d\n",
env->prog->type, off, size);
return -EACCES;
}
if (info.ctx_field_size) {
env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size;
env->insn_aux_data[insn_idx].converted_op_size = info.converted_op_size;
}
*reg_type = info.reg_type; *reg_type = info.reg_type;
/* remember the offset of last byte accessed in ctx */ /* remember the offset of last byte accessed in ctx */
...@@ -1680,6 +1657,65 @@ static int evaluate_reg_alu(struct bpf_verifier_env *env, struct bpf_insn *insn) ...@@ -1680,6 +1657,65 @@ static int evaluate_reg_alu(struct bpf_verifier_env *env, struct bpf_insn *insn)
return 0; return 0;
} }
static int evaluate_reg_imm_alu_unknown(struct bpf_verifier_env *env,
struct bpf_insn *insn)
{
struct bpf_reg_state *regs = env->cur_state.regs;
struct bpf_reg_state *dst_reg = &regs[insn->dst_reg];
struct bpf_reg_state *src_reg = &regs[insn->src_reg];
u8 opcode = BPF_OP(insn->code);
s64 imm_log2 = __ilog2_u64((long long)dst_reg->imm);
/* BPF_X code with src_reg->type UNKNOWN_VALUE here. */
if (src_reg->imm > 0 && dst_reg->imm) {
switch (opcode) {
case BPF_ADD:
/* dreg += sreg
* where both have zero upper bits. Adding them
* can only result making one more bit non-zero
* in the larger value.
* Ex. 0xffff (imm=48) + 1 (imm=63) = 0x10000 (imm=47)
* 0xffff (imm=48) + 0xffff = 0x1fffe (imm=47)
*/
dst_reg->imm = min(src_reg->imm, 63 - imm_log2);
dst_reg->imm--;
break;
case BPF_AND:
/* dreg &= sreg
* AND can not extend zero bits only shrink
* Ex. 0x00..00ffffff
* & 0x0f..ffffffff
* ----------------
* 0x00..00ffffff
*/
dst_reg->imm = max(src_reg->imm, 63 - imm_log2);
break;
case BPF_OR:
/* dreg |= sreg
* OR can only extend zero bits
* Ex. 0x00..00ffffff
* | 0x0f..ffffffff
* ----------------
* 0x0f..00ffffff
*/
dst_reg->imm = min(src_reg->imm, 63 - imm_log2);
break;
case BPF_SUB:
case BPF_MUL:
case BPF_RSH:
case BPF_LSH:
/* These may be flushed out later */
default:
mark_reg_unknown_value(regs, insn->dst_reg);
}
} else {
mark_reg_unknown_value(regs, insn->dst_reg);
}
dst_reg->type = UNKNOWN_VALUE;
return 0;
}
static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, static int evaluate_reg_imm_alu(struct bpf_verifier_env *env,
struct bpf_insn *insn) struct bpf_insn *insn)
{ {
...@@ -1689,6 +1725,9 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env, ...@@ -1689,6 +1725,9 @@ static int evaluate_reg_imm_alu(struct bpf_verifier_env *env,
u8 opcode = BPF_OP(insn->code); u8 opcode = BPF_OP(insn->code);
u64 dst_imm = dst_reg->imm; u64 dst_imm = dst_reg->imm;
if (BPF_SRC(insn->code) == BPF_X && src_reg->type == UNKNOWN_VALUE)
return evaluate_reg_imm_alu_unknown(env, insn);
/* dst_reg->type == CONST_IMM here. Simulate execution of insns /* dst_reg->type == CONST_IMM here. Simulate execution of insns
* containing ALU ops. Don't care about overflow or negative * containing ALU ops. Don't care about overflow or negative
* values, just add/sub/... them; registers are in u64. * values, just add/sub/... them; registers are in u64.
...@@ -3401,11 +3440,13 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of ...@@ -3401,11 +3440,13 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
static int convert_ctx_accesses(struct bpf_verifier_env *env) static int convert_ctx_accesses(struct bpf_verifier_env *env)
{ {
const struct bpf_verifier_ops *ops = env->prog->aux->ops; const struct bpf_verifier_ops *ops = env->prog->aux->ops;
int i, cnt, size, ctx_field_size, delta = 0;
const int insn_cnt = env->prog->len; const int insn_cnt = env->prog->len;
struct bpf_insn insn_buf[16], *insn; struct bpf_insn insn_buf[16], *insn;
struct bpf_prog *new_prog; struct bpf_prog *new_prog;
enum bpf_access_type type; enum bpf_access_type type;
int i, cnt, off, size, ctx_field_size, converted_op_size, is_narrower_load, delta = 0; bool is_narrower_load;
u32 target_size;
if (ops->gen_prologue) { if (ops->gen_prologue) {
cnt = ops->gen_prologue(insn_buf, env->seen_direct_write, cnt = ops->gen_prologue(insn_buf, env->seen_direct_write,
...@@ -3445,39 +3486,50 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) ...@@ -3445,39 +3486,50 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX)
continue; continue;
off = insn->off;
size = bpf_size_to_bytes(BPF_SIZE(insn->code));
ctx_field_size = env->insn_aux_data[i + delta].ctx_field_size; ctx_field_size = env->insn_aux_data[i + delta].ctx_field_size;
converted_op_size = env->insn_aux_data[i + delta].converted_op_size; size = BPF_LDST_BYTES(insn);
is_narrower_load = type == BPF_READ && size < ctx_field_size;
/* If the read access is a narrower load of the field, /* If the read access is a narrower load of the field,
* convert to a 4/8-byte load, to minimum program type specific * convert to a 4/8-byte load, to minimum program type specific
* convert_ctx_access changes. If conversion is successful, * convert_ctx_access changes. If conversion is successful,
* we will apply proper mask to the result. * we will apply proper mask to the result.
*/ */
is_narrower_load = size < ctx_field_size;
if (is_narrower_load) { if (is_narrower_load) {
int size_code = BPF_H; u32 off = insn->off;
u8 size_code;
if (type == BPF_WRITE) {
verbose("bpf verifier narrow ctx access misconfigured\n");
return -EINVAL;
}
size_code = BPF_H;
if (ctx_field_size == 4) if (ctx_field_size == 4)
size_code = BPF_W; size_code = BPF_W;
else if (ctx_field_size == 8) else if (ctx_field_size == 8)
size_code = BPF_DW; size_code = BPF_DW;
insn->off = off & ~(ctx_field_size - 1); insn->off = off & ~(ctx_field_size - 1);
insn->code = BPF_LDX | BPF_MEM | size_code; insn->code = BPF_LDX | BPF_MEM | size_code;
} }
cnt = ops->convert_ctx_access(type, insn, insn_buf, env->prog);
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) { target_size = 0;
cnt = ops->convert_ctx_access(type, insn, insn_buf, env->prog,
&target_size);
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf) ||
(ctx_field_size && !target_size)) {
verbose("bpf verifier is misconfigured\n"); verbose("bpf verifier is misconfigured\n");
return -EINVAL; return -EINVAL;
} }
if (is_narrower_load && size < converted_op_size) {
if (is_narrower_load && size < target_size) {
if (ctx_field_size <= 4) if (ctx_field_size <= 4)
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg, insn_buf[cnt++] = BPF_ALU32_IMM(BPF_AND, insn->dst_reg,
(1 << size * 8) - 1); (1 << size * 8) - 1);
else else
insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg, insn_buf[cnt++] = BPF_ALU64_IMM(BPF_AND, insn->dst_reg,
(1 << size * 8) - 1); (1 << size * 8) - 1);
} }
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
......
...@@ -122,8 +122,8 @@ static const struct bpf_func_proto *bpf_get_probe_write_proto(void) ...@@ -122,8 +122,8 @@ static const struct bpf_func_proto *bpf_get_probe_write_proto(void)
} }
/* /*
* limited trace_printk() * Only limited trace_printk() conversion specifiers allowed:
* only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed * %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %s
*/ */
BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1, BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
u64, arg2, u64, arg3) u64, arg2, u64, arg3)
...@@ -198,7 +198,8 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1, ...@@ -198,7 +198,8 @@ BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
i++; i++;
} }
if (fmt[i] != 'd' && fmt[i] != 'u' && fmt[i] != 'x') if (fmt[i] != 'i' && fmt[i] != 'd' &&
fmt[i] != 'u' && fmt[i] != 'x')
return -EINVAL; return -EINVAL;
fmt_cnt++; fmt_cnt++;
} }
...@@ -583,7 +584,8 @@ const struct bpf_verifier_ops tracepoint_prog_ops = { ...@@ -583,7 +584,8 @@ const struct bpf_verifier_ops tracepoint_prog_ops = {
static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
struct bpf_insn_access_aux *info) struct bpf_insn_access_aux *info)
{ {
int sample_period_off; const int size_sp = FIELD_SIZEOF(struct bpf_perf_event_data,
sample_period);
if (off < 0 || off >= sizeof(struct bpf_perf_event_data)) if (off < 0 || off >= sizeof(struct bpf_perf_event_data))
return false; return false;
...@@ -592,43 +594,35 @@ static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type ...@@ -592,43 +594,35 @@ static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type
if (off % size != 0) if (off % size != 0)
return false; return false;
/* permit 1, 2, 4 byte narrower and 8 normal read access to sample_period */ switch (off) {
sample_period_off = offsetof(struct bpf_perf_event_data, sample_period); case bpf_ctx_range(struct bpf_perf_event_data, sample_period):
if (off >= sample_period_off && off < sample_period_off + sizeof(__u64)) { bpf_ctx_record_field_size(info, size_sp);
int allowed; if (!bpf_ctx_narrow_access_ok(off, size, size_sp))
#ifdef __LITTLE_ENDIAN
allowed = (off & 0x7) == 0 && size <= 8 && (size & (size - 1)) == 0;
#else
allowed = ((off & 0x7) + size) == 8 && size <= 8 && (size & (size - 1)) == 0;
#endif
if (!allowed)
return false; return false;
info->ctx_field_size = 8; break;
info->converted_op_size = 8; default:
} else {
if (size != sizeof(long)) if (size != sizeof(long))
return false; return false;
} }
return true; return true;
} }
static u32 pe_prog_convert_ctx_access(enum bpf_access_type type, static u32 pe_prog_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog, u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
switch (si->off) { switch (si->off) {
case offsetof(struct bpf_perf_event_data, sample_period): case offsetof(struct bpf_perf_event_data, sample_period):
BUILD_BUG_ON(FIELD_SIZEOF(struct perf_sample_data, period) != sizeof(u64));
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern, *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern,
data), si->dst_reg, si->src_reg, data), si->dst_reg, si->src_reg,
offsetof(struct bpf_perf_event_data_kern, data)); offsetof(struct bpf_perf_event_data_kern, data));
*insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg, *insn++ = BPF_LDX_MEM(BPF_DW, si->dst_reg, si->dst_reg,
offsetof(struct perf_sample_data, period)); bpf_target_off(struct perf_sample_data, period, 8,
target_size));
break; break;
default: default:
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern, *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_perf_event_data_kern,
......
...@@ -2012,7 +2012,7 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len) ...@@ -2012,7 +2012,7 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
static int bpf_skb_proto_4_to_6(struct sk_buff *skb) static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
{ {
const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr); const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
u32 off = skb->network_header - skb->mac_header; u32 off = skb_mac_header_len(skb);
int ret; int ret;
ret = skb_cow(skb, len_diff); ret = skb_cow(skb, len_diff);
...@@ -2048,7 +2048,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb) ...@@ -2048,7 +2048,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
static int bpf_skb_proto_6_to_4(struct sk_buff *skb) static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
{ {
const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr); const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
u32 off = skb->network_header - skb->mac_header; u32 off = skb_mac_header_len(skb);
int ret; int ret;
ret = skb_unclone(skb, GFP_ATOMIC); ret = skb_unclone(skb, GFP_ATOMIC);
...@@ -2154,6 +2154,124 @@ static const struct bpf_func_proto bpf_skb_change_type_proto = { ...@@ -2154,6 +2154,124 @@ static const struct bpf_func_proto bpf_skb_change_type_proto = {
.arg2_type = ARG_ANYTHING, .arg2_type = ARG_ANYTHING,
}; };
static u32 bpf_skb_net_base_len(const struct sk_buff *skb)
{
switch (skb->protocol) {
case htons(ETH_P_IP):
return sizeof(struct iphdr);
case htons(ETH_P_IPV6):
return sizeof(struct ipv6hdr);
default:
return ~0U;
}
}
static int bpf_skb_net_grow(struct sk_buff *skb, u32 len_diff)
{
u32 off = skb_mac_header_len(skb) + bpf_skb_net_base_len(skb);
int ret;
ret = skb_cow(skb, len_diff);
if (unlikely(ret < 0))
return ret;
ret = bpf_skb_net_hdr_push(skb, off, len_diff);
if (unlikely(ret < 0))
return ret;
if (skb_is_gso(skb)) {
/* Due to header grow, MSS needs to be downgraded. */
skb_shinfo(skb)->gso_size -= len_diff;
/* Header must be checked, and gso_segs recomputed. */
skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
skb_shinfo(skb)->gso_segs = 0;
}
return 0;
}
static int bpf_skb_net_shrink(struct sk_buff *skb, u32 len_diff)
{
u32 off = skb_mac_header_len(skb) + bpf_skb_net_base_len(skb);
int ret;
ret = skb_unclone(skb, GFP_ATOMIC);
if (unlikely(ret < 0))
return ret;
ret = bpf_skb_net_hdr_pop(skb, off, len_diff);
if (unlikely(ret < 0))
return ret;
if (skb_is_gso(skb)) {
/* Due to header shrink, MSS can be upgraded. */
skb_shinfo(skb)->gso_size += len_diff;
/* Header must be checked, and gso_segs recomputed. */
skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
skb_shinfo(skb)->gso_segs = 0;
}
return 0;
}
static u32 __bpf_skb_max_len(const struct sk_buff *skb)
{
return skb->dev->mtu + skb->dev->hard_header_len;
}
static int bpf_skb_adjust_net(struct sk_buff *skb, s32 len_diff)
{
bool trans_same = skb->transport_header == skb->network_header;
u32 len_cur, len_diff_abs = abs(len_diff);
u32 len_min = bpf_skb_net_base_len(skb);
u32 len_max = __bpf_skb_max_len(skb);
__be16 proto = skb->protocol;
bool shrink = len_diff < 0;
int ret;
if (unlikely(len_diff_abs > 0xfffU))
return -EFAULT;
if (unlikely(proto != htons(ETH_P_IP) &&
proto != htons(ETH_P_IPV6)))
return -ENOTSUPP;
len_cur = skb->len - skb_network_offset(skb);
if (skb_transport_header_was_set(skb) && !trans_same)
len_cur = skb_network_header_len(skb);
if ((shrink && (len_diff_abs >= len_cur ||
len_cur - len_diff_abs < len_min)) ||
(!shrink && (skb->len + len_diff_abs > len_max &&
!skb_is_gso(skb))))
return -ENOTSUPP;
ret = shrink ? bpf_skb_net_shrink(skb, len_diff_abs) :
bpf_skb_net_grow(skb, len_diff_abs);
bpf_compute_data_end(skb);
return 0;
}
BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
u32, mode, u64, flags)
{
if (unlikely(flags))
return -EINVAL;
if (likely(mode == BPF_ADJ_ROOM_NET))
return bpf_skb_adjust_net(skb, len_diff);
return -ENOTSUPP;
}
static const struct bpf_func_proto bpf_skb_adjust_room_proto = {
.func = bpf_skb_adjust_room,
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_ANYTHING,
.arg4_type = ARG_ANYTHING,
};
static u32 __bpf_skb_min_len(const struct sk_buff *skb) static u32 __bpf_skb_min_len(const struct sk_buff *skb)
{ {
u32 min_len = skb_network_offset(skb); u32 min_len = skb_network_offset(skb);
...@@ -2166,11 +2284,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb) ...@@ -2166,11 +2284,6 @@ static u32 __bpf_skb_min_len(const struct sk_buff *skb)
return min_len; return min_len;
} }
static u32 __bpf_skb_max_len(const struct sk_buff *skb)
{
return skb->dev->mtu + skb->dev->hard_header_len;
}
static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len) static int bpf_skb_grow_rcsum(struct sk_buff *skb, unsigned int new_len)
{ {
unsigned int old_len = skb->len; unsigned int old_len = skb->len;
...@@ -2307,6 +2420,7 @@ bool bpf_helper_changes_pkt_data(void *func) ...@@ -2307,6 +2420,7 @@ bool bpf_helper_changes_pkt_data(void *func)
func == bpf_skb_change_proto || func == bpf_skb_change_proto ||
func == bpf_skb_change_head || func == bpf_skb_change_head ||
func == bpf_skb_change_tail || func == bpf_skb_change_tail ||
func == bpf_skb_adjust_room ||
func == bpf_skb_pull_data || func == bpf_skb_pull_data ||
func == bpf_clone_redirect || func == bpf_clone_redirect ||
func == bpf_l3_csum_replace || func == bpf_l3_csum_replace ||
...@@ -2849,6 +2963,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) ...@@ -2849,6 +2963,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
return &bpf_skb_change_proto_proto; return &bpf_skb_change_proto_proto;
case BPF_FUNC_skb_change_type: case BPF_FUNC_skb_change_type:
return &bpf_skb_change_type_proto; return &bpf_skb_change_type_proto;
case BPF_FUNC_skb_adjust_room:
return &bpf_skb_adjust_room_proto;
case BPF_FUNC_skb_change_tail: case BPF_FUNC_skb_change_tail:
return &bpf_skb_change_tail_proto; return &bpf_skb_change_tail_proto;
case BPF_FUNC_skb_get_tunnel_key: case BPF_FUNC_skb_get_tunnel_key:
...@@ -2972,38 +3088,11 @@ lwt_xmit_func_proto(enum bpf_func_id func_id) ...@@ -2972,38 +3088,11 @@ lwt_xmit_func_proto(enum bpf_func_id func_id)
} }
} }
static void __set_access_aux_info(int off, struct bpf_insn_access_aux *info) static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type,
struct bpf_insn_access_aux *info)
{ {
info->ctx_field_size = 4; const int size_default = sizeof(__u32);
switch (off) {
case offsetof(struct __sk_buff, pkt_type) ...
offsetof(struct __sk_buff, pkt_type) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, vlan_present) ...
offsetof(struct __sk_buff, vlan_present) + sizeof(__u32) - 1:
info->converted_op_size = 1;
break;
case offsetof(struct __sk_buff, queue_mapping) ...
offsetof(struct __sk_buff, queue_mapping) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, protocol) ...
offsetof(struct __sk_buff, protocol) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, vlan_tci) ...
offsetof(struct __sk_buff, vlan_tci) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, vlan_proto) ...
offsetof(struct __sk_buff, vlan_proto) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, tc_index) ...
offsetof(struct __sk_buff, tc_index) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, tc_classid) ...
offsetof(struct __sk_buff, tc_classid) + sizeof(__u32) - 1:
info->converted_op_size = 2;
break;
default:
info->converted_op_size = 4;
}
}
static bool __is_valid_access(int off, int size, enum bpf_access_type type,
struct bpf_insn_access_aux *info)
{
if (off < 0 || off >= sizeof(struct __sk_buff)) if (off < 0 || off >= sizeof(struct __sk_buff))
return false; return false;
...@@ -3012,40 +3101,24 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type, ...@@ -3012,40 +3101,24 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type,
return false; return false;
switch (off) { switch (off) {
case offsetof(struct __sk_buff, cb[0]) ... case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1: if (off + size > offsetofend(struct __sk_buff, cb[4]))
if (off + size >
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32))
return false; return false;
break; break;
case offsetof(struct __sk_buff, data) ... case bpf_ctx_range(struct __sk_buff, data):
offsetof(struct __sk_buff, data) + sizeof(__u32) - 1: case bpf_ctx_range(struct __sk_buff, data_end):
if (size != sizeof(__u32)) if (size != size_default)
return false; return false;
info->reg_type = PTR_TO_PACKET;
break;
case offsetof(struct __sk_buff, data_end) ...
offsetof(struct __sk_buff, data_end) + sizeof(__u32) - 1:
if (size != sizeof(__u32))
return false;
info->reg_type = PTR_TO_PACKET_END;
break; break;
default: default:
/* Only narrow read access allowed for now. */
if (type == BPF_WRITE) { if (type == BPF_WRITE) {
if (size != sizeof(__u32)) if (size != size_default)
return false; return false;
} else { } else {
int allowed; bpf_ctx_record_field_size(info, size_default);
if (!bpf_ctx_narrow_access_ok(off, size, size_default))
/* permit narrower load for not cb/data/data_end fields */
#ifdef __LITTLE_ENDIAN
allowed = (off & 0x3) == 0 && size <= 4 && (size & (size - 1)) == 0;
#else
allowed = (off & 0x3) + size == 4 && size <= 4 && (size & (size - 1)) == 0;
#endif
if (!allowed)
return false; return false;
__set_access_aux_info(off, info);
} }
} }
...@@ -3057,26 +3130,22 @@ static bool sk_filter_is_valid_access(int off, int size, ...@@ -3057,26 +3130,22 @@ static bool sk_filter_is_valid_access(int off, int size,
struct bpf_insn_access_aux *info) struct bpf_insn_access_aux *info)
{ {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, tc_classid) ... case bpf_ctx_range(struct __sk_buff, tc_classid):
offsetof(struct __sk_buff, tc_classid) + sizeof(__u32) - 1: case bpf_ctx_range(struct __sk_buff, data):
case offsetof(struct __sk_buff, data) ... case bpf_ctx_range(struct __sk_buff, data_end):
offsetof(struct __sk_buff, data) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, data_end) ...
offsetof(struct __sk_buff, data_end) + sizeof(__u32) - 1:
return false; return false;
} }
if (type == BPF_WRITE) { if (type == BPF_WRITE) {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, cb[0]) ... case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
break; break;
default: default:
return false; return false;
} }
} }
return __is_valid_access(off, size, type, info); return bpf_skb_is_valid_access(off, size, type, info);
} }
static bool lwt_is_valid_access(int off, int size, static bool lwt_is_valid_access(int off, int size,
...@@ -3084,24 +3153,31 @@ static bool lwt_is_valid_access(int off, int size, ...@@ -3084,24 +3153,31 @@ static bool lwt_is_valid_access(int off, int size,
struct bpf_insn_access_aux *info) struct bpf_insn_access_aux *info)
{ {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, tc_classid) ... case bpf_ctx_range(struct __sk_buff, tc_classid):
offsetof(struct __sk_buff, tc_classid) + sizeof(__u32) - 1:
return false; return false;
} }
if (type == BPF_WRITE) { if (type == BPF_WRITE) {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, mark): case bpf_ctx_range(struct __sk_buff, mark):
case offsetof(struct __sk_buff, priority): case bpf_ctx_range(struct __sk_buff, priority):
case offsetof(struct __sk_buff, cb[0]) ... case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
break; break;
default: default:
return false; return false;
} }
} }
return __is_valid_access(off, size, type, info); switch (off) {
case bpf_ctx_range(struct __sk_buff, data):
info->reg_type = PTR_TO_PACKET;
break;
case bpf_ctx_range(struct __sk_buff, data_end):
info->reg_type = PTR_TO_PACKET_END;
break;
}
return bpf_skb_is_valid_access(off, size, type, info);
} }
static bool sock_filter_is_valid_access(int off, int size, static bool sock_filter_is_valid_access(int off, int size,
...@@ -3173,19 +3249,27 @@ static bool tc_cls_act_is_valid_access(int off, int size, ...@@ -3173,19 +3249,27 @@ static bool tc_cls_act_is_valid_access(int off, int size,
{ {
if (type == BPF_WRITE) { if (type == BPF_WRITE) {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, mark): case bpf_ctx_range(struct __sk_buff, mark):
case offsetof(struct __sk_buff, tc_index): case bpf_ctx_range(struct __sk_buff, tc_index):
case offsetof(struct __sk_buff, priority): case bpf_ctx_range(struct __sk_buff, priority):
case offsetof(struct __sk_buff, cb[0]) ... case bpf_ctx_range(struct __sk_buff, tc_classid):
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1: case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):
case offsetof(struct __sk_buff, tc_classid):
break; break;
default: default:
return false; return false;
} }
} }
return __is_valid_access(off, size, type, info); switch (off) {
case bpf_ctx_range(struct __sk_buff, data):
info->reg_type = PTR_TO_PACKET;
break;
case bpf_ctx_range(struct __sk_buff, data_end):
info->reg_type = PTR_TO_PACKET_END;
break;
}
return bpf_skb_is_valid_access(off, size, type, info);
} }
static bool __is_valid_xdp_access(int off, int size) static bool __is_valid_xdp_access(int off, int size)
...@@ -3258,98 +3342,108 @@ static bool sock_ops_is_valid_access(int off, int size, ...@@ -3258,98 +3342,108 @@ static bool sock_ops_is_valid_access(int off, int size,
static u32 bpf_convert_ctx_access(enum bpf_access_type type, static u32 bpf_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog, u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
int off; int off;
switch (si->off) { switch (si->off) {
case offsetof(struct __sk_buff, len): case offsetof(struct __sk_buff, len):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, len)); bpf_target_off(struct sk_buff, len, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, protocol): case offsetof(struct __sk_buff, protocol):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
*insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, protocol)); bpf_target_off(struct sk_buff, protocol, 2,
target_size));
break; break;
case offsetof(struct __sk_buff, vlan_proto): case offsetof(struct __sk_buff, vlan_proto):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_proto) != 2);
*insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, vlan_proto)); bpf_target_off(struct sk_buff, vlan_proto, 2,
target_size));
break; break;
case offsetof(struct __sk_buff, priority): case offsetof(struct __sk_buff, priority):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, priority) != 4);
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, priority)); bpf_target_off(struct sk_buff, priority, 4,
target_size));
else else
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, priority)); bpf_target_off(struct sk_buff, priority, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, ingress_ifindex): case offsetof(struct __sk_buff, ingress_ifindex):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, skb_iif) != 4);
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, skb_iif)); bpf_target_off(struct sk_buff, skb_iif, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, ifindex): case offsetof(struct __sk_buff, ifindex):
BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, dev), *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, dev),
si->dst_reg, si->src_reg, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, dev)); offsetof(struct sk_buff, dev));
*insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 1); *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 1);
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg,
offsetof(struct net_device, ifindex)); bpf_target_off(struct net_device, ifindex, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, hash): case offsetof(struct __sk_buff, hash):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, hash)); bpf_target_off(struct sk_buff, hash, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, mark): case offsetof(struct __sk_buff, mark):
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, mark)); bpf_target_off(struct sk_buff, mark, 4,
target_size));
else else
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, mark)); bpf_target_off(struct sk_buff, mark, 4,
target_size));
break; break;
case offsetof(struct __sk_buff, pkt_type): case offsetof(struct __sk_buff, pkt_type):
return convert_skb_access(SKF_AD_PKTTYPE, si->dst_reg, *target_size = 1;
si->src_reg, insn); *insn++ = BPF_LDX_MEM(BPF_B, si->dst_reg, si->src_reg,
PKT_TYPE_OFFSET());
*insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, PKT_TYPE_MAX);
#ifdef __BIG_ENDIAN_BITFIELD
*insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, 5);
#endif
break;
case offsetof(struct __sk_buff, queue_mapping): case offsetof(struct __sk_buff, queue_mapping):
return convert_skb_access(SKF_AD_QUEUE, si->dst_reg, *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
si->src_reg, insn); bpf_target_off(struct sk_buff, queue_mapping, 2,
target_size));
break;
case offsetof(struct __sk_buff, vlan_present): case offsetof(struct __sk_buff, vlan_present):
return convert_skb_access(SKF_AD_VLAN_TAG_PRESENT,
si->dst_reg, si->src_reg, insn);
case offsetof(struct __sk_buff, vlan_tci): case offsetof(struct __sk_buff, vlan_tci):
return convert_skb_access(SKF_AD_VLAN_TAG, BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
si->dst_reg, si->src_reg, insn);
*insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
bpf_target_off(struct sk_buff, vlan_tci, 2,
target_size));
if (si->off == offsetof(struct __sk_buff, vlan_tci)) {
*insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg,
~VLAN_TAG_PRESENT);
} else {
*insn++ = BPF_ALU32_IMM(BPF_RSH, si->dst_reg, 12);
*insn++ = BPF_ALU32_IMM(BPF_AND, si->dst_reg, 1);
}
break;
case offsetof(struct __sk_buff, cb[0]) ... case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1: offsetofend(struct __sk_buff, cb[4]) - 1:
BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20); BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);
BUILD_BUG_ON((offsetof(struct sk_buff, cb) + BUILD_BUG_ON((offsetof(struct sk_buff, cb) +
offsetof(struct qdisc_skb_cb, data)) % offsetof(struct qdisc_skb_cb, data)) %
...@@ -3375,6 +3469,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, ...@@ -3375,6 +3469,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
off -= offsetof(struct __sk_buff, tc_classid); off -= offsetof(struct __sk_buff, tc_classid);
off += offsetof(struct sk_buff, cb); off += offsetof(struct sk_buff, cb);
off += offsetof(struct qdisc_skb_cb, tc_classid); off += offsetof(struct qdisc_skb_cb, tc_classid);
*target_size = 2;
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_H, si->dst_reg, *insn++ = BPF_STX_MEM(BPF_H, si->dst_reg,
si->src_reg, off); si->src_reg, off);
...@@ -3400,14 +3495,14 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, ...@@ -3400,14 +3495,14 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
case offsetof(struct __sk_buff, tc_index): case offsetof(struct __sk_buff, tc_index):
#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_SCHED
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2);
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_H, si->dst_reg, si->src_reg, *insn++ = BPF_STX_MEM(BPF_H, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, tc_index)); bpf_target_off(struct sk_buff, tc_index, 2,
target_size));
else else
*insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, tc_index)); bpf_target_off(struct sk_buff, tc_index, 2,
target_size));
#else #else
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_MOV64_REG(si->dst_reg, si->dst_reg); *insn++ = BPF_MOV64_REG(si->dst_reg, si->dst_reg);
...@@ -3418,10 +3513,9 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, ...@@ -3418,10 +3513,9 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
case offsetof(struct __sk_buff, napi_id): case offsetof(struct __sk_buff, napi_id):
#if defined(CONFIG_NET_RX_BUSY_POLL) #if defined(CONFIG_NET_RX_BUSY_POLL)
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, napi_id) != 4);
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, napi_id)); bpf_target_off(struct sk_buff, napi_id, 4,
target_size));
*insn++ = BPF_JMP_IMM(BPF_JGE, si->dst_reg, MIN_NAPI_ID, 1); *insn++ = BPF_JMP_IMM(BPF_JGE, si->dst_reg, MIN_NAPI_ID, 1);
*insn++ = BPF_MOV64_IMM(si->dst_reg, 0); *insn++ = BPF_MOV64_IMM(si->dst_reg, 0);
#else #else
...@@ -3436,7 +3530,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, ...@@ -3436,7 +3530,7 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, static u32 sock_filter_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog, u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
...@@ -3480,22 +3574,22 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, ...@@ -3480,22 +3574,22 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type,
static u32 tc_cls_act_convert_ctx_access(enum bpf_access_type type, static u32 tc_cls_act_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog, u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
switch (si->off) { switch (si->off) {
case offsetof(struct __sk_buff, ifindex): case offsetof(struct __sk_buff, ifindex):
BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, dev), *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, dev),
si->dst_reg, si->src_reg, si->dst_reg, si->src_reg,
offsetof(struct sk_buff, dev)); offsetof(struct sk_buff, dev));
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg,
offsetof(struct net_device, ifindex)); bpf_target_off(struct net_device, ifindex, 4,
target_size));
break; break;
default: default:
return bpf_convert_ctx_access(type, si, insn_buf, prog); return bpf_convert_ctx_access(type, si, insn_buf, prog,
target_size);
} }
return insn - insn_buf; return insn - insn_buf;
...@@ -3504,7 +3598,7 @@ static u32 tc_cls_act_convert_ctx_access(enum bpf_access_type type, ...@@ -3504,7 +3598,7 @@ static u32 tc_cls_act_convert_ctx_access(enum bpf_access_type type,
static u32 xdp_convert_ctx_access(enum bpf_access_type type, static u32 xdp_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog, u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
...@@ -3527,7 +3621,8 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, ...@@ -3527,7 +3621,8 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type,
static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, static u32 sock_ops_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si, const struct bpf_insn *si,
struct bpf_insn *insn_buf, struct bpf_insn *insn_buf,
struct bpf_prog *prog) struct bpf_prog *prog,
u32 *target_size)
{ {
struct bpf_insn *insn = insn_buf; struct bpf_insn *insn = insn_buf;
int off; int off;
......
...@@ -531,6 +531,14 @@ union bpf_attr { ...@@ -531,6 +531,14 @@ union bpf_attr {
* @optval: pointer to option value * @optval: pointer to option value
* @optlen: length of optval in byes * @optlen: length of optval in byes
* Return: 0 or negative error * Return: 0 or negative error
*
* int bpf_skb_adjust_room(skb, len_diff, mode, flags)
* Grow or shrink room in sk_buff.
* @skb: pointer to skb
* @len_diff: (signed) amount of room to grow/shrink
* @mode: operation mode (enum bpf_adj_room_mode)
* @flags: reserved for future use
* Return: 0 on success or negative error code
*/ */
#define __BPF_FUNC_MAPPER(FN) \ #define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \ FN(unspec), \
...@@ -582,7 +590,8 @@ union bpf_attr { ...@@ -582,7 +590,8 @@ union bpf_attr {
FN(get_socket_cookie), \ FN(get_socket_cookie), \
FN(get_socket_uid), \ FN(get_socket_uid), \
FN(set_hash), \ FN(set_hash), \
FN(setsockopt), FN(setsockopt), \
FN(skb_adjust_room),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper /* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call * function eBPF program intends to call
...@@ -632,6 +641,11 @@ enum bpf_func_id { ...@@ -632,6 +641,11 @@ enum bpf_func_id {
/* BPF_FUNC_perf_event_output for sk_buff input context. */ /* BPF_FUNC_perf_event_output for sk_buff input context. */
#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) #define BPF_F_CTXLEN_MASK (0xfffffULL << 32)
/* Mode for BPF_FUNC_skb_adjust_room helper. */
enum bpf_adj_room_mode {
BPF_ADJ_ROOM_NET_OPTS,
};
/* user accessible mirror of in-kernel sk_buff. /* user accessible mirror of in-kernel sk_buff.
* new fields can only be added to the end of this structure * new fields can only be added to the end of this structure
*/ */
......
...@@ -2657,6 +2657,171 @@ static struct bpf_test tests[] = { ...@@ -2657,6 +2657,171 @@ static struct bpf_test tests[] = {
.prog_type = BPF_PROG_TYPE_SCHED_CLS, .prog_type = BPF_PROG_TYPE_SCHED_CLS,
.flags = F_LOAD_WITH_STRICT_ALIGNMENT, .flags = F_LOAD_WITH_STRICT_ALIGNMENT,
}, },
{
"direct packet access: test18 (imm += pkt_ptr, 1)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_IMM(BPF_REG_0, 8),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.result = ACCEPT,
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
},
{
"direct packet access: test19 (imm += pkt_ptr, 2)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 3),
BPF_MOV64_IMM(BPF_REG_4, 4),
BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),
BPF_STX_MEM(BPF_B, BPF_REG_4, BPF_REG_4, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.result = ACCEPT,
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
},
{
"direct packet access: test20 (x += pkt_ptr, 1)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_IMM(BPF_REG_0, 0xffffffff),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff),
BPF_MOV64_REG(BPF_REG_4, BPF_REG_0),
BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),
BPF_MOV64_REG(BPF_REG_5, BPF_REG_4),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1),
BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
{
"direct packet access: test21 (x += pkt_ptr, 2)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 9),
BPF_MOV64_IMM(BPF_REG_4, 0xffffffff),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 0xffff),
BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),
BPF_MOV64_REG(BPF_REG_5, BPF_REG_4),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1),
BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
{
"direct packet access: test22 (x += pkt_ptr, 3)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -8),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, -16),
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 11),
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
BPF_MOV64_IMM(BPF_REG_4, 0xffffffff),
BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_4, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 48),
BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),
BPF_MOV64_REG(BPF_REG_0, BPF_REG_4),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2),
BPF_MOV64_IMM(BPF_REG_2, 1),
BPF_STX_MEM(BPF_H, BPF_REG_4, BPF_REG_2, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
{
"direct packet access: test23 (x += pkt_ptr, 4)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_IMM(BPF_REG_0, 0xffffffff),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff),
BPF_MOV64_REG(BPF_REG_4, BPF_REG_0),
BPF_MOV64_IMM(BPF_REG_0, 31),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2),
BPF_MOV64_REG(BPF_REG_5, BPF_REG_0),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0xffff - 1),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
.errstr = "cannot add integer value with 47 upper zero bits to ptr_to_packet",
},
{
"direct packet access: test24 (x += pkt_ptr, 5)",
.insns = {
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
offsetof(struct __sk_buff, data)),
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
offsetof(struct __sk_buff, data_end)),
BPF_MOV64_IMM(BPF_REG_0, 0xffffffff),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xff),
BPF_MOV64_REG(BPF_REG_4, BPF_REG_0),
BPF_MOV64_IMM(BPF_REG_0, 64),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4),
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2),
BPF_MOV64_REG(BPF_REG_5, BPF_REG_0),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0xffff - 1),
BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
},
{ {
"helper access to packet: test1, valid packet_ptr range", "helper access to packet: test1, valid packet_ptr range",
.insns = { .insns = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment