Skip to content

Commit 401af75

Browse files
author
Alexei Starovoitov
committed
Merge branch 'Fixes for bad PTR_TO_BTF_ID offset'
Kumar Kartikeya Dwivedi says: ==================== This set fixes a bug related to bad var_off being permitted for kfunc call in case of PTR_TO_BTF_ID, consolidates offset checks for all register types allowed as helper or kfunc arguments into a common shared helper, and introduces a couple of other checks to harden the kfunc release logic and prevent future bugs. Some selftests are also included that fail in absence of these fixes, serving as demonstration of the issues being fixed. Changelog: ---------- v3 -> v4: v3: https://lore.kernel.org/bpf/20220304000508.2904128-1-memxor@gmail.com * Update commit message for __diag patch to say clang instead of LLVM (Nathan) * Address nits for check_func_arg_reg_off (Martin) * Add comment for fixed_off_ok case, remove is_kfunc check (Martin) v2 -> v3: v2: https://lore.kernel.org/bpf/20220303045029.2645297-1-memxor@gmail.com * Add my SoB to __diag for clang patch (Nathan) v1 -> v2: v1: https://lore.kernel.org/bpf/20220301065745.1634848-1-memxor@gmail.com * Put reg->off check for release kfunc inside check_func_arg_reg_off, make the check a bit more readable * Squash verifier selftests errstr update into patch 3 for bisect (Alexei) * Include fix from Nathan for clang warning about missing prototypes * Add unified __diag_ingore_all that works for both GCC/LLVM (Alexei) Older discussion: Link: https://lore.kernel.org/bpf/20220219113744.1852259-1-memxor@gmail.com Kumar Kartikeya Dwivedi (7): bpf: Add check_func_arg_reg_off function bpf: Fix PTR_TO_BTF_ID var_off check bpf: Disallow negative offset in check_ptr_off_reg bpf: Harden register offset checks for release helpers and kfuncs compiler_types.h: Add unified __diag_ignore_all for GCC/LLVM bpf: Replace __diag_ignore with unified __diag_ignore_all selftests/bpf: Add tests for kfunc register offset checks ==================== Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents caec549 + 8218ccb commit 401af75

File tree

11 files changed

+230
-53
lines changed

11 files changed

+230
-53
lines changed

include/linux/bpf_verifier.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@ bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt);
521521

522522
int check_ptr_off_reg(struct bpf_verifier_env *env,
523523
const struct bpf_reg_state *reg, int regno);
524+
int check_func_arg_reg_off(struct bpf_verifier_env *env,
525+
const struct bpf_reg_state *reg, int regno,
526+
enum bpf_arg_type arg_type,
527+
bool is_release_func);
524528
int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
525529
u32 regno);
526530
int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,

include/linux/compiler-clang.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,28 @@
6868

6969
#define __nocfi __attribute__((__no_sanitize__("cfi")))
7070
#define __cficanonical __attribute__((__cfi_canonical_jump_table__))
71+
72+
/*
73+
* Turn individual warnings and errors on and off locally, depending
74+
* on version.
75+
*/
76+
#define __diag_clang(version, severity, s) \
77+
__diag_clang_ ## version(__diag_clang_ ## severity s)
78+
79+
/* Severity used in pragma directives */
80+
#define __diag_clang_ignore ignored
81+
#define __diag_clang_warn warning
82+
#define __diag_clang_error error
83+
84+
#define __diag_str1(s) #s
85+
#define __diag_str(s) __diag_str1(s)
86+
#define __diag(s) _Pragma(__diag_str(clang diagnostic s))
87+
88+
#if CONFIG_CLANG_VERSION >= 110000
89+
#define __diag_clang_11(s) __diag(s)
90+
#else
91+
#define __diag_clang_11(s)
92+
#endif
93+
94+
#define __diag_ignore_all(option, comment) \
95+
__diag_clang(11, ignore, option)

include/linux/compiler-gcc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@
151151
#define __diag_GCC_8(s)
152152
#endif
153153

