lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <d887e166-0fd8-4b70-b6b7-6d3c0138d87b@arm.com>
Date: Thu, 31 Jul 2025 16:42:52 +0100
From: Douglas Raillard <douglas.raillard@....com>
To: Steven Rostedt <rostedt@...dmis.org>
Cc: LKML <linux-kernel@...r.kernel.org>,
 Linux trace kernel <linux-trace-kernel@...r.kernel.org>,
 bpf@...r.kernel.org, Masami Hiramatsu <mhiramat@...nel.org>,
 Mathieu Desnoyers <mathieu.desnoyers@...icios.com>,
 Mark Rutland <mark.rutland@....com>, Peter Zijlstra <peterz@...radead.org>,
 Namhyung Kim <namhyung@...nel.org>, Takaya Saeki <takayas@...gle.com>,
 Tom Zanussi <zanussi@...nel.org>, Andrew Morton <akpm@...ux-foundation.org>,
 Thomas Gleixner <tglx@...utronix.de>, Ian Rogers <irogers@...gle.com>,
 aahringo@...hat.com
Subject: Re: [PATCH] tracing/probes: Allow use of BTF names to dereference
 pointers

On 31-07-2025 14:29, Steven Rostedt wrote:
> On Thu, 31 Jul 2025 12:44:49 +0100
> Douglas Raillard <douglas.raillard@....com> wrote:
> 
>>> The delimiter is '.' and the first item is the structure name. Then the
>>> member of the structure to get the offset of. If that member is an
>>> embedded structure, another '.MEMBER' may be added to get the offset of
>>> its members with respect to the original value.
>>>
>>>     "+kmem_cache.size($arg1)" is equivalent to:
>>>
>>>     (*(struct kmem_cache *)$arg1).size
>>>
>>> Anonymous structures are also handled:
>>>
>>>     # echo 'e:xmit net.net_dev_xmit +net_device.name(+sk_buff.dev($skbaddr)):string' >> dynamic_events
>>
>> Not sure how hard that would be but the type of the expression could probably be inferred from
>> BTF as well in some cases. Some cases may be ambiguous (like char* that could be either a buffer
>> to display as hex or a null-terminated ASCII string) but BTF would still allow to restrict
>> to something sensible (e.g. prevent u32 for a char*).
> 
> Hmm, should be possible, but would require passing that information back to
> the caller of the BTF lookup function.
> 
> 
> 
>>> diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c
>>> index 5bbdbcbbde3c..b69404451410 100644
>>> --- a/kernel/trace/trace_btf.c
>>> +++ b/kernel/trace/trace_btf.c
>>> @@ -120,3 +120,109 @@ const struct btf_member *btf_find_struct_member(struct btf *btf,
>>>    	return member;
>>>    }
>>>    
>>> +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
>>> +
>>> +static int find_member(const char *ptr, struct btf *btf,
>>> +		       const struct btf_type **type, int level)
>>> +{
>>> +	const struct btf_member *member;
>>> +	const struct btf_type *t = *type;
>>> +	int i;
>>> +
>>> +	/* Max of 3 depth of anonymous structures */
>>> +	if (level > 3)
>>> +		return -1;
>>> +
>>> +	for_each_member(i, t, member) {
>>> +		const char *tname = btf_name_by_offset(btf, member->name_off);
>>> +
>>> +		if (strcmp(ptr, tname) == 0) {
>>> +			*type = btf_type_by_id(btf, member->type);
>>> +			return BITS_ROUNDDOWN_BYTES(member->offset);
>>
>> member->offset does not only contain the offset, and the offset may not be
>> a multiple of 8:
>> https://elixir.bootlin.com/linux/v6.16/source/include/uapi/linux/btf.h#L126
>>
>>   From the BTF spec (https://docs.kernel.org/bpf/btf.html):
>>
>> If the kind_flag is set, the btf_member.offset contains
>> both member bitfield size and bit offset.
>> The bitfield size and bit offset are calculated as below.:
>>
>> #define BTF_MEMBER_BITFIELD_SIZE(val)   ((val) >> 24)
>> #define BTF_MEMBER_BIT_OFFSET(val)      ((val) & 0xffffff)
> 
> So basically just need to change that to:
> 
> 		if (strcmp(ptr, tname) == 0) {
> 			int offset = BTF_MEMBER_BIT_OFFSET(member->offset);
> 			*type = btf_type_by_id(btf, member->type);
> 			return BITS_ROUNDDOWN_BYTES(offset);
> 
> ?

This would work in practice for now, but strictly speaking you should check
the kind_flag field in btf_type.info (bit 31) of the parent struct/union:
https://elixir.bootlin.com/linux/v6.16/source/include/uapi/linux/btf.h#L38
  __btf_member_bit_offset() seems to do exactly that.


While writing that, I realized there is another subtlety: BTF encodes int member offsets in 2 different ways:
1. Either their bit offset is encoded struct btf_member, and the btf_type of the member is an integer type with no leading padding bits.
2. Or the rounded-down offset is encoded in struct btf_member and the integer type contains some leading padding bits information:
https://docs.kernel.org/bpf/btf.html#btf-kind-int

The 2nd case is somewhat surprising but BTF_KIND_INT has 3 pieces of information:
1. The C signedness of the type.
2. The number of value bits of the type.
3. The offset of the 1st bit to interpret as being the value. Anything before is leading padding.

That means that the actual bit offset of an int member's value in a parent struct is:

   <offset of the member> + <offset of the type of the member>

You could technically have all members with btf_member.offset == 0 and then encode the actual values offsets in the btf_type of the members.

> 
>>
>>> +		}
>>> +
>>> +		/* Handle anonymous structures */
>>> +		if (strlen(tname))
>>> +			continue;
>>> +
>>> +		*type = btf_type_by_id(btf, member->type);
>>> +		if (btf_type_is_struct(*type)) {
>>> +			int offset = find_member(ptr, btf, type, level + 1);
>>> +
>>> +			if (offset < 0)
>>> +				continue;
>>> +
>>> +			return offset + BITS_ROUNDDOWN_BYTES(member->offset);
> 
> And here too.
> 
> -- Steve
> 
>>> +		}
>>> +	}
>>> +
>>> +	return -1;
>>> +}
>>> +


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