All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf v2] bpf: Check variable-offset accesses against special fields
@ 2026-11-08 15:29 Shihuang Liu
  2026-11-08 16:01 ` bot+bpf-ci
  2026-11-08 16:11 ` Alexei Starovoitov
  0 siblings, 2 replies; 4+ messages in thread
From: Shihuang Liu @ 2026-11-08 15:29 UTC (permalink / raw)
  To: bpf
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, Shihuang Liu

btf_struct_access() rejects direct access to special fields (kptr,
spin_lock, list/rbtree nodes, ...) of program allocated objects, but
for a variable offset it only checks min_off.

With compiler-generated BTF the check at min_off is enough: the
register cannot take every value in [min_off, max_off], since
check_variable_offset() keeps the access inside one array with a
step that is a multiple of the element size, and btf_repeat_fields()
repeats the special fields for every element.  Every reachable
offset therefore selects the same member of some element.

The check breaks for hand-written BTF with overlapping members.
btf_struct_check_meta() only rejects offsets that go backwards, so
this layout is accepted:

  struct node {
          int arr[4];             /* offset 0, size 16 */
          struct foo __kptr *p;   /* offset 8 */
  };

A 4-byte store to arr[i], i in [0, 3], passes the check at offset 0 and
overwrites p at run time when i == 2 or 3; the release path
(bpf_obj_free_fields()) then frees the corrupted pointer.

Check every offset the register can take (min_off + k * step)
against the record.  Constant offsets keep the existing check.
Since only reachable offsets are checked, accesses that stay on a
non-special member of every element (e.g. a store to arr[i].data
with step == sizeof(elem)) keep working.

Fixes: 89e72f81a455 ("bpf: Varying offset access support for PTR_TO_BTF_ID pointers")
Link: https://lore.kernel.org/bpf/20261107135419.81491-1-shlomojune6@gmail.com/
Signed-off-by: Shihuang Liu <shlomojune6@gmail.com>
---
Changes since v1:
- Check only the reachable offsets (min_off + k * step) instead of the
  whole [min_off, max_off + size) range
- Rework the commit message

 kernel/bpf/btf.c | 42 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 35 insertions(+), 7 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 8bf39098ba97a..738625d8e5879 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8056,6 +8056,21 @@ error:
 	return -EINVAL;
 }
 
+static bool btf_field_is_accessed(const struct bpf_reg_state *reg,
+				  s64 min_off, s64 max_off, int size,
+				  const struct btf_field *field)
+{
+	s64 first, last;
+
+	first = max_t(s64, min_off, (s64)field->offset - size + 1);
+	last = min_t(s64, max_off, (s64)field->offset + field->size - 1);
+	if (first > last)
+		return false;
+
+	first += imod(min_off - first, reg->step);
+	return first <= last;
+}
+
 int btf_struct_access(struct bpf_verifier_log *log,
 		      const struct bpf_reg_state *reg,
 		      int _off, int size, enum bpf_access_type atype __maybe_unused,
@@ -8091,15 +8106,28 @@ int btf_struct_access(struct bpf_verifier_log *log,
 		if (!meta)
 			break;
 		rec = meta->record;
+		if (min_off == max_off) {
+			for (i = 0; i < rec->cnt; i++) {
+				struct btf_field *field = &rec->fields[i];
+				u32 offset = field->offset;
+
+				if (off < offset + field->size && offset < off + size) {
+					bpf_log(log, "direct access to %s is disallowed\n",
+						btf_field_type_name(field->type));
+					return -EACCES;
+				}
+			}
+			break;
+		}
+
 		for (i = 0; i < rec->cnt; i++) {
 			struct btf_field *field = &rec->fields[i];
-			u32 offset = field->offset;
-			if (off < offset + field->size && offset < off + size) {
-				bpf_log(log,
-					"direct access to %s is disallowed\n",
-					btf_field_type_name(field->type));
-				return -EACCES;
-			}
+
+			if (!btf_field_is_accessed(reg, min_off, max_off, size, field))
+				continue;
+			bpf_log(log, "direct access to %s is disallowed\n",
+				btf_field_type_name(field->type));
+			return -EACCES;
 		}
 		break;
 	}

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-09  9:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-08 15:29 [PATCH bpf v2] bpf: Check variable-offset accesses against special fields Shihuang Liu
2026-11-08 16:01 ` bot+bpf-ci
2026-11-08 16:11 ` Alexei Starovoitov
2026-11-09  9:47   ` J4f J4f

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.