154+
#define __diag_ignore_all(option, comment) \
155+
__diag_GCC(8, ignore, option)
156+
154157
/*
155158
* Prior to 9.1, -Wno-alloc-size-larger-than (and therefore the "alloc_size"
156159
* attribute) do not work, and must be disabled.

include/linux/compiler_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,8 @@ struct ftrace_likely_data {
371371
#define __diag_error(compiler, version, option, comment) \
372372
__diag_ ## compiler(version, error, option)
373373

374+
#ifndef __diag_ignore_all
375+
#define __diag_ignore_all(option, comment)
376+
#endif
377+
374378
#endif /* __LINUX_COMPILER_TYPES_H */

kernel/bpf/btf.c

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5726,7 +5726,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
57265726
const char *func_name, *ref_tname;
57275727
const struct btf_type *t, *ref_t;
57285728
const struct btf_param *args;
5729-
int ref_regno = 0;
5729+
int ref_regno = 0, ret;
57305730
bool rel = false;
57315731

57325732
t = btf_type_by_id(btf, func_id);
@@ -5753,6 +5753,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
57535753
return -EINVAL;
57545754
}
57555755

5756+
/* Only kfunc can be release func */
5757+
if (is_kfunc)
5758+
rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
5759+
BTF_KFUNC_TYPE_RELEASE, func_id);
57565760
/* check that BTF function arguments match actual types that the
57575761
* verifier sees.
57585762
*/
@@ -5776,6 +5780,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
57765780

