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] [day] [month] [year] [list]
Message-ID: <1ea2c9bc-4dde-4e26-9e50-066e5e3900ab@linux.ibm.com>
Date: Tue, 27 Jan 2026 00:00:35 +0530
From: Hari Bathini <hbathini@...ux.ibm.com>
To: bot+bpf-ci@...nel.org, adubey@...ux.ibm.com, bpf@...r.kernel.org,
        linuxppc-dev@...ts.ozlabs.org, linux-kselftest@...r.kernel.org,
        linux-kernel@...r.kernel.org
Cc: sachinpb@...ux.ibm.com, venkat88@...ux.ibm.com, andrii@...nel.org,
        eddyz87@...il.com, mykolal@...com, ast@...nel.org,
        daniel@...earbox.net, martin.lau@...ux.dev, song@...nel.org,
        yonghong.song@...ux.dev, john.fastabend@...il.com, kpsingh@...nel.org,
        sdf@...ichev.me, haoluo@...gle.com, jolsa@...nel.org,
        christophe.leroy@...roup.eu, naveen@...nel.org, maddy@...ux.ibm.com,
        mpe@...erman.id.au, npiggin@...il.com, memxor@...il.com,
        iii@...ux.ibm.com, shuah@...nel.org, martin.lau@...nel.org,
        clm@...a.com, ihor.solodrai@...ux.dev
Subject: Re: [PATCH v5 6/6] powerpc64/bpf: Additional NVR handling for
 bpf_throw



On 24/01/26 1:53 pm, bot+bpf-ci@...nel.org wrote:
>> diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
>> index db121b1404fe..17de8b53a962 100644
>> --- a/arch/powerpc/net/bpf_jit_comp64.c
>> +++ b/arch/powerpc/net/bpf_jit_comp64.c
> 
> [ ... ]
> 
>> +/*
>> + * For exception boundary & exception_cb progs:
>> + *     return increased size to accommodate additional NVRs.
>> + */
>> +static int bpf_jit_stack_size(struct codegen_context *ctx)
>> +{
>> +	return ctx->exception_boundary || ctx->exception_cb ?
>> +					BPF_PPC_EXC_STACKFRAME :
>> +					BPF_PPC_STACKFRAME;
>> +}
> 
> [ ... ]
> 
>> @@ -198,7 +249,19 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
>>   			EMIT(PPC_RAW_STD(_R0, _R1, PPC_LR_STKOFF));
>>   		}
>>
>> -		EMIT(PPC_RAW_STDU(_R1, _R1, -(BPF_PPC_STACKFRAME + ctx->stack_size)));
>> +		EMIT(PPC_RAW_STDU(_R1, _R1,
>> +				-(bpf_jit_stack_size(ctx) + ctx->stack_size)));
>> +	}
> 
> [ ... ]
> 
>> @@ -248,9 +311,19 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx)
>>   		EMIT(PPC_RAW_LD(bpf_to_ppc(ARENA_VM_START), _R1,
>>   				bpf_jit_stack_offsetof(ctx, bpf_to_ppc(ARENA_VM_START))));
>>
>> +	if (ctx->exception_cb) {
>> +		/*
>> +		 * Recover additionally saved non volatile registers from stack
>> +		 * frame of exception boundary program.
>> +		 */
>> +		for (i = _R14; i <= _R25; i++)
>> +			EMIT(PPC_RAW_LD(i, _R1, bpf_jit_stack_offsetof(ctx, i)));
>> +	}
>> +
>>   	/* Tear down our stack frame */
>>   	if (bpf_has_stack_frame(ctx)) {
>> -		EMIT(PPC_RAW_ADDI(_R1, _R1, BPF_PPC_STACKFRAME + ctx->stack_size));
>> +		EMIT(PPC_RAW_ADDI(_R1, _R1, bpf_jit_stack_size(ctx) + ctx->stack_size));
> 
> For the exception callback case, doesn't this use the wrong stack_size?
> 
> In bpf_jit_build_prologue(), the exception callback does not create its own
> frame - it sets R1 to the boundary's frame pointer via EMIT(PPC_RAW_MR(_R1,
> _R5)). However here in the epilogue, the frame teardown uses ctx->stack_size
> which is the callback's stack depth, not the boundary's.
> 
> The boundary program creates its frame with:
>      STDU R1, R1, -(bpf_jit_stack_size(boundary_ctx) + boundary_stack_size)
> 
> But the callback tears it down with:
>      ADDI R1, R1, bpf_jit_stack_size(callback_ctx) + callback_stack_size
> 
> Since each BPF subprogram can have a different stack_depth (set per-subprog
> in kernel/bpf/verifier.c via func[i]->aux->stack_depth), if the exception
> callback has a different stack depth than the boundary, R1 will end up at
> the wrong location after this ADDI.
> 
> After the wrong R1 adjustment, the LR restore below loads from R1+16 which
> is now pointing to the wrong memory location, potentially causing a return
> to an invalid address.
> 
> Would it be correct to store the boundary's stack_size somewhere accessible
> to the callback, or ensure the callback uses the same stack layout as the
> boundary?

