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: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Link: https://lore.kernel.org/bpf/20200825192124.710397-8-jolsa@kernel.org
This commit is contained in:
parent
dafe58fc19
commit
1c6d28a6ac
@ -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 @@ error:
|
|||||||
*/
|
*/
|
||||||
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 @@ error:
|
|||||||
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 @@ error:
|
|||||||
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 @@ error:
|
|||||||
/* 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 @@ error:
|
|||||||
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 @@ error:
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user