|
|
|
@ -445,18 +445,6 @@ static bool reg_type_not_null(enum bpf_reg_type type)
|
|
|
|
|
type == PTR_TO_SOCK_COMMON;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool reg_type_may_be_null(enum bpf_reg_type type)
|
|
|
|
|
{
|
|
|
|
|
return type == PTR_TO_MAP_VALUE_OR_NULL ||
|
|
|
|
|
type == PTR_TO_SOCKET_OR_NULL ||
|
|
|
|
|
type == PTR_TO_SOCK_COMMON_OR_NULL ||
|
|
|
|
|
type == PTR_TO_TCP_SOCK_OR_NULL ||
|
|
|
|
|
type == PTR_TO_BTF_ID_OR_NULL ||
|
|
|
|
|
type == PTR_TO_MEM_OR_NULL ||
|
|
|
|
|
type == PTR_TO_RDONLY_BUF_OR_NULL ||
|
|
|
|
|
type == PTR_TO_RDWR_BUF_OR_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg)
|
|
|
|
|
{
|
|
|
|
|
return reg->type == PTR_TO_MAP_VALUE &&
|
|
|
|
@ -465,12 +453,9 @@ static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg)
|
|
|
|
|
|
|
|
|
|
static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
|
|
|
|
|
{
|
|
|
|
|
return type == PTR_TO_SOCKET ||
|
|
|
|
|
type == PTR_TO_SOCKET_OR_NULL ||
|
|
|
|
|
type == PTR_TO_TCP_SOCK ||
|
|
|
|
|
type == PTR_TO_TCP_SOCK_OR_NULL ||
|
|
|
|
|
type == PTR_TO_MEM ||
|
|
|
|
|
type == PTR_TO_MEM_OR_NULL;
|
|
|
|
|
return base_type(type) == PTR_TO_SOCKET ||
|
|
|
|
|
base_type(type) == PTR_TO_TCP_SOCK ||
|
|
|
|
|
base_type(type) == PTR_TO_MEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
|
|
|
|
@ -540,39 +525,52 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
|
|
|
|
|
insn->imm == BPF_CMPXCHG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* string representation of 'enum bpf_reg_type' */
|
|
|
|
|
static const char * const reg_type_str[] = {
|
|
|
|
|
[NOT_INIT] = "?",
|
|
|
|
|
[SCALAR_VALUE] = "inv",
|
|
|
|
|
[PTR_TO_CTX] = "ctx",
|
|
|
|
|
[CONST_PTR_TO_MAP] = "map_ptr",
|
|
|
|
|
[PTR_TO_MAP_VALUE] = "map_value",
|
|
|
|
|
[PTR_TO_MAP_VALUE_OR_NULL] = "map_value_or_null",
|
|
|
|
|
[PTR_TO_STACK] = "fp",
|
|
|
|
|
[PTR_TO_PACKET] = "pkt",
|
|
|
|
|
[PTR_TO_PACKET_META] = "pkt_meta",
|
|
|
|
|
[PTR_TO_PACKET_END] = "pkt_end",
|
|
|
|
|
[PTR_TO_FLOW_KEYS] = "flow_keys",
|
|
|
|
|
[PTR_TO_SOCKET] = "sock",
|
|
|
|
|
[PTR_TO_SOCKET_OR_NULL] = "sock_or_null",
|
|
|
|
|
[PTR_TO_SOCK_COMMON] = "sock_common",
|
|
|
|
|
[PTR_TO_SOCK_COMMON_OR_NULL] = "sock_common_or_null",
|
|
|
|
|
[PTR_TO_TCP_SOCK] = "tcp_sock",
|
|
|
|
|
[PTR_TO_TCP_SOCK_OR_NULL] = "tcp_sock_or_null",
|
|
|
|
|
[PTR_TO_TP_BUFFER] = "tp_buffer",
|
|
|
|
|
[PTR_TO_XDP_SOCK] = "xdp_sock",
|
|
|
|
|
[PTR_TO_BTF_ID] = "ptr_",
|
|
|
|
|
[PTR_TO_BTF_ID_OR_NULL] = "ptr_or_null_",
|
|
|
|
|
[PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_",
|
|
|
|
|
[PTR_TO_MEM] = "mem",
|
|
|
|
|
[PTR_TO_MEM_OR_NULL] = "mem_or_null",
|
|
|
|
|
[PTR_TO_RDONLY_BUF] = "rdonly_buf",
|
|
|
|
|
[PTR_TO_RDONLY_BUF_OR_NULL] = "rdonly_buf_or_null",
|
|
|
|
|
[PTR_TO_RDWR_BUF] = "rdwr_buf",
|
|
|
|
|
[PTR_TO_RDWR_BUF_OR_NULL] = "rdwr_buf_or_null",
|
|
|
|
|
[PTR_TO_FUNC] = "func",
|
|
|
|
|
[PTR_TO_MAP_KEY] = "map_key",
|
|
|
|
|
};
|
|
|
|
|
/* string representation of 'enum bpf_reg_type'
|
|
|
|
|
*
|
|
|
|
|
* Note that reg_type_str() can not appear more than once in a single verbose()
|
|
|
|
|
* statement.
|
|
|
|
|
*/
|
|
|
|
|
static const char *reg_type_str(struct bpf_verifier_env *env,
|
|
|
|
|
enum bpf_reg_type type)
|
|
|
|
|
{
|
|
|
|
|
char postfix[16] = {0};
|
|
|
|
|
static const char * const str[] = {
|
|
|
|
|
[NOT_INIT] = "?",
|
|
|
|
|
[SCALAR_VALUE] = "inv",
|
|
|
|
|
[PTR_TO_CTX] = "ctx",
|
|
|
|
|
[CONST_PTR_TO_MAP] = "map_ptr",
|
|
|
|
|
[PTR_TO_MAP_VALUE] = "map_value",
|
|
|
|
|
[PTR_TO_STACK] = "fp",
|
|
|
|
|
[PTR_TO_PACKET] = "pkt",
|
|
|
|
|
[PTR_TO_PACKET_META] = "pkt_meta",
|
|
|
|
|
[PTR_TO_PACKET_END] = "pkt_end",
|
|
|
|
|
[PTR_TO_FLOW_KEYS] = "flow_keys",
|
|
|
|
|
[PTR_TO_SOCKET] = "sock",
|
|
|
|
|
[PTR_TO_SOCK_COMMON] = "sock_common",
|
|
|
|
|
[PTR_TO_TCP_SOCK] = "tcp_sock",
|
|
|
|
|
[PTR_TO_TP_BUFFER] = "tp_buffer",
|
|
|
|
|
[PTR_TO_XDP_SOCK] = "xdp_sock",
|
|
|
|
|
[PTR_TO_BTF_ID] = "ptr_",
|
|
|
|
|
[PTR_TO_PERCPU_BTF_ID] = "percpu_ptr_",
|
|
|
|
|
[PTR_TO_MEM] = "mem",
|
|
|
|
|
[PTR_TO_RDONLY_BUF] = "rdonly_buf",
|
|
|
|
|
[PTR_TO_RDWR_BUF] = "rdwr_buf",
|
|
|
|
|
[PTR_TO_FUNC] = "func",
|
|
|
|
|
[PTR_TO_MAP_KEY] = "map_key",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (type & PTR_MAYBE_NULL) {
|
|
|
|
|
if (base_type(type) == PTR_TO_BTF_ID ||
|
|
|
|
|
base_type(type) == PTR_TO_PERCPU_BTF_ID)
|
|
|
|
|
strncpy(postfix, "or_null_", 16);
|
|
|
|
|
else
|
|
|
|
|
strncpy(postfix, "_or_null", 16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s",
|
|
|
|
|
str[base_type(type)], postfix);
|
|
|
|
|
return env->type_str_buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char slot_type_char[] = {
|
|
|
|
|
[STACK_INVALID] = '?',
|
|
|
|
@ -623,7 +621,7 @@ static void print_verifier_state(struct bpf_verifier_env *env,
|
|
|
|
|
continue;
|
|
|
|
|
verbose(env, " R%d", i);
|
|
|
|
|
print_liveness(env, reg->live);
|
|
|
|
|
verbose(env, "=%s", reg_type_str[t]);
|
|
|
|
|
verbose(env, "=%s", reg_type_str(env, t));
|
|
|
|
|
if (t == SCALAR_VALUE && reg->precise)
|
|
|
|
|
verbose(env, "P");
|
|
|
|
|
if ((t == SCALAR_VALUE || t == PTR_TO_STACK) &&
|
|
|
|
@ -631,9 +629,8 @@ static void print_verifier_state(struct bpf_verifier_env *env,
|
|
|
|
|
/* reg->off should be 0 for SCALAR_VALUE */
|
|
|
|
|
verbose(env, "%lld", reg->var_off.value + reg->off);
|
|
|
|
|
} else {
|
|
|
|
|
if (t == PTR_TO_BTF_ID ||
|
|
|
|
|
t == PTR_TO_BTF_ID_OR_NULL ||
|
|
|
|
|
t == PTR_TO_PERCPU_BTF_ID)
|
|
|
|
|
if (base_type(t) == PTR_TO_BTF_ID ||
|
|
|
|
|
base_type(t) == PTR_TO_PERCPU_BTF_ID)
|
|
|
|
|
verbose(env, "%s", kernel_type_name(reg->btf, reg->btf_id));
|
|
|
|
|
verbose(env, "(id=%d", reg->id);
|
|
|
|
|
if (reg_type_may_be_refcounted_or_null(t))
|
|
|
|
@ -642,10 +639,9 @@ static void print_verifier_state(struct bpf_verifier_env *env,
|
|
|
|
|
verbose(env, ",off=%d", reg->off);
|
|
|
|
|
if (type_is_pkt_pointer(t))
|
|
|
|
|
verbose(env, ",r=%d", reg->range);
|
|
|
|
|
else if (t == CONST_PTR_TO_MAP ||
|
|
|
|
|
t == PTR_TO_MAP_KEY ||
|
|
|
|
|
t == PTR_TO_MAP_VALUE ||
|
|
|
|
|
t == PTR_TO_MAP_VALUE_OR_NULL)
|
|
|
|
|
else if (base_type(t) == CONST_PTR_TO_MAP ||
|
|
|
|
|
base_type(t) == PTR_TO_MAP_KEY ||
|
|
|
|
|
base_type(t) == PTR_TO_MAP_VALUE)
|
|
|
|
|
verbose(env, ",ks=%d,vs=%d",
|
|
|
|
|
reg->map_ptr->key_size,
|
|
|
|
|
reg->map_ptr->value_size);
|
|
|
|
@ -715,7 +711,7 @@ static void print_verifier_state(struct bpf_verifier_env *env,
|
|
|
|
|
if (state->stack[i].slot_type[0] == STACK_SPILL) {
|
|
|
|
|
reg = &state->stack[i].spilled_ptr;
|
|
|
|
|
t = reg->type;
|
|
|
|
|
verbose(env, "=%s", reg_type_str[t]);
|
|
|
|
|
verbose(env, "=%s", reg_type_str(env, t));
|
|
|
|
|
if (t == SCALAR_VALUE && reg->precise)
|
|
|
|
|
verbose(env, "P");
|
|
|
|
|
if (t == SCALAR_VALUE && tnum_is_const(reg->var_off))
|
|
|
|
@ -1128,8 +1124,7 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env,
|
|
|
|
|
|
|
|
|
|
static void mark_ptr_not_null_reg(struct bpf_reg_state *reg)
|
|
|
|
|
{
|
|
|
|
|
switch (reg->type) {
|
|
|
|
|
case PTR_TO_MAP_VALUE_OR_NULL: {
|
|
|
|
|
if (base_type(reg->type) == PTR_TO_MAP_VALUE) {
|
|
|
|
|
const struct bpf_map *map = reg->map_ptr;
|
|
|
|
|
|
|
|
|
|
if (map->inner_map_meta) {
|
|
|
|
@ -1148,32 +1143,10 @@ static void mark_ptr_not_null_reg(struct bpf_reg_state *reg)
|
|
|
|
|
} else {
|
|
|
|
|
reg->type = PTR_TO_MAP_VALUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PTR_TO_SOCKET_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_SOCKET;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_SOCK_COMMON_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_SOCK_COMMON;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_TCP_SOCK_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_TCP_SOCK;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_BTF_ID_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_BTF_ID;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_MEM_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_MEM;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_RDONLY_BUF_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_RDONLY_BUF;
|
|
|
|
|
break;
|
|
|
|
|
case PTR_TO_RDWR_BUF_OR_NULL:
|
|
|
|
|
reg->type = PTR_TO_RDWR_BUF;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WARN_ONCE(1, "unknown nullable register type");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reg->type &= ~PTR_MAYBE_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg)
|
|
|
|
@ -1901,7 +1874,7 @@ static int mark_reg_read(struct bpf_verifier_env *env,
|
|
|
|
|
break;
|
|
|
|
|
if (parent->live & REG_LIVE_DONE) {
|
|
|
|
|
verbose(env, "verifier BUG type %s var_off %lld off %d\n",
|
|
|
|
|
reg_type_str[parent->type],
|
|
|
|
|
reg_type_str(env, parent->type),
|
|
|
|
|
parent->var_off.value, parent->off);
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
}
|
|
|
|
@ -2559,9 +2532,8 @@ static int mark_chain_precision_stack(struct bpf_verifier_env *env, int spi)
|
|
|
|
|
|
|
|
|
|
static bool is_spillable_regtype(enum bpf_reg_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
switch (base_type(type)) {
|
|
|
|
|
case PTR_TO_MAP_VALUE:
|
|
|
|
|
case PTR_TO_MAP_VALUE_OR_NULL:
|
|
|
|
|
case PTR_TO_STACK:
|
|
|
|
|
case PTR_TO_CTX:
|
|
|
|
|
case PTR_TO_PACKET:
|
|
|
|
@ -2570,21 +2542,14 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
|
|
|
|
|
case PTR_TO_FLOW_KEYS:
|
|
|
|
|
case CONST_PTR_TO_MAP:
|
|
|
|
|
case PTR_TO_SOCKET:
|
|
|
|
|
case PTR_TO_SOCKET_OR_NULL:
|
|
|
|
|
case PTR_TO_SOCK_COMMON:
|
|
|
|
|
case PTR_TO_SOCK_COMMON_OR_NULL:
|
|
|
|
|
case PTR_TO_TCP_SOCK:
|
|
|
|
|
case PTR_TO_TCP_SOCK_OR_NULL:
|
|
|
|
|
case PTR_TO_XDP_SOCK:
|
|
|
|
|
case PTR_TO_BTF_ID:
|
|
|
|
|
case PTR_TO_BTF_ID_OR_NULL:
|
|
|
|
|
case PTR_TO_RDONLY_BUF:
|
|
|
|
|
case PTR_TO_RDONLY_BUF_OR_NULL:
|
|
|
|
|
case PTR_TO_RDWR_BUF:
|
|
|
|
|
case PTR_TO_RDWR_BUF_OR_NULL:
|
|
|
|
|
case PTR_TO_PERCPU_BTF_ID:
|
|
|
|
|
case PTR_TO_MEM:
|
|
|
|
|
case PTR_TO_MEM_OR_NULL:
|
|
|
|
|
case PTR_TO_FUNC:
|
|
|
|
|
case PTR_TO_MAP_KEY:
|
|
|
|
|
return true;
|
|
|
|
@ -3400,7 +3365,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
|
|
|
|
|
*/
|
|
|
|
|
*reg_type = info.reg_type;
|
|
|
|
|
|
|
|
|
|
if (*reg_type == PTR_TO_BTF_ID || *reg_type == PTR_TO_BTF_ID_OR_NULL) {
|
|
|
|
|
if (base_type(*reg_type) == PTR_TO_BTF_ID) {
|
|
|
|
|
*btf = info.btf;
|
|
|
|
|
*btf_id = info.btf_id;
|
|
|
|
|
} else {
|
|
|
|
@ -3468,7 +3433,7 @@ static int check_sock_access(struct bpf_verifier_env *env, int insn_idx,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verbose(env, "R%d invalid %s access off=%d size=%d\n",
|
|
|
|
|
regno, reg_type_str[reg->type], off, size);
|
|
|
|
|
regno, reg_type_str(env, reg->type), off, size);
|
|
|
|
|
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
@ -4233,7 +4198,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|
|
|
|
} else {
|
|
|
|
|
mark_reg_known_zero(env, regs,
|
|
|
|
|
value_regno);
|
|
|
|
|
if (reg_type_may_be_null(reg_type))
|
|
|
|
|
if (type_may_be_null(reg_type))
|
|
|
|
|
regs[value_regno].id = ++env->id_gen;
|
|
|
|
|
/* A load of ctx field could have different
|
|
|
|
|
* actual load size with the one encoded in the
|
|
|
|
@ -4241,8 +4206,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|
|
|
|
* a sub-register.
|
|
|
|
|
*/
|
|
|
|
|
regs[value_regno].subreg_def = DEF_NOT_SUBREG;
|
|
|
|
|
if (reg_type == PTR_TO_BTF_ID ||
|
|
|
|
|
reg_type == PTR_TO_BTF_ID_OR_NULL) {
|
|
|
|
|
if (base_type(reg_type) == PTR_TO_BTF_ID) {
|
|
|
|
|
regs[value_regno].btf = btf;
|
|
|
|
|
regs[value_regno].btf_id = btf_id;
|
|
|
|
|
}
|
|
|
|
@ -4295,7 +4259,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|
|
|
|
} else if (type_is_sk_pointer(reg->type)) {
|
|
|
|
|
if (t == BPF_WRITE) {
|
|
|
|
|
verbose(env, "R%d cannot write into %s\n",
|
|
|
|
|
regno, reg_type_str[reg->type]);
|
|
|
|
|
regno, reg_type_str(env, reg->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
err = check_sock_access(env, insn_idx, regno, off, size, t);
|
|
|
|
@ -4314,7 +4278,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|
|
|
|
} else if (reg->type == PTR_TO_RDONLY_BUF) {
|
|
|
|
|
if (t == BPF_WRITE) {
|
|
|
|
|
verbose(env, "R%d cannot write into %s\n",
|
|
|
|
|
regno, reg_type_str[reg->type]);
|
|
|
|
|
regno, reg_type_str(env, reg->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
err = check_buffer_access(env, reg, regno, off, size, false,
|
|
|
|
@ -4330,7 +4294,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
|
|
|
|
mark_reg_unknown(env, regs, value_regno);
|
|
|
|
|
} else {
|
|
|
|
|
verbose(env, "R%d invalid mem access '%s'\n", regno,
|
|
|
|
|
reg_type_str[reg->type]);
|
|
|
|
|
reg_type_str(env, reg->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -4404,7 +4368,7 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
|
|
|
|
|
is_sk_reg(env, insn->dst_reg)) {
|
|
|
|
|
verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
|
|
|
|
|
insn->dst_reg,
|
|
|
|
|
reg_type_str[reg_state(env, insn->dst_reg)->type]);
|
|
|
|
|
reg_type_str(env, reg_state(env, insn->dst_reg)->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -4630,9 +4594,9 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
|
|
|
|
|
register_is_null(reg))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
verbose(env, "R%d type=%s expected=%s\n", regno,
|
|
|
|
|
reg_type_str[reg->type],
|
|
|
|
|
reg_type_str[PTR_TO_STACK]);
|
|
|
|
|
verbose(env, "R%d type=%s ", regno,
|
|
|
|
|
reg_type_str(env, reg->type));
|
|
|
|
|
verbose(env, "expected=%s\n", reg_type_str(env, PTR_TO_STACK));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -4643,7 +4607,7 @@ int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
|
|
|
|
|
if (register_is_null(reg))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (reg_type_may_be_null(reg->type)) {
|
|
|
|
|
if (type_may_be_null(reg->type)) {
|
|
|
|
|
/* Assuming that the register contains a value check if the memory
|
|
|
|
|
* access is safe. Temporarily save and restore the register's state as
|
|
|
|
|
* the conversion shouldn't be visible to a caller.
|
|
|
|
@ -4974,10 +4938,10 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verbose(env, "R%d type=%s expected=", regno, reg_type_str[type]);
|
|
|
|
|
verbose(env, "R%d type=%s expected=", regno, reg_type_str(env, type));
|
|
|
|
|
for (j = 0; j + 1 < i; j++)
|
|
|
|
|
verbose(env, "%s, ", reg_type_str[compatible->types[j]]);
|
|
|
|
|
verbose(env, "%s\n", reg_type_str[compatible->types[j]]);
|
|
|
|
|
verbose(env, "%s, ", reg_type_str(env, compatible->types[j]));
|
|
|
|
|
verbose(env, "%s\n", reg_type_str(env, compatible->types[j]));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
|
|
|
|
|
found:
|
|
|
|
@ -6196,6 +6160,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
{
|
|
|
|
|
const struct bpf_func_proto *fn = NULL;
|
|
|
|
|
enum bpf_return_type ret_type;
|
|
|
|
|
enum bpf_type_flag ret_flag;
|
|
|
|
|
struct bpf_reg_state *regs;
|
|
|
|
|
struct bpf_call_arg_meta meta;
|
|
|
|
|
int insn_idx = *insn_idx_p;
|
|
|
|
@ -6330,6 +6295,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
|
|
|
|
|
/* update return register (already marked as written above) */
|
|
|
|
|
ret_type = fn->ret_type;
|
|
|
|
|
ret_flag = type_flag(fn->ret_type);
|
|
|
|
|
if (ret_type == RET_INTEGER) {
|
|
|
|
|
/* sets type to SCALAR_VALUE */
|
|
|
|
|
mark_reg_unknown(env, regs, BPF_REG_0);
|
|
|
|
@ -6349,25 +6315,23 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
}
|
|
|
|
|
regs[BPF_REG_0].map_ptr = meta.map_ptr;
|
|
|
|
|
regs[BPF_REG_0].map_uid = meta.map_uid;
|
|
|
|
|
if (type_may_be_null(ret_type)) {
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL;
|
|
|
|
|
} else {
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MAP_VALUE;
|
|
|
|
|
if (map_value_has_spin_lock(meta.map_ptr))
|
|
|
|
|
regs[BPF_REG_0].id = ++env->id_gen;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag;
|
|
|
|
|
if (!type_may_be_null(ret_type) &&
|
|
|
|
|
map_value_has_spin_lock(meta.map_ptr)) {
|
|
|
|
|
regs[BPF_REG_0].id = ++env->id_gen;
|
|
|
|
|
}
|
|
|
|
|
} else if (base_type(ret_type) == RET_PTR_TO_SOCKET) {
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_SOCKET_OR_NULL;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_SOCKET | ret_flag;
|
|
|
|
|
} else if (base_type(ret_type) == RET_PTR_TO_SOCK_COMMON) {
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_SOCK_COMMON_OR_NULL;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_SOCK_COMMON | ret_flag;
|
|
|
|
|
} else if (base_type(ret_type) == RET_PTR_TO_TCP_SOCK) {
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_TCP_SOCK | ret_flag;
|
|
|
|
|
} else if (base_type(ret_type) == RET_PTR_TO_ALLOC_MEM) {
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MEM_OR_NULL;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag;
|
|
|
|
|
regs[BPF_REG_0].mem_size = meta.mem_size;
|
|
|
|
|
} else if (base_type(ret_type) == RET_PTR_TO_MEM_OR_BTF_ID) {
|
|
|
|
|
const struct btf_type *t;
|
|
|
|
@ -6387,14 +6351,10 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
tname, PTR_ERR(ret));
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
regs[BPF_REG_0].type =
|
|
|
|
|
(ret_type & PTR_MAYBE_NULL) ?
|
|
|
|
|
PTR_TO_MEM_OR_NULL : PTR_TO_MEM;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_MEM | ret_flag;
|
|
|
|
|
regs[BPF_REG_0].mem_size = tsize;
|
|
|
|
|
} else {
|
|
|
|
|
regs[BPF_REG_0].type =
|
|
|
|
|
(ret_type & PTR_MAYBE_NULL) ?
|
|
|
|
|
PTR_TO_BTF_ID_OR_NULL : PTR_TO_BTF_ID;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag;
|
|
|
|
|
regs[BPF_REG_0].btf = meta.ret_btf;
|
|
|
|
|
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
|
|
|
|
|
}
|
|
|
|
@ -6402,9 +6362,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
int ret_btf_id;
|
|
|
|
|
|
|
|
|
|
mark_reg_known_zero(env, regs, BPF_REG_0);
|
|
|
|
|
regs[BPF_REG_0].type = (ret_type & PTR_MAYBE_NULL) ?
|
|
|
|
|
PTR_TO_BTF_ID_OR_NULL :
|
|
|
|
|
PTR_TO_BTF_ID;
|
|
|
|
|
regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag;
|
|
|
|
|
ret_btf_id = *fn->ret_btf_id;
|
|
|
|
|
if (ret_btf_id == 0) {
|
|
|
|
|
verbose(env, "invalid return type %u of func %s#%d\n",
|
|
|
|
@ -6423,7 +6381,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reg_type_may_be_null(regs[BPF_REG_0].type))
|
|
|
|
|
if (type_may_be_null(regs[BPF_REG_0].type))
|
|
|
|
|
regs[BPF_REG_0].id = ++env->id_gen;
|
|
|
|
|
|
|
|
|
|
if (is_ptr_cast_function(func_id)) {
|
|
|
|
@ -6622,25 +6580,25 @@ static bool check_reg_sane_offset(struct bpf_verifier_env *env,
|
|
|
|
|
|
|
|
|
|
if (known && (val >= BPF_MAX_VAR_OFF || val <= -BPF_MAX_VAR_OFF)) {
|
|
|
|
|
verbose(env, "math between %s pointer and %lld is not allowed\n",
|
|
|
|
|
reg_type_str[type], val);
|
|
|
|
|
reg_type_str(env, type), val);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reg->off >= BPF_MAX_VAR_OFF || reg->off <= -BPF_MAX_VAR_OFF) {
|
|
|
|
|
verbose(env, "%s pointer offset %d is not allowed\n",
|
|
|
|
|
reg_type_str[type], reg->off);
|
|
|
|
|
reg_type_str(env, type), reg->off);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (smin == S64_MIN) {
|
|
|
|
|
verbose(env, "math between %s pointer and register with unbounded min value is not allowed\n",
|
|
|
|
|
reg_type_str[type]);
|
|
|
|
|
reg_type_str(env, type));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (smin >= BPF_MAX_VAR_OFF || smin <= -BPF_MAX_VAR_OFF) {
|
|
|
|
|
verbose(env, "value %lld makes %s pointer be out of bounds\n",
|
|
|
|
|
smin, reg_type_str[type]);
|
|
|
|
|
smin, reg_type_str(env, type));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -7017,11 +6975,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ptr_reg->type) {
|
|
|
|
|
case PTR_TO_MAP_VALUE_OR_NULL:
|
|
|
|
|
if (ptr_reg->type & PTR_MAYBE_NULL) {
|
|
|
|
|
verbose(env, "R%d pointer arithmetic on %s prohibited, null-check it first\n",
|
|
|
|
|
dst, reg_type_str[ptr_reg->type]);
|
|
|
|
|
dst, reg_type_str(env, ptr_reg->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (base_type(ptr_reg->type)) {
|
|
|
|
|
case CONST_PTR_TO_MAP:
|
|
|
|
|
/* smin_val represents the known value */
|
|
|
|
|
if (known && smin_val == 0 && opcode == BPF_ADD)
|
|
|
|
@ -7034,10 +6994,10 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
|
|
|
|
|
case PTR_TO_XDP_SOCK:
|
|
|
|
|
reject:
|
|
|
|
|
verbose(env, "R%d pointer arithmetic on %s prohibited\n",
|
|
|
|
|
dst, reg_type_str[ptr_reg->type]);
|
|
|
|
|
dst, reg_type_str(env, ptr_reg->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
default:
|
|
|
|
|
if (reg_type_may_be_null(ptr_reg->type))
|
|
|
|
|
if (type_may_be_null(ptr_reg->type))
|
|
|
|
|
goto reject;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -8759,7 +8719,7 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
|
|
|
|
|
struct bpf_reg_state *reg, u32 id,
|
|
|
|
|
bool is_null)
|
|
|
|
|
{
|
|
|
|
|
if (reg_type_may_be_null(reg->type) && reg->id == id &&
|
|
|
|
|
if (type_may_be_null(reg->type) && reg->id == id &&
|
|
|
|
|
!WARN_ON_ONCE(!reg->id)) {
|
|
|
|
|
if (WARN_ON_ONCE(reg->smin_value || reg->smax_value ||
|
|
|
|
|
!tnum_equals_const(reg->var_off, 0) ||
|
|
|
|
@ -9137,7 +9097,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
|
|
|
|
*/
|
|
|
|
|
if (!is_jmp32 && BPF_SRC(insn->code) == BPF_K &&
|
|
|
|
|
insn->imm == 0 && (opcode == BPF_JEQ || opcode == BPF_JNE) &&
|
|
|
|
|
reg_type_may_be_null(dst_reg->type)) {
|
|
|
|
|
type_may_be_null(dst_reg->type)) {
|
|
|
|
|
/* Mark all identical registers in each branch as either
|
|
|
|
|
* safe or unknown depending R == 0 or R != 0 conditional.
|
|
|
|
|
*/
|
|
|
|
@ -9393,7 +9353,7 @@ static int check_return_code(struct bpf_verifier_env *env)
|
|
|
|
|
/* enforce return zero from async callbacks like timer */
|
|
|
|
|
if (reg->type != SCALAR_VALUE) {
|
|
|
|
|
verbose(env, "In async callback the register R0 is not a known value (%s)\n",
|
|
|
|
|
reg_type_str[reg->type]);
|
|
|
|
|
reg_type_str(env, reg->type));
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -9407,7 +9367,7 @@ static int check_return_code(struct bpf_verifier_env *env)
|
|
|
|
|
if (is_subprog) {
|
|
|
|
|
if (reg->type != SCALAR_VALUE) {
|
|
|
|
|
verbose(env, "At subprogram exit the register R0 is not a scalar value (%s)\n",
|
|
|
|
|
reg_type_str[reg->type]);
|
|
|
|
|
reg_type_str(env, reg->type));
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
@ -9471,7 +9431,7 @@ static int check_return_code(struct bpf_verifier_env *env)
|
|
|
|
|
|
|
|
|
|
if (reg->type != SCALAR_VALUE) {
|
|
|
|
|
verbose(env, "At program exit the register R0 is not a known value (%s)\n",
|
|
|
|
|
reg_type_str[reg->type]);
|
|
|
|
|
reg_type_str(env, reg->type));
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -10252,7 +10212,7 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|
|
|
|
return true;
|
|
|
|
|
if (rcur->type == NOT_INIT)
|
|
|
|
|
return false;
|
|
|
|
|
switch (rold->type) {
|
|
|
|
|
switch (base_type(rold->type)) {
|
|
|
|
|
case SCALAR_VALUE:
|
|
|
|
|
if (env->explore_alu_limits)
|
|
|
|
|
return false;
|
|
|
|
@ -10274,6 +10234,22 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|
|
|
|
}
|
|
|
|
|
case PTR_TO_MAP_KEY:
|
|
|
|
|
case PTR_TO_MAP_VALUE:
|
|
|
|
|
/* a PTR_TO_MAP_VALUE could be safe to use as a
|
|
|
|
|
* PTR_TO_MAP_VALUE_OR_NULL into the same map.
|
|
|
|
|
* However, if the old PTR_TO_MAP_VALUE_OR_NULL then got NULL-
|
|
|
|
|
* checked, doing so could have affected others with the same
|
|
|
|
|
* id, and we can't check for that because we lost the id when
|
|
|
|
|
* we converted to a PTR_TO_MAP_VALUE.
|
|
|
|
|
*/
|
|
|
|
|
if (type_may_be_null(rold->type)) {
|
|
|
|
|
if (!type_may_be_null(rcur->type))
|
|
|
|
|
return false;
|
|
|
|
|
if (memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)))
|
|
|
|
|
return false;
|
|
|
|
|
/* Check our ids match any regs they're supposed to */
|
|
|
|
|
return check_ids(rold->id, rcur->id, idmap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the new min/max/var_off satisfy the old ones and
|
|
|
|
|
* everything else matches, we are OK.
|
|
|
|
|
* 'id' is not compared, since it's only used for maps with
|
|
|
|
@ -10285,20 +10261,6 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|
|
|
|
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
|
|
|
|
|
range_within(rold, rcur) &&
|
|
|
|
|
tnum_in(rold->var_off, rcur->var_off);
|
|
|
|
|
case PTR_TO_MAP_VALUE_OR_NULL:
|
|
|
|
|
/* a PTR_TO_MAP_VALUE could be safe to use as a
|
|
|
|
|
* PTR_TO_MAP_VALUE_OR_NULL into the same map.
|
|
|
|
|
* However, if the old PTR_TO_MAP_VALUE_OR_NULL then got NULL-
|
|
|
|
|
* checked, doing so could have affected others with the same
|
|
|
|
|
* id, and we can't check for that because we lost the id when
|
|
|
|
|
* we converted to a PTR_TO_MAP_VALUE.
|
|
|
|
|
*/
|
|
|
|
|
if (rcur->type != PTR_TO_MAP_VALUE_OR_NULL)
|
|
|
|
|
return false;
|
|
|
|
|
if (memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)))
|
|
|
|
|
return false;
|
|
|
|
|
/* Check our ids match any regs they're supposed to */
|
|
|
|
|
return check_ids(rold->id, rcur->id, idmap);
|
|
|
|
|
case PTR_TO_PACKET_META:
|
|
|
|
|
case PTR_TO_PACKET:
|
|
|
|
|
if (rcur->type != rold->type)
|
|
|
|
@ -10327,11 +10289,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
|
|
|
|
|
case PTR_TO_PACKET_END:
|
|
|
|
|
case PTR_TO_FLOW_KEYS:
|
|
|
|
|
case PTR_TO_SOCKET:
|
|
|
|
|
case PTR_TO_SOCKET_OR_NULL:
|
|
|
|
|
case PTR_TO_SOCK_COMMON:
|
|
|
|
|
case PTR_TO_SOCK_COMMON_OR_NULL:
|
|
|
|
|
case PTR_TO_TCP_SOCK:
|
|
|
|
|
case PTR_TO_TCP_SOCK_OR_NULL:
|
|
|
|
|
case PTR_TO_XDP_SOCK:
|
|
|
|
|
/* Only valid matches are exact, which memcmp() above
|
|
|
|
|
* would have accepted
|
|
|
|
@ -10857,17 +10816,13 @@ next:
|
|
|
|
|
/* Return true if it's OK to have the same insn return a different type. */
|
|
|
|
|
static bool reg_type_mismatch_ok(enum bpf_reg_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
switch (base_type(type)) {
|
|
|
|
|
case PTR_TO_CTX:
|
|
|
|
|
case PTR_TO_SOCKET:
|
|
|
|
|
case PTR_TO_SOCKET_OR_NULL:
|
|
|
|
|
case PTR_TO_SOCK_COMMON:
|
|
|
|
|
case PTR_TO_SOCK_COMMON_OR_NULL:
|
|
|
|
|
case PTR_TO_TCP_SOCK:
|
|
|
|
|
case PTR_TO_TCP_SOCK_OR_NULL:
|
|
|
|
|
case PTR_TO_XDP_SOCK:
|
|
|
|
|
case PTR_TO_BTF_ID:
|
|
|
|
|
case PTR_TO_BTF_ID_OR_NULL:
|
|
|
|
|
return false;
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
@ -11091,7 +11046,7 @@ static int do_check(struct bpf_verifier_env *env)
|
|
|
|
|
if (is_ctx_reg(env, insn->dst_reg)) {
|
|
|
|
|
verbose(env, "BPF_ST stores into R%d %s is not allowed\n",
|
|
|
|
|
insn->dst_reg,
|
|
|
|
|
reg_type_str[reg_state(env, insn->dst_reg)->type]);
|
|
|
|
|
reg_type_str(env, reg_state(env, insn->dst_reg)->type));
|
|
|
|
|
return -EACCES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|