57775781
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
57785782
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
5783+
5784+
ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE, rel);
5785+
if (ret < 0)
5786+
return ret;
5787+
57795788
if (btf_get_prog_ctx_type(log, btf, t,
57805789
env->prog->type, i)) {
57815790
/* If function expects ctx type in BTF check that caller
@@ -5787,8 +5796,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
57875796
i, btf_type_str(t));
57885797
return -EINVAL;
57895798
}
5790-
if (check_ptr_off_reg(env, reg, regno))
5791-
return -EINVAL;
57925799
} else if (is_kfunc && (reg->type == PTR_TO_BTF_ID ||
57935800
(reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) {
57945801
const struct btf_type *reg_ref_t;
@@ -5806,7 +5813,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
58065813
if (reg->type == PTR_TO_BTF_ID) {
58075814
reg_btf = reg->btf;
58085815
reg_ref_id = reg->btf_id;
5809-
/* Ensure only one argument is referenced PTR_TO_BTF_ID */
5816+
/* Ensure only one argument is referenced
5817+
* PTR_TO_BTF_ID, check_func_arg_reg_off relies
5818+
* on only one referenced register being allowed
5819+
* for kfuncs.
5820+
*/
58105821
if (reg->ref_obj_id) {
58115822
if (ref_obj_id) {
58125823
bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
@@ -5888,18 +5899,15 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
58885899

58895900
/* Either both are set, or neither */
58905901
WARN_ON_ONCE((ref_obj_id && !ref_regno) || (!ref_obj_id && ref_regno));
5891-
if (is_kfunc) {
5892-
rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
5893-
BTF_KFUNC_TYPE_RELEASE, func_id);
5894-
/* We already made sure ref_obj_id is set only for one argument */
5895-
if (rel && !ref_obj_id) {
5896-
bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
5897-
func_name);
5898-
return -EINVAL;
5899-
}
5900-
/* Allow (!rel && ref_obj_id), so that passing such referenced PTR_TO_BTF_ID to
5901-
* other kfuncs works
5902-
*/
5902+
/* We already made sure ref_obj_id is set only for one argument. We do
5903+
* allow (!rel && ref_obj_id), so that passing such referenced
5904+
* PTR_TO_BTF_ID to other kfuncs works. Note that rel is only true when
5905+
* is_kfunc is true.
5906+
*/
5907+
if (rel && !ref_obj_id) {
5908+
bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
5909+
func_name);
5910+
return -EINVAL;
59035911
}
59045912
/* returns argument register number > 0 in case of reference release kfunc */
59055913
return rel ? ref_regno : 0;

kernel/bpf/verifier.c

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3990,6 +3990,12 @@ static int __check_ptr_off_reg(struct bpf_verifier_env *env,
39903990
* is only allowed in its original, unmodified form.
39913991
*/
39923992

3993+
if (reg->off < 0) {
3994+
verbose(env, "negative offset %s ptr R%d off=%d disallowed\n",
3995+
reg_type_str(env, reg->type), regno, reg->off);
3996+
return -EACCES;
3997+
}
3998+
39933999
if (!fixed_off_ok && reg->off) {
39944000
verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n",
39954001
reg_type_str(env, reg->type), regno, reg->off);
@@ -5359,6 +5365,60 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
53595365
return 0;
53605366
}
53615367

5368+
int check_func_arg_reg_off(struct bpf_verifier_env *env,
5369+
const struct bpf_reg_state *reg, int regno,
5370+
enum bpf_arg_type arg_type,
5371+
bool is_release_func)
5372+
{
5373+
bool fixed_off_ok = false, release_reg;
5374+
enum bpf_reg_type type = reg->type;
5375+
5376+
switch ((u32)type) {
5377+
case SCALAR_VALUE:
5378+
/* Pointer types where reg offset is explicitly allowed: */
5379+
case PTR_TO_PACKET:
5380+
case PTR_TO_PACKET_META:
5381+
case PTR_TO_MAP_KEY:
5382+
case PTR_TO_MAP_VALUE:
5383+
case PTR_TO_MEM:
5384+
case PTR_TO_MEM | MEM_RDONLY:
5385+
case PTR_TO_MEM | MEM_ALLOC:
5386+
case PTR_TO_BUF:
5387+
case PTR_TO_BUF | MEM_RDONLY:
5388+
case PTR_TO_STACK:
5389+
/* Some of the argument types nevertheless require a
5390+
* zero register offset.
5391+
*/
5392+
if (arg_type != ARG_PTR_TO_ALLOC_MEM)
5393+
return 0;
5394+
break;
5395+
/* All the rest must be rejected, except PTR_TO_BTF_ID which allows
5396+
* fixed offset.
5397+
*/
5398+
case PTR_TO_BTF_ID:
5399+
/* When referenced PTR_TO_BTF_ID is passed to release function,
5400+
* it's fixed offset must be 0. We rely on the property that
5401+
* only one referenced register can be passed to BPF helpers and
5402+
* kfuncs. In the other cases, fixed offset can be non-zero.
5403+
*/
5404+
release_reg = is_release_func && reg->ref_obj_id;
5405+
if (release_reg && reg->off) {
5406+
verbose(env, "R%d must have zero offset when passed to release func\n",
5407+
regno);
5408+
return -EINVAL;
5409+
}
5410+
/* For release_reg == true, fixed_off_ok must be false, but we
5411+
* already checked and rejected reg->off != 0 above, so set to
5412+
* true to allow fixed offset for all other cases.
5413+
*/
5414+
fixed_off_ok = true;
5415+
break;
5416+
default:
5417+
break;
5418+
}
5419+
return __check_ptr_off_reg(env, reg, regno, fixed_off_ok);
5420+
}
5421+
53625422
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
53635423
struct bpf_call_arg_meta *meta,
53645424
const struct bpf_func_proto *fn)
@@ -5408,36 +5468,14 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
54085468
if (err)
54095469
return err;
54105470

5411-
switch ((u32)type) {
5412-
case SCALAR_VALUE:
5413-
/* Pointer types where reg offset is explicitly allowed: */
5414-
case PTR_TO_PACKET:
5415-
case PTR_TO_PACKET_META:
5416-
case PTR_TO_MAP_KEY:
5417-
case PTR_TO_MAP_VALUE:
5418-
case PTR_TO_MEM:
5419-
case PTR_TO_MEM | MEM_RDONLY:
5420-
case PTR_TO_MEM | MEM_ALLOC:
5421-
case PTR_TO_BUF:
5422-
case PTR_TO_BUF | MEM_RDONLY:
5423-
case PTR_TO_STACK:
5424-
/* Some of the argument types nevertheless require a
5425-
* zero register offset.
5426-
*/
5427-
if (arg_type == ARG_PTR_TO_ALLOC_MEM)
5428-
goto force_off_check;
5429-
break;
5430-
/* All the rest must be rejected: */
5431-
default:
5432-
force_off_check:
5433-
err = __check_ptr_off_reg(env, reg, regno,
5434-
type == PTR_TO_BTF_ID);
5435-
if (err < 0)
5436-
return err;
5437-
break;
5438-
}
5471+
err = check_func_arg_reg_off(env, reg, regno, arg_type, is_release_function(meta->func_id));
5472+
if (err)
5473+
return err;
54395474

