Skip to content

Commit b173ea5

Browse files
slava-at-csKernel Patches Daemon
authored andcommitted
selftests/bpf: Add test for bpftool access to read-only protected maps
Add selftest cases that validate bpftool's expected behavior when accessing maps protected from modification via security_bpf_map. The test includes a BPF program attached to security_bpf_map with two maps: - A protected map that only allows read-only access - An unprotected map that allows full access The test script attaches the BPF program to security_bpf_map and verifies that for the bpftool map command: - Read access works on both maps - Write access fails on the protected map - Write access succeeds on the unprotected map - These behaviors remain consistent when the maps are pinned Signed-off-by: Slava Imameev <[email protected]>
1 parent 3a98af7 commit b173ea5

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

tools/testing/selftests/bpf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ TEST_PROGS := test_kmod.sh \
109109
test_xdping.sh \
110110
test_bpftool_build.sh \
111111
test_bpftool.sh \
112+
test_bpftool_map.sh \
112113
test_bpftool_metadata.sh \
113114
test_doc_build.sh \
114115
test_xsk.sh \
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_tracing.h>
5+
#include <bpf/bpf_helpers.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
#define EPERM 1 /* Operation not permitted */
10+
11+
/* From include/linux/mm.h. */
12+
#define FMODE_WRITE 0x2
13+
14+
struct map;
15+
16+
struct {
17+
__uint(type, BPF_MAP_TYPE_ARRAY);
18+
__type(key, __u32);
19+
__type(value, __u32);
20+
__uint(max_entries, 1);
21+
} prot_map SEC(".maps");
22+
23+
struct {
24+
__uint(type, BPF_MAP_TYPE_ARRAY);
25+
__type(key, __u32);
26+
__type(value, __u32);
27+
__uint(max_entries, 1);
28+
} not_prot_map SEC(".maps");
29+
30+
SEC("fmod_ret/security_bpf_map")
31+
int BPF_PROG(fmod_bpf_map, struct bpf_map *map, int fmode)
32+
{
33+
if (map == &prot_map) {
34+
/* Allow read-only access */
35+
if (fmode & FMODE_WRITE)
36+
return -EPERM;
37+
}
38+
39+
return 0;
40+
}
41+
42+
/*
43+
* This program keeps references to maps. This is needed to prevent
44+
* optimizing them out.
45+
*/
46+
SEC("fentry/bpf_fentry_test1")
47+
int BPF_PROG(bpf_map_test0, int a)
48+
{
49+
__u32 key = 0;
50+
__u32 val1 = a;
51+
__u32 val2 = a + 1;
52+
53+
bpf_map_update_elem(&prot_map, &key, &val1, BPF_ANY);
54+
bpf_map_update_elem(&not_prot_map, &key, &val2, BPF_ANY);
55+
return 0;
56+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/bin/sh
2+
# SPDX-License-Identifier: GPL-2.0
3+
4+
# Kselftest framework requirement - SKIP code is 4.
5+
ksft_skip=4
6+
7+
PROTECTED_MAP_NAME="prot_map"
8+
NOT_PROTECTED_MAP_NAME="not_prot_map"
9+
BPF_FILE="security_bpf_map.bpf.o"
10+
TESTNAME="security_bpf_map"
11+
BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts)
12+
BPF_DIR="$BPF_FS/test_$TESTNAME"
13+
SCRIPT_DIR=$(dirname $(realpath "$0"))
14+
BPF_FILE_PATH="$SCRIPT_DIR/$BPF_FILE"
15+
# Assume the script is located under tools/testing/selftests/bpf/
16+
KDIR_ROOT_DIR=$(realpath "$SCRIPT_DIR"/../../../../)
17+
18+
_cleanup()
19+
{
20+
set +eu
21+
[ -d "$TMPDIR" ] && rm -rf "$TMPDIR" 2> /dev/null
22+
[ -d "$BPF_DIR" ] && rm -rf "$BPF_DIR" 2> /dev/null
23+
}
24+
25+
cleanup_skip()
26+
{
27+
echo "selftests: $TESTNAME [SKIP]"
28+
_cleanup
29+
30+
exit $ksft_skip
31+
}
32+
33+
cleanup()
34+
{
35+
if [ "$?" = 0 ]; then
36+
echo "selftests: $TESTNAME [PASS]"
37+
else
38+
echo "selftests: $TESTNAME [FAILED]"
39+
fi
40+
_cleanup
41+
}
42+
43+
# Parameters:
44+
# $1: The top of kernel repository
45+
# $2: Output directory
46+
build_bpftool()
47+
{
48+
local kdir_root_dir="$1"
49+
local output_dir="$2"
50+
local pwd="$(pwd)"
51+
local ncpus=1
52+
53+
echo Building bpftool ...
54+
55+
#We want to start build from the top of kernel repository.
56+
cd "$kdir_root_dir"
57+
if [ ! -e tools/bpf/bpftool/Makefile ]; then
58+
echo bpftool files not found
59+
exit $ksft_skip
60+
fi
61+
62+
# Determine the number of CPUs for parallel compilation
63+
if command -v nproc >/dev/null 2>&1; then
64+
ncpus=$(nproc)
65+
fi
66+
67+
make -C tools/bpf/bpftool -s -j"$ncpus" OUTPUT="$output_dir"/ >/dev/null
68+
echo ... finished building bpftool
69+
cd "$pwd"
70+
}
71+
72+
# Function to test map access with configurable write expectations
73+
# Parameters:
74+
# $1: Map name
75+
# $2: Whether write should succeed (true/false)
76+
# $3: bpftool path
77+
# $4: BPF_DIR
78+
test_map_access() {
79+
local map_name="$1"
80+
local write_should_succeed="$2"
81+
local bpftool_path="$3"
82+
local pin_path="$4/${map_name}_pinned"
83+
local key="0 0 0 0"
84+
local value="1 1 1 1"
85+
86+
echo "Testing access to map: $map_name"
87+
88+
# Test read access to the map
89+
if "$bpftool_path" map lookup name "$map_name" key $key; then
90+
echo " Read access to $map_name succeeded"
91+
else
92+
echo " Read access to $map_name failed"
93+
exit 1
94+
fi
95+
96+
# Test write access to the map
97+
if "$bpftool_path" map update name "$map_name" key $key value $value; then
98+
if [ "$write_should_succeed" = "true" ]; then
99+
echo " Write access to $map_name succeeded as expected"
100+
else
101+
echo " Write access to $map_name succeeded but should have failed"
102+
exit 1
103+
fi
104+
else
105+
if [ "$write_should_succeed" = "true" ]; then
106+
echo " Write access to $map_name failed but should have succeeded"
107+
exit 1
108+
else
109+
echo " Write access to $map_name failed as expected"
110+
fi
111+
fi
112+
113+
# Pin the map to the BPF filesystem
114+
"$bpftool_path" map pin name "$map_name" "$pin_path"
115+
if [ -e "$pin_path" ]; then
116+
echo " Successfully pinned $map_name to $pin_path"
117+
else
118+
echo " Failed to pin $map_name"
119+
exit 1
120+
fi
121+
122+
# Test read access to the pinned map
123+
if "$bpftool_path" map lookup pinned "$pin_path" key $key; then
124+
echo " Read access to pinned $map_name succeeded"
125+
else
126+
echo " Read access to pinned $map_name failed"
127+
exit 1
128+
fi
129+
130+
# Test write access to the pinned map
131+
if "$bpftool_path" map update pinned "$pin_path" key $key value $value; then
132+
if [ "$write_should_succeed" = "true" ]; then
133+
echo " Write access to pinned $map_name succeeded as expected"
134+
else
135+
echo " Write access to pinned $map_name succeeded but should have failed"
136+
exit 1
137+
fi
138+
else
139+
if [ "$write_should_succeed" = "true" ]; then
140+
echo " Write access to pinned $map_name failed but should have succeeded"
141+
exit 1
142+
else
143+
echo " Write access to pinned $map_name failed as expected"
144+
fi
145+
fi
146+
147+
echo " Finished testing $map_name"
148+
echo
149+
}
150+
151+
check_root_privileges() {
152+
if [ $(id -u) -ne 0 ]; then
153+
echo "Need root privileges"
154+
exit $ksft_skip
155+
fi
156+
}
157+
158+
check_bpffs() {
159+
if [ -z "$BPF_FS" ]; then
160+
echo "Could not run test without bpffs mounted"
161+
exit $ksft_skip
162+
fi
163+
}
164+
165+
create_tmp_dir() {
166+
TMPDIR=$(mktemp -d)
167+
if [ $? -ne 0 ] || [ ! -d "$TMPDIR" ]; then
168+
echo "Failed to create temporary directory"
169+
exit $ksft_skip
170+
fi
171+
}
172+
173+
locate_or_build_bpftool() {
174+
if ! bpftool version > /dev/null 2>&1; then
175+
build_bpftool "$KDIR_ROOT_DIR" "$TMPDIR"
176+
BPFTOOL_PATH="$TMPDIR"/bpftool
177+
else
178+
echo "Using bpftool from PATH"
179+
BPFTOOL_PATH="bpftool"
180+
fi
181+
}
182+
183+
set -eu
184+
185+
trap cleanup_skip EXIT
186+
187+
check_root_privileges
188+
189+
check_bpffs
190+
191+
create_tmp_dir
192+
193+
locate_or_build_bpftool
194+
195+
mkdir "$BPF_DIR"
196+
197+
trap cleanup EXIT
198+
199+
# Load and attach the BPF programs to control maps access
200+
"$BPFTOOL_PATH" prog loadall "$BPF_FILE_PATH" "$BPF_DIR"/prog autoattach
201+
202+
# Test protected map (write should fail)
203+
test_map_access "$PROTECTED_MAP_NAME" "false" "$BPFTOOL_PATH" "$BPF_DIR"
204+
205+
# Test not protected map (write should succeed)
206+
test_map_access "$NOT_PROTECTED_MAP_NAME" "true" "$BPFTOOL_PATH" "$BPF_DIR"
207+
208+
exit 0

0 commit comments

Comments
 (0)