scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the
pointer as the source register. The untrusted PTR_TO_MEM case returns there
without updating the scalar destination, leaving stale verifier state.
Handle the untrusted PTR_TO_MEM case before the early return. Addition and
pointer-minus-scalar subtraction preserve the pointer type and keep the
existing "do not track offsets" rule for untrusted memory.
Scalar-minus-pointer and unsupported pointer ALU operations are still
rejected instead of being accepted by the early return.
Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()")
Signed-off-by: Nuoqi Gui <[email protected]>
---
kernel/bpf/verifier.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index c8d980fdd709..26c67c53166b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13729,8 +13729,32 @@ static int adjust_ptr_min_max_vals(struct
bpf_verifier_env *env,
* Accesses to untrusted PTR_TO_MEM are done through probe
* instructions, hence no need to track offsets.
*/
- if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type &
PTR_UNTRUSTED))
- return 0;
+ if (base_type(ptr_reg->type) == PTR_TO_MEM &&
+ (ptr_reg->type & PTR_UNTRUSTED)) {
+ switch (opcode) {
+ case BPF_ADD:
+ *dst_reg = *ptr_reg;
+ return 0;
+ case BPF_SUB:
+ if (dst_reg == off_reg) {
+ verbose(env, "R%d tried to subtract pointer
from scalar\n",
+ dst);
+ return -EACCES;
+ }
+ *dst_reg = *ptr_reg;
+ return 0;
+ case BPF_AND:
+ case BPF_OR:
+ case BPF_XOR:
+ verbose(env, "R%d bitwise operator %s on pointer
prohibited\n",
+ dst, bpf_alu_string[opcode >> 4]);
+ return -EACCES;
+ default:
+ verbose(env, "R%d pointer arithmetic with %s operator
prohibited\n",
+ dst, bpf_alu_string[opcode >> 4]);
+ return -EACCES;
+ }
+ }
switch (base_type(ptr_reg->type)) {
case PTR_TO_CTX:
--
2.34.1