Skip to content

Commit e2cf913

Browse files
author
Alexei Starovoitov
committed
Merge branch 'fixes-for-stack-with-allow_ptr_leaks'
Kumar Kartikeya Dwivedi says: ==================== Fixes for stack with allow_ptr_leaks Two fixes for usability/correctness gaps when interacting with the stack without CAP_PERFMON (i.e. with allow_ptr_leaks = false). See the commits for details. I've verified that the tests fail when run without the fixes. Changelog: ---------- v3 -> v4 v3: https://lore.kernel.org/bpf/[email protected] * Address Andrii's comments * Fix bug paperered over by missing CAP_NET_ADMIN in verifier_mtu test * Add warning when undefined CAP_ constant is specified, and fail test * Reorder annotations to be more clear * Verify that fixes fail without patches again * Add Acked-by from Andrii v2 -> v3 v2: https://lore.kernel.org/bpf/[email protected] * Address comments from Eduard * Fix comment for mark_stack_slot_misc * We can simply always return early when stype == STACK_INVALID * Drop allow_ptr_leaks conditionals * Add Eduard's __caps_unpriv patch into the series * Convert test_verifier_mtu to use it * Move existing tests to __caps_unpriv annotation and verifier_spill_fill.c * Add Acked-by from Eduard v1 -> v2 v1: https://lore.kernel.org/bpf/[email protected] * Fix CI errors in selftest by removing dependence on BPF_ST ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 5a6ea70 + 19b6dbc commit e2cf913

File tree

6 files changed

+104
-22
lines changed

6 files changed

+104
-22
lines changed

kernel/bpf/verifier.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -1202,14 +1202,17 @@ static bool is_spilled_scalar_reg64(const struct bpf_stack_state *stack)
12021202
/* Mark stack slot as STACK_MISC, unless it is already STACK_INVALID, in which
12031203
* case they are equivalent, or it's STACK_ZERO, in which case we preserve
12041204
* more precise STACK_ZERO.
1205-
* Note, in uprivileged mode leaving STACK_INVALID is wrong, so we take
1206-
* env->allow_ptr_leaks into account and force STACK_MISC, if necessary.
1205+
* Regardless of allow_ptr_leaks setting (i.e., privileged or unprivileged
1206+
* mode), we won't promote STACK_INVALID to STACK_MISC. In privileged case it is
1207+
* unnecessary as both are considered equivalent when loading data and pruning,
1208+
* in case of unprivileged mode it will be incorrect to allow reads of invalid
1209+
* slots.
12071210
*/
12081211
static void mark_stack_slot_misc(struct bpf_verifier_env *env, u8 *stype)
12091212
{
12101213
if (*stype == STACK_ZERO)
12111214
return;
1212-
if (env->allow_ptr_leaks && *stype == STACK_INVALID)
1215+
if (*stype == STACK_INVALID)
12131216
return;
12141217
*stype = STACK_MISC;
12151218
}
@@ -4700,6 +4703,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
47004703
*/
47014704
if (!env->allow_ptr_leaks &&
47024705
is_spilled_reg(&state->stack[spi]) &&
4706+
!is_spilled_scalar_reg(&state->stack[spi]) &&
47034707
size != BPF_REG_SIZE) {
47044708
verbose(env, "attempt to corrupt spilled pointer on stack\n");
47054709
return -EACCES;

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

+1-18
Original file line numberDiff line numberDiff line change
@@ -225,24 +225,7 @@ void test_verifier_xdp(void) { RUN(verifier_xdp); }
225225
void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
226226
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
227227
void test_verifier_lsm(void) { RUN(verifier_lsm); }
228-
229-
void test_verifier_mtu(void)
230-
{
231-
__u64 caps = 0;
232-
int ret;
233-
234-
/* In case CAP_BPF and CAP_PERFMON is not set */
235-
ret = cap_enable_effective(1ULL << CAP_BPF | 1ULL << CAP_NET_ADMIN, &caps);
236-
if (!ASSERT_OK(ret, "set_cap_bpf_cap_net_admin"))
237-
return;
238-
ret = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL);
239-
if (!ASSERT_OK(ret, "disable_cap_sys_admin"))
240-
goto restore_cap;
241-
RUN(verifier_mtu);
242-
restore_cap:
243-
if (caps)
244-
cap_enable_effective(caps, NULL);
245-
}
228+
void test_verifier_mtu(void) { RUN(verifier_mtu); }
246229

247230
static int init_test_val_map(struct bpf_object *obj, char *map_name)
248231
{

tools/testing/selftests/bpf/progs/bpf_misc.h

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#define XSTR(s) STR(s)
66
#define STR(s) #s
77

8+
/* Expand a macro and then stringize the expansion */
9+
#define QUOTE(str) #str
10+
#define EXPAND_QUOTE(str) QUOTE(str)
11+
812
/* This set of attributes controls behavior of the
913
* test_loader.c:test_loader__run_subtests().
1014
*
@@ -106,6 +110,7 @@
106110
* __arch_* Specify on which architecture the test case should be tested.
107111
* Several __arch_* annotations could be specified at once.
108112
* When test case is not run on current arch it is marked as skipped.
113+
* __caps_unpriv Specify the capabilities that should be set when running the test.
109114
*/
110115
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
111116
#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
@@ -129,6 +134,13 @@
129134
#define __arch_x86_64 __arch("X86_64")
130135
#define __arch_arm64 __arch("ARM64")
131136
#define __arch_riscv64 __arch("RISCV64")
137+
#define __caps_unpriv(caps) __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps))))
138+
139+
/* Define common capabilities tested using __caps_unpriv */
140+
#define CAP_NET_ADMIN 12
141+
#define CAP_SYS_ADMIN 21
142+
#define CAP_PERFMON 38
143+
#define CAP_BPF 39
132144

133145
/* Convenience macro for use with 'asm volatile' blocks */
134146
#define __naked __attribute__((naked))

tools/testing/selftests/bpf/progs/verifier_mtu.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
SEC("tc/ingress")
88
__description("uninit/mtu: write rejected")
9-
__failure __msg("invalid indirect read from stack")
9+
__success
10+
__caps_unpriv(CAP_BPF|CAP_NET_ADMIN)
11+
__failure_unpriv __msg_unpriv("invalid indirect read from stack")
1012
int tc_uninit_mtu(struct __sk_buff *ctx)
1113
{
1214
__u32 mtu;

tools/testing/selftests/bpf/progs/verifier_spill_fill.c

+35
Original file line numberDiff line numberDiff line change
@@ -1244,4 +1244,39 @@ __naked void old_stack_misc_vs_cur_ctx_ptr(void)
12441244
: __clobber_all);
12451245
}
12461246