That is a good catch! Jitting the rollback of the stack frame of
exception boundary program in exception callback program is tricky
as stack_depth of exception boundary program is not readily available
while jitting exception callback program...

"ld r1, 0(r1)" instruction can be used instead of "addi r1, r1, size"
form for tearing down the stack.

The other issue is calculating the offset of non volatile registers
to restore them from the stack. To reliably restore, move one frame
above exception boundary frame and restore NVRs with redzone offset
without relying on stack_depth of exception boundary program.

Also, as exception callback may also use stack, setup frame for
exception callback and restore BPF JIT NVRs in its epilogue.

In essence, something like the below is needed on top of your patch..

---

diff --git a/arch/powerpc/net/bpf_jit_comp64.c 
b/arch/powerpc/net/bpf_jit_comp64.c
index b1a3945ccc9f..15342d76f96f 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -32,7 +32,8 @@
   *
   *		[	prev sp		] <-------------
   *		[    tail_call_info	] 8		|
- *		[   nv gpr save area	] 6*8 + (12*8)	|
+ *		[   nv gpr save area	] 6*8		|
+ *		[ addl. nv gpr save area] (12*8)	| <--- only in exception boundary 
program
   *		[    local_tmp_var	] 24		|
   * fp (r31) -->	[   ebpf stack space	] upto 512	|
   *		[     frame header	] 32/112	|
@@ -125,7 +126,8 @@ static inline bool bpf_has_stack_frame(struct 
codegen_context *ctx)
   *		[	  ...       	] 		|
   * sp (r1) --->	[    stack pointer	] --------------
   *		[    tail_call_info	] 8
- *		[   nv gpr save area	] 6*8 + (12*8)
+ *		[   nv gpr save area	] 6*8
+ *		[ addl. nv gpr save area] (12*8) <--- Only in case of exception 
boundary
   *		[    local_tmp_var	] 24
   *		[   unused red zone	] 224
   *
@@ -139,12 +141,9 @@ static int bpf_jit_stack_local(struct 
codegen_context *ctx)
  		return STACK_FRAME_MIN_SIZE + ctx->stack_size;
  	} else {
  		/* Stack layout with redzone */
-		return -(BPF_PPC_TAILCALL
-			+BPF_PPC_STACK_SAVE
-			+(ctx->exception_boundary || ctx->exception_cb ?
-						BPF_PPC_EXC_STACK_SAVE : 0)
-			+BPF_PPC_STACK_LOCALS
-			);
+		return -(BPF_PPC_TAILCALL + BPF_PPC_STACK_SAVE +
+			 (ctx->exception_boundary ? BPF_PPC_EXC_STACK_SAVE : 0) +
+			 BPF_PPC_STACK_LOCALS);
  	}
  }

@@ -153,20 +152,27 @@ int bpf_jit_stack_tailcallinfo_offset(struct 
codegen_context *ctx)
  	return bpf_jit_stack_local(ctx) + BPF_PPC_STACK_LOCALS + 
BPF_PPC_STACK_SAVE;
  }