54405475
skip_type_check:
5476+
/* check_func_arg_reg_off relies on only one referenced register being
5477+
* allowed for BPF helpers.
5478+
*/
54415479
if (reg->ref_obj_id) {
54425480
if (meta->ref_obj_id) {
54435481
verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",

net/bpf/test_run.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ static int bpf_test_finish(const union bpf_attr *kattr,
201201
* future.
202202
*/
203203
__diag_push();
204-
__diag_ignore(GCC, 8, "-Wmissing-prototypes",
205-
"Global functions as their definitions will be in vmlinux BTF");
204+
__diag_ignore_all("-Wmissing-prototypes",
205+
"Global functions as their definitions will be in vmlinux BTF");
206206
int noinline bpf_fentry_test1(int a)
207207
{
208208
return a + 1;
@@ -270,9 +270,14 @@ struct sock * noinline bpf_kfunc_call_test3(struct sock *sk)
270270
return sk;
271271
}
272272

273+
struct prog_test_member {
274+
u64 c;
275+
};
276+
273277
struct prog_test_ref_kfunc {
274278
int a;
275279
int b;
280+
struct prog_test_member memb;
276281
struct prog_test_ref_kfunc *next;
277282
};
278283

@@ -295,6 +300,10 @@ noinline void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p)
295300
{
296301
}
297302

303+
noinline void bpf_kfunc_call_memb_release(struct prog_test_member *p)
304+
{
305+
}
306+
298307
struct prog_test_pass1 {
299308
int x0;
300309
struct {
@@ -379,6 +388,7 @@ BTF_ID(func, bpf_kfunc_call_test2)
379388
BTF_ID(func, bpf_kfunc_call_test3)
380389
BTF_ID(func, bpf_kfunc_call_test_acquire)
381390
BTF_ID(func, bpf_kfunc_call_test_release)
391+
BTF_ID(func, bpf_kfunc_call_memb_release)
382392
BTF_ID(func, bpf_kfunc_call_test_pass_ctx)
383393
BTF_ID(func, bpf_kfunc_call_test_pass1)
384394
BTF_ID(func, bpf_kfunc_call_test_pass2)
@@ -396,6 +406,7 @@ BTF_SET_END(test_sk_acquire_kfunc_ids)
396406

397407
BTF_SET_START(test_sk_release_kfunc_ids)
398408
BTF_ID(func, bpf_kfunc_call_test_release)
409+
BTF_ID(func, bpf_kfunc_call_memb_release)
399410
BTF_SET_END(test_sk_release_kfunc_ids)
400411

401412
BTF_SET_START(test_sk_ret_null_kfunc_ids)

net/netfilter/nf_conntrack_bpf.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/btf_ids.h>
1313
#include <linux/net_namespace.h>
1414
#include <net/netfilter/nf_conntrack.h>
15+
#include <net/netfilter/nf_conntrack_bpf.h>
1516
#include <net/netfilter/nf_conntrack_core.h>
1617

1718
/* bpf_ct_opts - Options for CT lookup helpers
@@ -102,8 +103,8 @@ static struct nf_conn *__bpf_nf_ct_lookup(struct net *net,
102103
}
103104

104105
__diag_push();
105-
__diag_ignore(GCC, 8, "-Wmissing-prototypes",
106-
"Global functions as their definitions will be in nf_conntrack BTF");
106+
__diag_ignore_all("-Wmissing-prototypes",
107+
"Global functions as their definitions will be in nf_conntrack BTF");
107108

108109
/* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a
109110
* reference to it

tools/testing/selftests/bpf/verifier/bounds_deduction.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
BPF_EXIT_INSN(),
106106
},
107107
.errstr_unpriv = "R1 has pointer with unsupported alu operation",
108-
.errstr = "dereference of modified ctx ptr",
108+
.errstr = "negative offset ctx ptr R1 off=-1 disallowed",
109109
.result = REJECT,
110110
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
111111
},

0 commit comments

Comments
 (0)