mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 21:39:35 -04:00
Bump MAX_CALL_FRAMES from 8 to 16 to allow deeper call chains that Rust-BPF requires and update selftests. Link: https://lore.kernel.org/r/20260613180755.29671-1-alexei.starovoitov@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
140 lines
3.6 KiB
C
140 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
|
|
|
#include <linux/bpf.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include "bpf_misc.h"
|
|
|
|
/*
|
|
* Exponential complexity in analyze_subprog() liveness analysis.
|
|
*
|
|
* analyze_subprog() recurses into each call site that passes FP-derived
|
|
* arguments, creating a unique func_instance per (callsite, depth).
|
|
* There is no memoization for callees reached with equivalent entry args.
|
|
* Even if memoization were added, it can be defeated by passing a distinct
|
|
* FP offset at each call site. arg_track keys on (frame, off[]), so
|
|
* r1=fp-8, r1=fp-16, ... r1=fp-400 produce 50 unique cache keys per level.
|
|
*
|
|
* This test chains 8 subprograms (within the MAX_CALL_FRAMES limit). Each
|
|
* intermediate function calls the next one 50 times, each time with a
|
|
* different FP-relative offset in r1.
|
|
*
|
|
* Without complexity limits in analyze_subprog() the resulting 50^7 ~ 7.8 * 10^11
|
|
* recursive analyze_subprog() calls will cause a CPU soft lockup or OOM.
|
|
*
|
|
* The BPF program itself is ~1200 instructions and perfectly valid.
|
|
*/
|
|
|
|
char _license[] SEC("license") = "GPL";
|
|
|
|
/* Call fn with r1 = r10 + off (a unique FP-derived arg per call site) */
|
|
#define C(fn, off) "r1 = r10;" \
|
|
"r1 += -" #off ";" \
|
|
"call " #fn ";"
|
|
|
|
/* 50 calls, each with a distinct FP offset: -8, -16, ... -400 */
|
|
#define CALLS_50(fn) \
|
|
C(fn, 8) C(fn, 16) C(fn, 24) C(fn, 32) C(fn, 40) \
|
|
C(fn, 48) C(fn, 56) C(fn, 64) C(fn, 72) C(fn, 80) \
|
|
C(fn, 88) C(fn, 96) C(fn, 104) C(fn, 112) C(fn, 120) \
|
|
C(fn, 128) C(fn, 136) C(fn, 144) C(fn, 152) C(fn, 160) \
|
|
C(fn, 168) C(fn, 176) C(fn, 184) C(fn, 192) C(fn, 200) \
|
|
C(fn, 208) C(fn, 216) C(fn, 224) C(fn, 232) C(fn, 240) \
|
|
C(fn, 248) C(fn, 256) C(fn, 264) C(fn, 272) C(fn, 280) \
|
|
C(fn, 288) C(fn, 296) C(fn, 304) C(fn, 312) C(fn, 320) \
|
|
C(fn, 328) C(fn, 336) C(fn, 344) C(fn, 352) C(fn, 360) \
|
|
C(fn, 368) C(fn, 376) C(fn, 384) C(fn, 392) C(fn, 400)
|
|
|
|
/* Leaf: depth 7, no further calls */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub7(void)
|
|
{
|
|
asm volatile (
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 6 -> calls exp_sub7 x50 with distinct offsets */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub6(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub7)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 5 -> calls exp_sub6 x50 */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub5(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub6)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 4 -> calls exp_sub5 x50 */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub4(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub5)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 3 -> calls exp_sub4 x50 */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub3(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub4)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 2 -> calls exp_sub3 x50 */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub2(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub3)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/* depth 1 -> calls exp_sub2 x50 */
|
|
__naked __noinline __used
|
|
static unsigned long exp_sub1(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub2)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|
|
|
|
/*
|
|
* Entry: depth 0. Calls exp_sub1 50 times, each with a distinct
|
|
* FP offset in r1. Every call site produces a unique arg_track,
|
|
* defeating any memoization keyed on entry args.
|
|
*/
|
|
SEC("?raw_tp")
|
|
__failure __log_level(2)
|
|
__msg("liveness analysis exceeded complexity limit")
|
|
__naked int liveness_exponential_complexity(void)
|
|
{
|
|
asm volatile (
|
|
CALLS_50(exp_sub1)
|
|
"r0 = 0;"
|
|
"exit;"
|
|
::: __clobber_all);
|
|
}
|