Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Alexei Starovoitov says: ==================== pull-request: bpf 2018-12-05 The following pull-request contains BPF updates for your *net* tree. The main changes are: 1) fix bpf uapi pointers for 32-bit architectures, from Daniel. 2) improve verifer ability to handle progs with a lot of branches, from Alexei. 3) strict btf checks, from Yonghong. 4) bpf_sk_lookup api cleanup, from Joe. 5) other misc fixes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <uapi/linux/types.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
@@ -426,6 +427,30 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
|
||||
offset < btf->hdr.str_len;
|
||||
}
|
||||
|
||||
/* Only C-style identifier is permitted. This can be relaxed if
|
||||
* necessary.
|
||||
*/
|
||||
static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
|
||||
{
|
||||
/* offset must be valid */
|
||||
const char *src = &btf->strings[offset];
|
||||
const char *src_limit;
|
||||
|
||||
if (!isalpha(*src) && *src != '_')
|
||||
return false;
|
||||
|
||||
/* set a limit on identifier length */
|
||||
src_limit = src + KSYM_NAME_LEN;
|
||||
src++;
|
||||
while (*src && src < src_limit) {
|
||||
if (!isalnum(*src) && *src != '_')
|
||||
return false;
|
||||
src++;
|
||||
}
|
||||
|
||||
return !*src;
|
||||
}
|
||||
|
||||
static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
|
||||
{
|
||||
if (!offset)
|
||||
@@ -1143,6 +1168,22 @@ static int btf_ref_type_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* typedef type must have a valid name, and other ref types,
|
||||
* volatile, const, restrict, should have a null name.
|
||||
*/
|
||||
if (BTF_INFO_KIND(t->info) == BTF_KIND_TYPEDEF) {
|
||||
if (!t->name_off ||
|
||||
!btf_name_valid_identifier(env->btf, t->name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (t->name_off) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
return 0;
|
||||
@@ -1300,6 +1341,13 @@ static s32 btf_fwd_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* fwd type must have a valid name */
|
||||
if (!t->name_off ||
|
||||
!btf_name_valid_identifier(env->btf, t->name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
return 0;
|
||||
@@ -1356,6 +1404,12 @@ static s32 btf_array_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* array type should not have a name */
|
||||
if (t->name_off) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (btf_type_vlen(t)) {
|
||||
btf_verifier_log_type(env, t, "vlen != 0");
|
||||
return -EINVAL;
|
||||
@@ -1532,6 +1586,13 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* struct type either no name or a valid one */
|
||||
if (t->name_off &&
|
||||
!btf_name_valid_identifier(env->btf, t->name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
last_offset = 0;
|
||||
@@ -1543,6 +1604,12 @@ static s32 btf_struct_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* struct member either no name or a valid one */
|
||||
if (member->name_off &&
|
||||
!btf_name_valid_identifier(btf, member->name_off)) {
|
||||
btf_verifier_log_member(env, t, member, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* A member cannot be in type void */
|
||||
if (!member->type || !BTF_TYPE_ID_VALID(member->type)) {
|
||||
btf_verifier_log_member(env, t, member,
|
||||
@@ -1730,6 +1797,13 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* enum type either no name or a valid one */
|
||||
if (t->name_off &&
|
||||
!btf_name_valid_identifier(env->btf, t->name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
for (i = 0; i < nr_enums; i++) {
|
||||
@@ -1739,6 +1813,14 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* enum member must have a valid name */
|
||||
if (!enums[i].name_off ||
|
||||
!btf_name_valid_identifier(btf, enums[i].name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
btf_verifier_log(env, "\t%s val=%d\n",
|
||||
btf_name_by_offset(btf, enums[i].name_off),
|
||||
enums[i].val);
|
||||
|
@@ -175,6 +175,7 @@ struct bpf_verifier_stack_elem {
|
||||
|
||||
#define BPF_COMPLEXITY_LIMIT_INSNS 131072
|
||||
#define BPF_COMPLEXITY_LIMIT_STACK 1024
|
||||
#define BPF_COMPLEXITY_LIMIT_STATES 64
|
||||
|
||||
#define BPF_MAP_PTR_UNPRIV 1UL
|
||||
#define BPF_MAP_PTR_POISON ((void *)((0xeB9FUL << 1) + \
|
||||
@@ -3751,6 +3752,79 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
|
||||
}
|
||||
}
|
||||
|
||||
/* compute branch direction of the expression "if (reg opcode val) goto target;"
|
||||
* and return:
|
||||
* 1 - branch will be taken and "goto target" will be executed
|
||||
* 0 - branch will not be taken and fall-through to next insn
|
||||
* -1 - unknown. Example: "if (reg < 5)" is unknown when register value range [0,10]
|
||||
*/
|
||||
static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode)
|
||||
{
|
||||
if (__is_pointer_value(false, reg))
|
||||
return -1;
|
||||
|
||||
switch (opcode) {
|
||||
case BPF_JEQ:
|
||||
if (tnum_is_const(reg->var_off))
|
||||
return !!tnum_equals_const(reg->var_off, val);
|
||||
break;
|
||||
case BPF_JNE:
|
||||
if (tnum_is_const(reg->var_off))
|
||||
return !tnum_equals_const(reg->var_off, val);
|
||||
break;
|
||||
case BPF_JGT:
|
||||
if (reg->umin_value > val)
|
||||
return 1;
|
||||
else if (reg->umax_value <= val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JSGT:
|
||||
if (reg->smin_value > (s64)val)
|
||||
return 1;
|
||||
else if (reg->smax_value < (s64)val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JLT:
|
||||
if (reg->umax_value < val)
|
||||
return 1;
|
||||
else if (reg->umin_value >= val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JSLT:
|
||||
if (reg->smax_value < (s64)val)
|
||||
return 1;
|
||||
else if (reg->smin_value >= (s64)val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JGE:
|
||||
if (reg->umin_value >= val)
|
||||
return 1;
|
||||
else if (reg->umax_value < val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JSGE:
|
||||
if (reg->smin_value >= (s64)val)
|
||||
return 1;
|
||||
else if (reg->smax_value < (s64)val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JLE:
|
||||
if (reg->umax_value <= val)
|
||||
return 1;
|
||||
else if (reg->umin_value > val)
|
||||
return 0;
|
||||
break;
|
||||
case BPF_JSLE:
|
||||
if (reg->smax_value <= (s64)val)
|
||||
return 1;
|
||||
else if (reg->smin_value > (s64)val)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Adjusts the register min/max values in the case that the dst_reg is the
|
||||
* variable register that we are working on, and src_reg is a constant or we're
|
||||
* simply doing a BPF_K check.
|
||||
@@ -4152,21 +4226,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
||||
|
||||
dst_reg = ®s[insn->dst_reg];
|
||||
|
||||
/* detect if R == 0 where R was initialized to zero earlier */
|
||||
if (BPF_SRC(insn->code) == BPF_K &&
|
||||
(opcode == BPF_JEQ || opcode == BPF_JNE) &&
|
||||
dst_reg->type == SCALAR_VALUE &&
|
||||
tnum_is_const(dst_reg->var_off)) {
|
||||
if ((opcode == BPF_JEQ && dst_reg->var_off.value == insn->imm) ||
|
||||
(opcode == BPF_JNE && dst_reg->var_off.value != insn->imm)) {
|
||||
/* if (imm == imm) goto pc+off;
|
||||
* only follow the goto, ignore fall-through
|
||||
*/
|
||||
if (BPF_SRC(insn->code) == BPF_K) {
|
||||
int pred = is_branch_taken(dst_reg, insn->imm, opcode);
|
||||
|
||||
if (pred == 1) {
|
||||
/* only follow the goto, ignore fall-through */
|
||||
*insn_idx += insn->off;
|
||||
return 0;
|
||||
} else {
|
||||
/* if (imm != imm) goto pc+off;
|
||||
* only follow fall-through branch, since
|
||||
} else if (pred == 0) {
|
||||
/* only follow fall-through branch, since
|
||||
* that's where the program will go
|
||||
*/
|
||||
return 0;
|
||||
@@ -4980,7 +5048,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
||||
struct bpf_verifier_state_list *new_sl;
|
||||
struct bpf_verifier_state_list *sl;
|
||||
struct bpf_verifier_state *cur = env->cur_state, *new;
|
||||
int i, j, err;
|
||||
int i, j, err, states_cnt = 0;
|
||||
|
||||
sl = env->explored_states[insn_idx];
|
||||
if (!sl)
|
||||
@@ -5007,8 +5075,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
||||
return 1;
|
||||
}
|
||||
sl = sl->next;
|
||||
states_cnt++;
|
||||
}
|
||||
|
||||
if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES)
|
||||
return 0;
|
||||
|
||||
/* there were no equivalent states, remember current one.
|
||||
* technically the current state is not proven to be safe yet,
|
||||
* but it will either reach outer most bpf_exit (which means it's safe)
|
||||
@@ -5148,6 +5220,9 @@ static int do_check(struct bpf_verifier_env *env)
|
||||
goto process_bpf_exit;
|
||||
}
|
||||
|
||||
if (signal_pending(current))
|
||||
return -EAGAIN;
|
||||
|
||||
if (need_resched())
|
||||
cond_resched();
|
||||
|
||||
|
Reference in New Issue
Block a user