* [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
@ 2025-09-05 5:18 Hoyeon Lee
2025-09-05 5:18 ` [RFC bpf-next v2 1/1] " Hoyeon Lee
2025-09-05 8:42 ` [RFC bpf-next v2 0/1] " Shung-Hsi Yu
0 siblings, 2 replies; 7+ messages in thread
From: Hoyeon Lee @ 2025-09-05 5:18 UTC (permalink / raw)
Cc: netdev, Hoyeon Lee, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, open list:BPF [LIBRARY] (libbpf),
open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
This RFC adds a compile-time check to bpf_tail_call_static() to warn
when a constant slot(index) is >= map->max_entries. This uses a small
BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
Clang front-end keeps the map type with a '(*max_entries)[N]' field,
so the expression
sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
is resolved to N entirely at compile time. This allows diagnose_if()
to emit a warning when a constant slot index is out of range.
Example:
struct { /* BPF_MAP_TYPE_PROG_ARRAY = 3 */
__uint(type, 3); // int (*type)[3];
__uint(max_entries, 100); // int (*max_entries)[100];
__type(key, __u32); // typeof(__u32) *key;
__type(value, __u32); // typeof(__u32) *value;
} progs SEC(".maps");
bpf_tail_call_static(ctx, &progs, 111);
produces:
bound.bpf.c:26:9: warning: bpf_tail_call: slot >= max_entries [-Wuser-defined-warnings]
26 | bpf_tail_call_static(ctx, &progs, 111);
| ^
/usr/local/include/bpf/bpf_helpers.h:190:54: note: expanded from macro 'bpf_tail_call_static'
190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
| ^
/usr/local/include/bpf/bpf_helpers.h:183:20: note: from 'diagnose_if' attribute on '__bpf_tail_call_warn':
183 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries", "warning")));
| ^ ~~~
Out-of-bounds tail call checkup is no-ops at runtime. Emitting a
compile-time warning can help developers detect mistakes earlier. The
check is currently limited to Clang (due to diagnose_if) and constant
indices, but should catch common errors.
---
Changes in V2:
- add function definition for __bpf_tail_call_warn for compile error
Hoyeon Lee (1):
libbpf: add compile-time OOB warning to bpf_tail_call_static
tools/lib/bpf/bpf_helpers.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC bpf-next v2 1/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 5:18 [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static Hoyeon Lee
@ 2025-09-05 5:18 ` Hoyeon Lee
2025-09-05 15:54 ` Yonghong Song
2025-09-05 8:42 ` [RFC bpf-next v2 0/1] " Shung-Hsi Yu
1 sibling, 1 reply; 7+ messages in thread
From: Hoyeon Lee @ 2025-09-05 5:18 UTC (permalink / raw)
Cc: netdev, Hoyeon Lee, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, open list:BPF [LIBRARY] (libbpf),
open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
Add a compile-time check to bpf_tail_call_static() to warn when a
constant slot(index) >= map->max_entries. This uses a small
BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
Clang front-end keeps the map type with a '(*max_entries)[N]' field,
so the expression
sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
is resolved to N entirely at compile time. This allows diagnose_if()
to emit a warning when a constant slot index is out of range.
Out-of-bounds tail calls are currently silent no-ops at runtime, so
emitting a compile-time warning helps detect logic errors earlier.
This is currently limited to Clang (due to diagnose_if) and only for
constant indices, but should still catch the common cases.
Signed-off-by: Hoyeon Lee <hoyeon.lee@suse.com>
---
Changes in V2:
- add function definition for __bpf_tail_call_warn for compile error
tools/lib/bpf/bpf_helpers.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index 80c028540656..98bc1536c497 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -173,6 +173,27 @@ bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
:: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
: "r0", "r1", "r2", "r3", "r4", "r5");
}
+
+#if __has_attribute(diagnose_if)
+static __always_inline void __bpf_tail_call_warn(int oob)
+ __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
+ "warning"))) {};
+
+#define BPF_MAP_ENTRIES(m) \
+ ((__u32)(sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)))
+
+#ifndef bpf_tail_call_static
+#define bpf_tail_call_static(ctx, map, slot) \
+({ \
+ /* wrapped to avoid double evaluation. */ \
+ const __u32 __slot = (slot); \
+ __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
+ /* Avoid re-expand & invoke original as (bpf_tail_call_static)(..) */ \
+ (bpf_tail_call_static)(ctx, map, __slot); \
+})
+#endif /* bpf_tail_call_static */
+#endif
+
#endif
#endif
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 5:18 [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static Hoyeon Lee
2025-09-05 5:18 ` [RFC bpf-next v2 1/1] " Hoyeon Lee
@ 2025-09-05 8:42 ` Shung-Hsi Yu
2025-09-05 18:57 ` Andrii Nakryiko
1 sibling, 1 reply; 7+ messages in thread
From: Shung-Hsi Yu @ 2025-09-05 8:42 UTC (permalink / raw)
To: bpf
Cc: Hoyeon Lee, netdev, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
Adding some context that I think was miss per off-list discussion with
Hoyeon.
On Fri, Sep 05, 2025 at 02:18:11PM +0900, Hoyeon Lee wrote:
> This RFC adds a compile-time check to bpf_tail_call_static() to warn
> when a constant slot(index) is >= map->max_entries. This uses a small
> BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
This is an attempt to see if it is possible to warn user of out-of-bound
tail calls, with the assumption being that with bpf_tail_call_static()
users would not be intentionally calling with an index that is superior
to the number of entries.
However, there concerns with the current implementation, so this is
being sent as RFC to gather feedback, and to see if it can be better
done. Currently the concerns are:
- use macro to override bpf_tail_call_static()
- only works for Clang and not GCC
- uncertain whether this fit into libbpf conventions
> Clang front-end keeps the map type with a '(*max_entries)[N]' field,
> so the expression
>
> sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
>
> is resolved to N entirely at compile time. This allows diagnose_if()
> to emit a warning when a constant slot index is out of range.
>
> Example:
>
> struct { /* BPF_MAP_TYPE_PROG_ARRAY = 3 */
> __uint(type, 3); // int (*type)[3];
> __uint(max_entries, 100); // int (*max_entries)[100];
> __type(key, __u32); // typeof(__u32) *key;
> __type(value, __u32); // typeof(__u32) *value;
> } progs SEC(".maps");
>
> bpf_tail_call_static(ctx, &progs, 111);
>
> produces:
>
> bound.bpf.c:26:9: warning: bpf_tail_call: slot >= max_entries [-Wuser-defined-warnings]
> 26 | bpf_tail_call_static(ctx, &progs, 111);
> | ^
> /usr/local/include/bpf/bpf_helpers.h:190:54: note: expanded from macro 'bpf_tail_call_static'
> 190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> | ^
> /usr/local/include/bpf/bpf_helpers.h:183:20: note: from 'diagnose_if' attribute on '__bpf_tail_call_warn':
> 183 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries", "warning")));
> | ^ ~~~
>
> Out-of-bounds tail call checkup is no-ops at runtime. Emitting a
> compile-time warning can help developers detect mistakes earlier. The
> check is currently limited to Clang (due to diagnose_if) and constant
> indices, but should catch common errors.
...
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC bpf-next v2 1/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 5:18 ` [RFC bpf-next v2 1/1] " Hoyeon Lee
@ 2025-09-05 15:54 ` Yonghong Song
2025-09-08 4:52 ` Hoyeon Lee
0 siblings, 1 reply; 7+ messages in thread
From: Yonghong Song @ 2025-09-05 15:54 UTC (permalink / raw)
To: Hoyeon Lee
Cc: netdev, Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Song Liu, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
open list:BPF [LIBRARY] (libbpf), open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
On 9/4/25 10:18 PM, Hoyeon Lee wrote:
> Add a compile-time check to bpf_tail_call_static() to warn when a
> constant slot(index) >= map->max_entries. This uses a small
> BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
>
> Clang front-end keeps the map type with a '(*max_entries)[N]' field,
> so the expression
>
> sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
>
> is resolved to N entirely at compile time. This allows diagnose_if()
> to emit a warning when a constant slot index is out of range.
>
> Out-of-bounds tail calls are currently silent no-ops at runtime, so
> emitting a compile-time warning helps detect logic errors earlier.
> This is currently limited to Clang (due to diagnose_if) and only for
> constant indices, but should still catch the common cases.
>
> Signed-off-by: Hoyeon Lee <hoyeon.lee@suse.com>
> ---
> Changes in V2:
> - add function definition for __bpf_tail_call_warn for compile error
>
> tools/lib/bpf/bpf_helpers.h | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
> index 80c028540656..98bc1536c497 100644
> --- a/tools/lib/bpf/bpf_helpers.h
> +++ b/tools/lib/bpf/bpf_helpers.h
> @@ -173,6 +173,27 @@ bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
> :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
> : "r0", "r1", "r2", "r3", "r4", "r5");
> }
> +
> +#if __has_attribute(diagnose_if)
> +static __always_inline void __bpf_tail_call_warn(int oob)
> + __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
> + "warning"))) {};
> +
> +#define BPF_MAP_ENTRIES(m) \
> + ((__u32)(sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)))
> +
> +#ifndef bpf_tail_call_static
> +#define bpf_tail_call_static(ctx, map, slot) \
> +({ \
> + /* wrapped to avoid double evaluation. */ \
> + const __u32 __slot = (slot); \
> + __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> + /* Avoid re-expand & invoke original as (bpf_tail_call_static)(..) */ \
> + (bpf_tail_call_static)(ctx, map, __slot); \
> +})
> +#endif /* bpf_tail_call_static */
> +#endif
I got the following error with llvm21.
progs/tailcall_bpf2bpf3.c:20:3: error: bpf_tail_call: slot >= max_entries [-Werror,-Wuser-defined-warnings]
20 | bpf_tail_call_static(skb, &jmp_table,progs/tailcall_bpf2bpf2.c:17:3 10);
| : ^
/home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:190:53: note: expanded from macro
'bpf_tail_call_static'
190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
| ^
/home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:179:17: note: from 'diagnose_if'
attribute on '__bpf_tail_call_warn':
179 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
| ^ ~~~
error: bpf_tail_call: slot >= max_entries [-Werror,-Wuser-defined-warnings]
17 | bpf_tail_call_static(skb, &jmp_table, 1);
| ^
/home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:190:53: note: expanded from macro
'bpf_tail_call_static'
190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
| ^
/home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:179:17: note: from 'diagnose_if'
attribute on '__bpf_tail_call_warn':
179 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
| ^ ~~~
CLNG-BPF [test_progs] tailcall_poke.bpf.o
1 error generated.
make: *** [Makefile:733: /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tailcall_bpf2bpf3.bpf.o] Error 1
> +
> #endif
> #endif
>
> --
> 2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 8:42 ` [RFC bpf-next v2 0/1] " Shung-Hsi Yu
@ 2025-09-05 18:57 ` Andrii Nakryiko
2025-09-08 4:56 ` Hoyeon Lee
0 siblings, 1 reply; 7+ messages in thread
From: Andrii Nakryiko @ 2025-09-05 18:57 UTC (permalink / raw)
To: Shung-Hsi Yu
Cc: bpf, Hoyeon Lee, netdev, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
On Fri, Sep 5, 2025 at 1:42 AM Shung-Hsi Yu <shung-hsi.yu@suse.com> wrote:
>
> Adding some context that I think was miss per off-list discussion with
> Hoyeon.
>
> On Fri, Sep 05, 2025 at 02:18:11PM +0900, Hoyeon Lee wrote:
> > This RFC adds a compile-time check to bpf_tail_call_static() to warn
> > when a constant slot(index) is >= map->max_entries. This uses a small
> > BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
>
> This is an attempt to see if it is possible to warn user of out-of-bound
> tail calls, with the assumption being that with bpf_tail_call_static()
> users would not be intentionally calling with an index that is superior
> to the number of entries.
>
> However, there concerns with the current implementation, so this is
> being sent as RFC to gather feedback, and to see if it can be better
> done. Currently the concerns are:
> - use macro to override bpf_tail_call_static()
> - only works for Clang and not GCC
> - uncertain whether this fit into libbpf conventions
- map definition's max_entries can be set from user space at runtime
making this check actively wrong
This diagnose_if attribute seems very useful, but I'm not sure we
should do this for anything map-related because statically provided
map attributes are all overridable from user space when loading BPF
object.
>
> > Clang front-end keeps the map type with a '(*max_entries)[N]' field,
> > so the expression
> >
> > sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
> >
> > is resolved to N entirely at compile time. This allows diagnose_if()
> > to emit a warning when a constant slot index is out of range.
> >
> > Example:
> >
> > struct { /* BPF_MAP_TYPE_PROG_ARRAY = 3 */
> > __uint(type, 3); // int (*type)[3];
> > __uint(max_entries, 100); // int (*max_entries)[100];
> > __type(key, __u32); // typeof(__u32) *key;
> > __type(value, __u32); // typeof(__u32) *value;
> > } progs SEC(".maps");
> >
> > bpf_tail_call_static(ctx, &progs, 111);
> >
> > produces:
> >
> > bound.bpf.c:26:9: warning: bpf_tail_call: slot >= max_entries [-Wuser-defined-warnings]
> > 26 | bpf_tail_call_static(ctx, &progs, 111);
> > | ^
> > /usr/local/include/bpf/bpf_helpers.h:190:54: note: expanded from macro 'bpf_tail_call_static'
> > 190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> > | ^
> > /usr/local/include/bpf/bpf_helpers.h:183:20: note: from 'diagnose_if' attribute on '__bpf_tail_call_warn':
> > 183 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries", "warning")));
> > | ^ ~~~
> >
> > Out-of-bounds tail call checkup is no-ops at runtime. Emitting a
> > compile-time warning can help developers detect mistakes earlier. The
> > check is currently limited to Clang (due to diagnose_if) and constant
> > indices, but should catch common errors.
> ...
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC bpf-next v2 1/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 15:54 ` Yonghong Song
@ 2025-09-08 4:52 ` Hoyeon Lee
0 siblings, 0 replies; 7+ messages in thread
From: Hoyeon Lee @ 2025-09-08 4:52 UTC (permalink / raw)
To: Yonghong Song
Cc: netdev, Andrii Nakryiko, Eduard Zingerman, Alexei Starovoitov,
Daniel Borkmann, Martin KaFai Lau, Song Liu, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
open list:BPF [LIBRARY] (libbpf), open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
On Sat, Sep 6, 2025 at 12:55 AM Yonghong Song <yonghong.song@linux.dev> wrote:
>
>
>
> On 9/4/25 10:18 PM, Hoyeon Lee wrote:
> > Add a compile-time check to bpf_tail_call_static() to warn when a
> > constant slot(index) >= map->max_entries. This uses a small
> > BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
> >
> > Clang front-end keeps the map type with a '(*max_entries)[N]' field,
> > so the expression
> >
> > sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
> >
> > is resolved to N entirely at compile time. This allows diagnose_if()
> > to emit a warning when a constant slot index is out of range.
> >
> > Out-of-bounds tail calls are currently silent no-ops at runtime, so
> > emitting a compile-time warning helps detect logic errors earlier.
> > This is currently limited to Clang (due to diagnose_if) and only for
> > constant indices, but should still catch the common cases.
> >
> > Signed-off-by: Hoyeon Lee <hoyeon.lee@suse.com>
> > ---
> > Changes in V2:
> > - add function definition for __bpf_tail_call_warn for compile error
> >
> > tools/lib/bpf/bpf_helpers.h | 21 +++++++++++++++++++++
> > 1 file changed, 21 insertions(+)
> >
> > diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
> > index 80c028540656..98bc1536c497 100644
> > --- a/tools/lib/bpf/bpf_helpers.h
> > +++ b/tools/lib/bpf/bpf_helpers.h
> > @@ -173,6 +173,27 @@ bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
> > :: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
> > : "r0", "r1", "r2", "r3", "r4", "r5");
> > }
> > +
> > +#if __has_attribute(diagnose_if)
> > +static __always_inline void __bpf_tail_call_warn(int oob)
> > + __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
> > + "warning"))) {};
> > +
> > +#define BPF_MAP_ENTRIES(m) \
> > + ((__u32)(sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)))
> > +
> > +#ifndef bpf_tail_call_static
> > +#define bpf_tail_call_static(ctx, map, slot) \
> > +({ \
> > + /* wrapped to avoid double evaluation. */ \
> > + const __u32 __slot = (slot); \
> > + __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> > + /* Avoid re-expand & invoke original as (bpf_tail_call_static)(..) */ \
> > + (bpf_tail_call_static)(ctx, map, __slot); \
> > +})
> > +#endif /* bpf_tail_call_static */
> > +#endif
>
> I got the following error with llvm21.
>
> progs/tailcall_bpf2bpf3.c:20:3: error: bpf_tail_call: slot >= max_entries [-Werror,-Wuser-defined-warnings]
> 20 | bpf_tail_call_static(skb, &jmp_table,progs/tailcall_bpf2bpf2.c:17:3 10);
> | : ^
> /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:190:53: note: expanded from macro
> 'bpf_tail_call_static'
> 190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> | ^
> /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:179:17: note: from 'diagnose_if'
> attribute on '__bpf_tail_call_warn':
> 179 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
> | ^ ~~~
> error: bpf_tail_call: slot >= max_entries [-Werror,-Wuser-defined-warnings]
> 17 | bpf_tail_call_static(skb, &jmp_table, 1);
> | ^
> /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:190:53: note: expanded from macro
> 'bpf_tail_call_static'
> 190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> | ^
> /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include/bpf/bpf_helpers.h:179:17: note: from 'diagnose_if'
> attribute on '__bpf_tail_call_warn':
> 179 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries",
> | ^ ~~~
> CLNG-BPF [test_progs] tailcall_poke.bpf.o
> 1 error generated.
> make: *** [Makefile:733: /home/yhs/work/bpf-next/tools/testing/selftests/bpf/tailcall_bpf2bpf3.bpf.o] Error 1
>
Thank you for sharing build results! Checked BPF CI, and found 2 issues:
1. selftests/bpf promote warnings to errors (-Werror)
For bpf2bpf tail-call variant progs that intentionally calls OOB trigger
this diagnostic, relaxing just those files keeps CI green while still
showing the warning:
# tools/testing/selftests/bpf/Makefile
progs/tailcall_bpf2bpf%.c-CFLAGS := -Wno-error=user-defined-warnings
2. 'void *' maps build error (bpf2bpf / map-in-map)
The proposed warning is meant only for typed .maps objects. When a prog
passes a void * map, BPF_MAP_ENTRIES() must not attempt member
access. A _Generic gate fixes this by filtering only for typed maps and
yielding 0U for void* families:
# from BPF-CI build error
# function prototype:
# int subprog_tail(struct __sk_buff *skb, void *jmp_table)
progs/tailcall_bpf2bpf_hierarchy3.c:36:2: error: member
reference base type 'void' is not a structure or union
36 | bpf_tail_call_static(skb, jmp_table, 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fixes:
#define BPF_MAP_ENTRIES(m) _Generic((m), \
void *: 0U, \
const void *: 0U, \
volatile void *: 0U, \
const volatile void *: 0U, \
default:
((__u32)(sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries))) \
)
This avoids the compile error, but this is not a very clean solution.
As Andrii Nakryiko noted, map->max_entries can be changed at runtime,
which makes this compile-time approach misleading. I hadn’t considered
that, so this RFC seems less practical to pursue further. Still, I’m
sharing the troubleshooting above in case parts of it may be useful for
future attempts.
> > +
> > #endif
> > #endif
> >
> > --
> > 2.51.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static
2025-09-05 18:57 ` Andrii Nakryiko
@ 2025-09-08 4:56 ` Hoyeon Lee
0 siblings, 0 replies; 7+ messages in thread
From: Hoyeon Lee @ 2025-09-08 4:56 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: Shung-Hsi Yu, bpf, netdev, Andrii Nakryiko, Eduard Zingerman,
Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt, open list,
open list:CLANG/LLVM BUILD SUPPORT:Keyword:b(?i:clang|llvm)b
On Sat, Sep 6, 2025 at 3:57 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Fri, Sep 5, 2025 at 1:42 AM Shung-Hsi Yu <shung-hsi.yu@suse.com> wrote:
> >
> > Adding some context that I think was miss per off-list discussion with
> > Hoyeon.
> >
> > On Fri, Sep 05, 2025 at 02:18:11PM +0900, Hoyeon Lee wrote:
> > > This RFC adds a compile-time check to bpf_tail_call_static() to warn
> > > when a constant slot(index) is >= map->max_entries. This uses a small
> > > BPF_MAP_ENTRIES() macro together with Clang's diagnose_if attribute.
> >
> > This is an attempt to see if it is possible to warn user of out-of-bound
> > tail calls, with the assumption being that with bpf_tail_call_static()
> > users would not be intentionally calling with an index that is superior
> > to the number of entries.
> >
> > However, there concerns with the current implementation, so this is
> > being sent as RFC to gather feedback, and to see if it can be better
> > done. Currently the concerns are:
> > - use macro to override bpf_tail_call_static()
> > - only works for Clang and not GCC
> > - uncertain whether this fit into libbpf conventions
>
> - map definition's max_entries can be set from user space at runtime
> making this check actively wrong
>
>
> This diagnose_if attribute seems very useful, but I'm not sure we
> should do this for anything map-related because statically provided
> map attributes are all overridable from user space when loading BPF
> object.
>
Thanks for pointing this out.
I hadn’t thought about map->max_entries being changed from user
space. That does make a compile-time check misleading, so I don’t
plan to take this RFC further. I’ve left some of the build workarounds
in the other thread in case they are useful.
Thanks to everyone for the time and thoughtful feedback, always
much appreciated.
> >
> > > Clang front-end keeps the map type with a '(*max_entries)[N]' field,
> > > so the expression
> > >
> > > sizeof(*(m)->max_entries) / sizeof(**(m)->max_entries)
> > >
> > > is resolved to N entirely at compile time. This allows diagnose_if()
> > > to emit a warning when a constant slot index is out of range.
> > >
> > > Example:
> > >
> > > struct { /* BPF_MAP_TYPE_PROG_ARRAY = 3 */
> > > __uint(type, 3); // int (*type)[3];
> > > __uint(max_entries, 100); // int (*max_entries)[100];
> > > __type(key, __u32); // typeof(__u32) *key;
> > > __type(value, __u32); // typeof(__u32) *value;
> > > } progs SEC(".maps");
> > >
> > > bpf_tail_call_static(ctx, &progs, 111);
> > >
> > > produces:
> > >
> > > bound.bpf.c:26:9: warning: bpf_tail_call: slot >= max_entries [-Wuser-defined-warnings]
> > > 26 | bpf_tail_call_static(ctx, &progs, 111);
> > > | ^
> > > /usr/local/include/bpf/bpf_helpers.h:190:54: note: expanded from macro 'bpf_tail_call_static'
> > > 190 | __bpf_tail_call_warn(__slot >= BPF_MAP_ENTRIES(map)); \
> > > | ^
> > > /usr/local/include/bpf/bpf_helpers.h:183:20: note: from 'diagnose_if' attribute on '__bpf_tail_call_warn':
> > > 183 | __attribute__((diagnose_if(oob, "bpf_tail_call: slot >= max_entries", "warning")));
> > > | ^ ~~~
> > >
> > > Out-of-bounds tail call checkup is no-ops at runtime. Emitting a
> > > compile-time warning can help developers detect mistakes earlier. The
> > > check is currently limited to Clang (due to diagnose_if) and constant
> > > indices, but should catch common errors.
> > ...
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-09-08 4:56 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-05 5:18 [RFC bpf-next v2 0/1] libbpf: add compile-time OOB warning to bpf_tail_call_static Hoyeon Lee
2025-09-05 5:18 ` [RFC bpf-next v2 1/1] " Hoyeon Lee
2025-09-05 15:54 ` Yonghong Song
2025-09-08 4:52 ` Hoyeon Lee
2025-09-05 8:42 ` [RFC bpf-next v2 0/1] " Shung-Hsi Yu
2025-09-05 18:57 ` Andrii Nakryiko
2025-09-08 4:56 ` Hoyeon Lee
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).