Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Allow narrow loads of bpf_sysctl fields with offset > 0

"ctx:file_pos sysctl:read read ok narrow" works on s390 by accident: it
reads the wrong byte, which happens to have the expected value of 0.
Improve the test by seeking to the 4th byte and expecting 4 instead of
0.

This makes the latent problem apparent: the test attempts to read the
first byte of bpf_sysctl.file_pos, assuming this is the least-significant
byte, which is not the case on big-endian machines: a non-zero offset is
needed.

The point of the test is to verify narrow loads, so we cannot cheat our
way out by simply using BPF_W. The existence of the test means that such
loads have to be supported, most likely because llvm can generate them.
Fix the test by adding a big-endian variant, which uses an offset to
access the least-significant byte of bpf_sysctl.file_pos.

This reveals the final problem: verifier rejects accesses to bpf_sysctl
fields with offset > 0. Such accesses are already allowed for a wide
range of structs: __sk_buff, bpf_sock_addr and sk_msg_md to name a few.
Extend this support to bpf_sysctl by using bpf_ctx_range instead of
offsetof when matching field offsets.

Fixes: 7b146cebe30c ("bpf: Sysctl hook")
Fixes: e1550bfe0de4 ("bpf: Add file_pos field to bpf_sysctl ctx")
Fixes: 9a1027e52535 ("selftests/bpf: Test file_pos field in bpf_sysctl ctx")
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrey Ignatov <rdna@fb.com>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20191028122902.9763-1-iii@linux.ibm.com

authored by

Ilya Leoshkevich and committed by
Alexei Starovoitov
7541c87c 050668c1

+9 -3
+2 -2
kernel/bpf/cgroup.c
··· 1311 1311 return false; 1312 1312 1313 1313 switch (off) { 1314 - case offsetof(struct bpf_sysctl, write): 1314 + case bpf_ctx_range(struct bpf_sysctl, write): 1315 1315 if (type != BPF_READ) 1316 1316 return false; 1317 1317 bpf_ctx_record_field_size(info, size_default); 1318 1318 return bpf_ctx_narrow_access_ok(off, size, size_default); 1319 - case offsetof(struct bpf_sysctl, file_pos): 1319 + case bpf_ctx_range(struct bpf_sysctl, file_pos): 1320 1320 if (type == BPF_READ) { 1321 1321 bpf_ctx_record_field_size(info, size_default); 1322 1322 return bpf_ctx_narrow_access_ok(off, size, size_default);
+7 -1
tools/testing/selftests/bpf/test_sysctl.c
··· 161 161 .descr = "ctx:file_pos sysctl:read read ok narrow", 162 162 .insns = { 163 163 /* If (file_pos == X) */ 164 + #if __BYTE_ORDER == __LITTLE_ENDIAN 164 165 BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_1, 165 166 offsetof(struct bpf_sysctl, file_pos)), 166 - BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0, 2), 167 + #else 168 + BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_1, 169 + offsetof(struct bpf_sysctl, file_pos) + 3), 170 + #endif 171 + BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 4, 2), 167 172 168 173 /* return ALLOW; */ 169 174 BPF_MOV64_IMM(BPF_REG_0, 1), ··· 181 176 .attach_type = BPF_CGROUP_SYSCTL, 182 177 .sysctl = "kernel/ostype", 183 178 .open_flags = O_RDONLY, 179 + .seek = 4, 184 180 .result = SUCCESS, 185 181 }, 186 182 {