1247+
SEC("socket")
1248+
__description("stack_noperfmon: reject read of invalid slots")
1249+
__success
1250+
__caps_unpriv(CAP_BPF)
1251+
__failure_unpriv __msg_unpriv("invalid read from stack off -8+1 size 8")
1252+
__naked void stack_noperfmon_reject_invalid_read(void)
1253+
{
1254+
asm volatile (" \
1255+
r2 = 1; \
1256+
r6 = r10; \
1257+
r6 += -8; \
1258+
*(u8 *)(r6 + 0) = r2; \
1259+
r2 = *(u64 *)(r6 + 0); \
1260+
r0 = 0; \
1261+
exit; \
1262+
" ::: __clobber_all);
1263+
}
1264+
1265+
SEC("socket")
1266+
__description("stack_noperfmon: narrow spill onto 64-bit scalar spilled slots")
1267+
__success
1268+
__caps_unpriv(CAP_BPF)
1269+
__success_unpriv
1270+
__naked void stack_noperfmon_spill_32bit_onto_64bit_slot(void)
1271+
{
1272+
asm volatile(" \
1273+
r0 = 0; \
1274+
*(u64 *)(r10 - 8) = r0; \
1275+
*(u32 *)(r10 - 8) = r0; \
1276+
exit; \
1277+
" :
1278+
:
1279+
: __clobber_all);
1280+
}
1281+
12471282
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/test_loader.c

+46
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#define TEST_TAG_ARCH "comment:test_arch="
3737
#define TEST_TAG_JITED_PFX "comment:test_jited="
3838
#define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv="
39+
#define TEST_TAG_CAPS_UNPRIV "comment:test_caps_unpriv="
3940

4041
/* Warning: duplicated in bpf_misc.h */
4142
#define POINTER_VALUE 0xcafe4all
@@ -74,6 +75,7 @@ struct test_subspec {
7475
struct expected_msgs jited;
7576
int retval;
7677
bool execute;
78+
__u64 caps;
7779
};
7880

7981
struct test_spec {
@@ -276,6 +278,37 @@ static int parse_int(const char *str, int *val, const char *name)
276278
return 0;
277279
}
278280

281+
static int parse_caps(const char *str, __u64 *val, const char *name)
282+
{
283+
int cap_flag = 0;
284+
char *token = NULL, *saveptr = NULL;
285+
286+
char *str_cpy = strdup(str);
287+
if (str_cpy == NULL) {
288+
PRINT_FAIL("Memory allocation failed\n");
289+
return -EINVAL;
290+
}
291+
292+
token = strtok_r(str_cpy, "|", &saveptr);
293+
while (token != NULL) {
294+
errno = 0;
295+
if (!strncmp("CAP_", token, sizeof("CAP_") - 1)) {
296+
PRINT_FAIL("define %s constant in bpf_misc.h, failed to parse caps\n", token);
297+
return -EINVAL;
298+
}
299+
cap_flag = strtol(token, NULL, 10);
300+
if (!cap_flag || errno) {
301+
PRINT_FAIL("failed to parse caps %s\n", name);
302+
return -EINVAL;
303+
}
304+
*val |= (1ULL << cap_flag);
305+
token = strtok_r(NULL, "|", &saveptr);
306+
}
307+
308+
free(str_cpy);
309+
return 0;
310+
}
311+
279312
static int parse_retval(const char *str, int *val, const char *name)
280313
{
281314
struct {
@@ -541,6 +574,12 @@ static int parse_test_spec(struct test_loader *tester,
541574
jit_on_next_line = true;
542575
} else if (str_has_pfx(s, TEST_BTF_PATH)) {
543576
spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
577+
} else if (str_has_pfx(s, TEST_TAG_CAPS_UNPRIV)) {
578+
val = s + sizeof(TEST_TAG_CAPS_UNPRIV) - 1;
579+
err = parse_caps(val, &spec->unpriv.caps, "test caps");
580+
if (err)
581+
goto cleanup;
582+
spec->mode_mask |= UNPRIV;
544583
}
545584
}
546585

@@ -917,6 +956,13 @@ void run_subtest(struct test_loader *tester,
917956
test__end_subtest();
918957
return;
919958
}
959+
if (subspec->caps) {
960+
err = cap_enable_effective(subspec->caps, NULL);
961+
if (err) {
962+
PRINT_FAIL("failed to set capabilities: %i, %s\n", err, strerror(err));
963+
goto subtest_cleanup;
964+
}
965+
}
920966
}
921967

922968
/* Implicitly reset to NULL if next test case doesn't specify */

0 commit comments

Comments
 (0)