-static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg)
+static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg, 
bool use_redzone)
  {
  	int min_valid_nvreg = BPF_PPC_NVR_MIN;
  	/* Default frame size for all cases except exception boundary */
  	int frame_nvr_size = BPF_PPC_STACKFRAME;

+	/*
+	 * When use_redzone is true, return offset of the NVR on redzone
+	 * Else, return offset based on whether stack is setup or not.
+	 */
+	if (!use_redzone)
+		use_redzone = bpf_has_stack_frame(ctx);
+
  	/* Consider all nv regs for handling exceptions */
-	if (ctx->exception_boundary || ctx->exception_cb) {
+	if (ctx->exception_boundary) {
  		min_valid_nvreg = _R14;
  		frame_nvr_size = BPF_PPC_EXC_STACKFRAME;
  	}

  	if (reg >= min_valid_nvreg && reg < 32)
-		return (bpf_has_stack_frame(ctx) ?
+		return (!use_redzone ?
  			(frame_nvr_size + ctx->stack_size) : 0)
  				- (8 * (32 - reg)) - BPF_PPC_TAILCALL;

@@ -179,14 +185,12 @@ void bpf_jit_realloc_regs(struct codegen_context *ctx)
  }

  /*
- * For exception boundary & exception_cb progs:
- *     return increased size to accommodate additional NVRs.
+ * For exception boundary program, return increased size to
+ * accommodate additional NVRs.
   */
  static int bpf_jit_stack_size(struct codegen_context *ctx)
  {
-	return ctx->exception_boundary || ctx->exception_cb ?
-					BPF_PPC_EXC_STACKFRAME :
-					BPF_PPC_STACKFRAME;
+	return ctx->exception_boundary ? BPF_PPC_EXC_STACKFRAME : 
BPF_PPC_STACKFRAME;
  }

  void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
@@ -235,12 +239,30 @@ void bpf_jit_build_prologue(u32 *image, struct 
codegen_context *ctx)
  		EMIT(PPC_RAW_STD(bpf_to_ppc(TMP_REG_1), _R1, -(BPF_PPC_TAILCALL)));
  	}

-	if (bpf_has_stack_frame(ctx) && !ctx->exception_cb) {
+	if (ctx->exception_cb) {
+		/*
+		 * Exception callback receives frame pointer of exception boundary 
program as
+		 * third argument but reliable offset calculation for NVRs is only 
possible
+		 * on the redzone because stack_depth of exception boundary is not known
+		 * while jitting the exception_cb program. So, rollback to an sp above
+		 * exception boundary frame and restore the additional NVRs using redzone
+		 * offset. The regular BPF JIT NVRs can be restored now, or in the 
epilogue
+		 * of exception_cb like any other bpf prog by reusing the BPF JIT 
NVRs part
+		 * of exception_boundary stack frame. The latter is preferred as 
exception
+		 * cb may also clobber the BPF JIT NVRs.
+		 */
+		EMIT(PPC_RAW_LD(_R1, _R5, 0));
+
+		/*
+		 * Recover additionally saved non volatile registers from stack
+		 * frame of exception boundary program.
+		 */
+		for (i = _R14; i <= _R25; i++)
+			EMIT(PPC_RAW_LD(i, _R1, bpf_jit_stack_offsetof(ctx, i, true)));
+	}
+
+	if (bpf_has_stack_frame(ctx)) {
  		/*
-		 * exception_cb uses boundary frame after stack walk.
-		 * It can simply use redzone, this optimization reduces
-		 * stack walk loop by one level.
-		 *
  		 * We need a stack frame, but we don't necessarily need to
  		 * save/restore LR unless we call other functions
  		 */
@@ -257,11 +279,11 @@ void bpf_jit_build_prologue(u32 *image, struct 
codegen_context *ctx)
  	 * Program acting as exception boundary pushes R14..R25 in addition to
  	 * BPF callee-saved non volatile registers. Exception callback uses
  	 * the boundary program's stack frame, recover additionally saved
-	 * registers in epilogue of exception callback.
+	 * registers in prologue of exception callback.
  	 */
  	if (ctx->exception_boundary) {
  		for (i = _R14; i <= _R25; i++)
-			EMIT(PPC_RAW_STD(i, _R1, bpf_jit_stack_offsetof(ctx, i)));
+			EMIT(PPC_RAW_STD(i, _R1, bpf_jit_stack_offsetof(ctx, i, false)));
  	}

  	if (!ctx->exception_cb) {
@@ -273,17 +295,11 @@ void bpf_jit_build_prologue(u32 *image, struct 
codegen_context *ctx)
  		for (i = BPF_REG_6; i <= BPF_REG_10; i++)
  			if (ctx->exception_boundary || bpf_is_seen_register(ctx, 
bpf_to_ppc(i)))
  				EMIT(PPC_RAW_STD(bpf_to_ppc(i), _R1,
-					bpf_jit_stack_offsetof(ctx, bpf_to_ppc(i))));
+					bpf_jit_stack_offsetof(ctx, bpf_to_ppc(i), false)));

  		if (ctx->exception_boundary || ctx->arena_vm_start)
  			EMIT(PPC_RAW_STD(bpf_to_ppc(ARENA_VM_START), _R1,
-				 bpf_jit_stack_offsetof(ctx, bpf_to_ppc(ARENA_VM_START))));
-	} else {
-		/*
-		 * Exception callback receives Frame Pointer of boundary
-		 * program(main prog) as third arg
-		 */
-		EMIT(PPC_RAW_MR(_R1, _R5));
+				 bpf_jit_stack_offsetof(ctx, bpf_to_ppc(ARENA_VM_START), false)));
  	}

  	/*
@@ -305,20 +321,12 @@ static void bpf_jit_emit_common_epilogue(u32 
*image, struct codegen_context *ctx
  	/* Restore NVRs */
  	for (i = BPF_REG_6; i <= BPF_REG_10; i++)
  		if (ctx->exception_cb || bpf_is_seen_register(ctx, bpf_to_ppc(i)))
-			EMIT(PPC_RAW_LD(bpf_to_ppc(i), _R1, bpf_jit_stack_offsetof(ctx, 
bpf_to_ppc(i))));
+			EMIT(PPC_RAW_LD(bpf_to_ppc(i), _R1,
+					bpf_jit_stack_offsetof(ctx, bpf_to_ppc(i), false)));

  	if (ctx->exception_cb || ctx->arena_vm_start)
  		EMIT(PPC_RAW_LD(bpf_to_ppc(ARENA_VM_START), _R1,
-				bpf_jit_stack_offsetof(ctx, bpf_to_ppc(ARENA_VM_START))));
-
-	if (ctx->exception_cb) {
-		/*
-		 * Recover additionally saved non volatile registers from stack
-		 * frame of exception boundary program.
-		 */
-		for (i = _R14; i <= _R25; i++)
-			EMIT(PPC_RAW_LD(i, _R1, bpf_jit_stack_offsetof(ctx, i)));
-	}
+				bpf_jit_stack_offsetof(ctx, bpf_to_ppc(ARENA_VM_START), false)));

  	/* Tear down our stack frame */
  	if (bpf_has_stack_frame(ctx)) {

- Hari

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