Commit 1c6d28a6 authored by Jiri Olsa's avatar Jiri Olsa Committed by Alexei Starovoitov

bpf: Factor btf_struct_access function

Adding btf_struct_walk function that walks through the
struct type + given offset and returns following values:

  enum bpf_struct_walk_result {
       /* < 0 error */
       WALK_SCALAR = 0,
       WALK_PTR,
       WALK_STRUCT,
  };

WALK_SCALAR - when SCALAR_VALUE is found
WALK_PTR    - when pointer value is found, its ID is stored
              in 'next_btf_id' output param
WALK_STRUCT - when nested struct object is found, its ID is stored
              in 'next_btf_id' output param

It will be used in following patches to get all nested
struct objects for given type and offset.

The btf_struct_access now calls btf_struct_walk function,
as long as it gets nested structs as return value.
Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Acked-by: default avatarAndrii Nakryiko <andriin@fb.com>
Link: https://lore.kernel.org/bpf/20200825192124.710397-8-jolsa@kernel.org
parent dafe58fc
...@@ -3886,16 +3886,22 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, ...@@ -3886,16 +3886,22 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
return true; return true;
} }
int btf_struct_access(struct bpf_verifier_log *log, enum bpf_struct_walk_result {
const struct btf_type *t, int off, int size, /* < 0 error */
enum bpf_access_type atype, WALK_SCALAR = 0,
u32 *next_btf_id) WALK_PTR,
WALK_STRUCT,
};
static int btf_struct_walk(struct bpf_verifier_log *log,
const struct btf_type *t, int off, int size,
u32 *next_btf_id)
{ {
u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
const struct btf_type *mtype, *elem_type = NULL; const struct btf_type *mtype, *elem_type = NULL;
const struct btf_member *member; const struct btf_member *member;
const char *tname, *mname; const char *tname, *mname;
u32 vlen; u32 vlen, elem_id, mid;
again: again:
tname = __btf_name_by_offset(btf_vmlinux, t->name_off); tname = __btf_name_by_offset(btf_vmlinux, t->name_off);
...@@ -3966,7 +3972,7 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -3966,7 +3972,7 @@ int btf_struct_access(struct bpf_verifier_log *log,
*/ */
if (off <= moff && if (off <= moff &&
BITS_ROUNDUP_BYTES(end_bit) <= off + size) BITS_ROUNDUP_BYTES(end_bit) <= off + size)
return SCALAR_VALUE; return WALK_SCALAR;
/* off may be accessing a following member /* off may be accessing a following member
* *
...@@ -3988,11 +3994,13 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -3988,11 +3994,13 @@ int btf_struct_access(struct bpf_verifier_log *log,
break; break;
/* type of the field */ /* type of the field */
mid = member->type;
mtype = btf_type_by_id(btf_vmlinux, member->type); mtype = btf_type_by_id(btf_vmlinux, member->type);
mname = __btf_name_by_offset(btf_vmlinux, member->name_off); mname = __btf_name_by_offset(btf_vmlinux, member->name_off);
mtype = __btf_resolve_size(btf_vmlinux, mtype, &msize, mtype = __btf_resolve_size(btf_vmlinux, mtype, &msize,
&elem_type, NULL, &total_nelems, NULL); &elem_type, &elem_id, &total_nelems,
&mid);
if (IS_ERR(mtype)) { if (IS_ERR(mtype)) {
bpf_log(log, "field %s doesn't have size\n", mname); bpf_log(log, "field %s doesn't have size\n", mname);
return -EFAULT; return -EFAULT;
...@@ -4054,6 +4062,7 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -4054,6 +4062,7 @@ int btf_struct_access(struct bpf_verifier_log *log,
elem_idx = (off - moff) / msize; elem_idx = (off - moff) / msize;
moff += elem_idx * msize; moff += elem_idx * msize;
mtype = elem_type; mtype = elem_type;
mid = elem_id;
} }
/* the 'off' we're looking for is either equal to start /* the 'off' we're looking for is either equal to start
...@@ -4063,6 +4072,12 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -4063,6 +4072,12 @@ int btf_struct_access(struct bpf_verifier_log *log,
/* our field must be inside that union or struct */ /* our field must be inside that union or struct */
t = mtype; t = mtype;
/* return if the offset matches the member offset */
if (off == moff) {
*next_btf_id = mid;
return WALK_STRUCT;
}
/* adjust offset we're looking for */ /* adjust offset we're looking for */
off -= moff; off -= moff;
goto again; goto again;
...@@ -4078,11 +4093,10 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -4078,11 +4093,10 @@ int btf_struct_access(struct bpf_verifier_log *log,
mname, moff, tname, off, size); mname, moff, tname, off, size);
return -EACCES; return -EACCES;
} }
stype = btf_type_skip_modifiers(btf_vmlinux, mtype->type, &id); stype = btf_type_skip_modifiers(btf_vmlinux, mtype->type, &id);
if (btf_type_is_struct(stype)) { if (btf_type_is_struct(stype)) {
*next_btf_id = id; *next_btf_id = id;
return PTR_TO_BTF_ID; return WALK_PTR;
} }
} }
...@@ -4099,12 +4113,53 @@ int btf_struct_access(struct bpf_verifier_log *log, ...@@ -4099,12 +4113,53 @@ int btf_struct_access(struct bpf_verifier_log *log,
return -EACCES; return -EACCES;
} }
return SCALAR_VALUE; return WALK_SCALAR;
} }
bpf_log(log, "struct %s doesn't have field at offset %d\n", tname, off); bpf_log(log, "struct %s doesn't have field at offset %d\n", tname, off);
return -EINVAL; return -EINVAL;
} }
int btf_struct_access(struct bpf_verifier_log *log,
const struct btf_type *t, int off, int size,
enum bpf_access_type atype __maybe_unused,
u32 *next_btf_id)
{
int err;
u32 id;
do {
err = btf_struct_walk(log, t, off, size, &id);
switch (err) {
case WALK_PTR:
/* If we found the pointer or scalar on t+off,
* we're done.
*/
*next_btf_id = id;
return PTR_TO_BTF_ID;
case WALK_SCALAR:
return SCALAR_VALUE;
case WALK_STRUCT:
/* We found nested struct, so continue the search
* by diving in it. At this point the offset is
* aligned with the new type, so set it to 0.
*/
t = btf_type_by_id(btf_vmlinux, id);
off = 0;
break;
default:
/* It's either error or unknown return value..
* scream and leave.
*/
if (WARN_ONCE(err > 0, "unknown btf_struct_walk return value"))
return -EINVAL;
return err;
}
} while (t);
return -EINVAL;
}
int btf_resolve_helper_id(struct bpf_verifier_log *log, int btf_resolve_helper_id(struct bpf_verifier_log *log,
const struct bpf_func_proto *fn, int arg) const struct bpf_func_proto *fn, int arg)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment