Skip to content

Commit a523e82

Browse files
committed
selftests/bpf: check operations on untrusted ro pointers to mem
The following cases are tested: - it is ok to load memory at any offset from rdonly_untrusted_mem; - rdonly_untrusted_mem offset/bounds are not tracked; - writes into rdonly_untrusted_mem are forbidden; - atomic operations on rdonly_untrusted_mem are forbidden; - rdonly_untrusted_mem can't be passed as a memory argument of a helper of kfunc; - it is ok to use PTR_TO_MEM and PTR_TO_BTF_ID in a same load instruction. Signed-off-by: Eduard Zingerman <[email protected]>
1 parent d4cb6ec commit a523e82

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "verifier_may_goto_1.skel.h"
5858
#include "verifier_may_goto_2.skel.h"
5959
#include "verifier_meta_access.skel.h"
60+
#include "verifier_mem_rdonly_untrusted.skel.h"
6061
#include "verifier_movsx.skel.h"
6162
#include "verifier_mtu.skel.h"
6263
#include "verifier_netfilter_ctx.skel.h"
@@ -205,6 +206,7 @@ void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup)
205206
void test_verifier_private_stack(void) { RUN(verifier_private_stack); }
206207
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
207208
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
209+
void test_verifier_mem_rdonly_untrusted(void) { RUN_FULL_CAPS(verifier_mem_rdonly_untrusted); }
208210
void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); }
209211
void test_verifier_ref_tracking(void) { RUN(verifier_ref_tracking); }
210212
void test_verifier_regalloc(void) { RUN(verifier_regalloc); }
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_core_read.h>
5+
#include "bpf_misc.h"
6+
#include "../test_kmods/bpf_testmod_kfunc.h"
7+
8+
SEC("socket")
9+
__success
10+
__retval(0)
11+
int ldx_is_ok_bad_addr(void *ctx)
12+
{
13+
char *p;
14+
15+
if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID))
16+
return 42;
17+
18+
p = bpf_rdonly_cast(0, 0);
19+
return p[0x7fff];
20+
}
21+
22+
SEC("socket")
23+
__success
24+
__retval(1)
25+
int ldx_is_ok_good_addr(void *ctx)
26+
{
27+
int v, *p;
28+
29+
v = 1;
30+
p = bpf_rdonly_cast(&v, 0);
31+
return *p;
32+
}
33+
34+
SEC("socket")
35+
__success
36+
int offset_not_tracked(void *ctx)
37+
{
38+
int *p, i, s;
39+
40+
p = bpf_rdonly_cast(0, 0);
41+
s = 0;
42+
bpf_for(i, 0, 1000 * 1000 * 1000) {
43+
p++;
44+
s += *p;
45+
}
46+
return s;
47+
}
48+
49+
SEC("socket")
50+
__failure
51+
__msg("cannot write into rdonly_untrusted_mem")
52+
int stx_not_ok(void *ctx)
53+
{
54+
int v, *p;
55+
56+
v = 1;
57+
p = bpf_rdonly_cast(&v, 0);
58+
*p = 1;
59+
return 0;
60+
}
61+
62+
SEC("socket")
63+
__failure
64+
__msg("cannot write into rdonly_untrusted_mem")
65+
int atomic_not_ok(void *ctx)
66+
{
67+
int v, *p;
68+
69+
v = 1;
70+
p = bpf_rdonly_cast(&v, 0);
71+
__sync_fetch_and_add(p, 1);
72+
return 0;
73+
}
74+
75+
SEC("socket")
76+
__failure
77+
__msg("cannot write into rdonly_untrusted_mem")
78+
int atomic_rmw_not_ok(void *ctx)
79+
{
80+
long v, *p;
81+
82+
v = 1;
83+
p = bpf_rdonly_cast(&v, 0);
84+
return __sync_val_compare_and_swap(p, 0, 42);
85+
}
86+
87+
SEC("socket")
88+
__failure
89+
__msg("invalid access to memory, mem_size=0 off=0 size=4")
90+
__msg("R1 min value is outside of the allowed memory range")
91+
int kfunc_param_not_ok(void *ctx)
92+
{
93+
int *p;
94+
95+
p = bpf_rdonly_cast(0, 0);
96+
bpf_kfunc_trusted_num_test(p);
97+
return 0;
98+
}
99+
100+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
101+
__failure
102+
__msg("R1 type=rdonly_untrusted_mem expected=")
103+
int helper_param_not_ok(void *ctx)
104+
{
105+
char *p;
106+
107+
p = bpf_rdonly_cast(0, 0);
108+
/*
109+
* Any helper with ARG_CONST_SIZE_OR_ZERO constraint will do,
110+
* the most permissive constraint
111+
*/
112+
bpf_copy_from_user(p, 0, (void *)42);
113+
return 0;
114+
}
115+
116+
static __noinline u64 *get_some_addr(void)
117+
{
118+
if (bpf_get_prandom_u32())
119+
return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock));
120+
else
121+
return bpf_rdonly_cast(0, 0);
122+
}
123+
124+
SEC("socket")
125+
__success
126+
__retval(0)
127+
int mixed_mem_type(void *ctx)
128+
{
129+
u64 *p;
130+
131+
/* Try to avoid compiler hoisting load to if branches by using __noinline func. */
132+
p = get_some_addr();
133+
return *p;
134+
}
135+
136+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)