From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794314303; b=MiwrHhx41rZssPcu1keXRuviy4nPu5kwOYxKs0M5c8rnnkD7PjVS0DpVKK/pgJmRsV+A vkBsQ3PlWMSnZfNpu0uMw+WItBKKJDaCaqY4zEDcgp6RkefyLUM7Q4zKuFFc6CfVNUxOL pdK7+LxvmVt0WIjBxoXKNc/b1EmGuaSoSkBV1jfO3tjdqbavuatKjrU983RTSQzNvsg+w c2n39nnZWpopvMnvlT52XCbfISDobtGR/M7ihlHEZfa6rbg3Fr3ci3g1AtjeBA09s38PN IAeF/BFDkg5ba/dBrT0UGz5tuE8pfkROOMXT2uoV3SXkJSkb75SQd8+hfEIlYkjzDUQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794314303; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Transfer-Encoding; bh=EbKqQtDQ9T+mKe+GoQrWlXczIa8Viv0bnHQEOiibHWY=; b=TD2xIKLKCbZPLVq5Uv+BS6VnBzyXdqRtAu7mmltyLjFI2Y7ahi4ePub2q0JEr2Xgr6wV oZrwj0/QCcZ2z9KJpBfXTZxHvHAxgC9Ch1mNFT2omOd5S7jdzu/InEOF9F4vyGeUwkXs5 wJ4MI6pfPcFki7za+xNkjDKKtCDg9a+tCzAKcCFkWo5QSFRGJ7rlLLuD0WF3JilUiBdQl fSLla+fBIGtfHBauxicf9yO0ctvnXB/mgXCXid1GeG3ZqTPNbS2hOnsspuJkklZ5bvfoB rPUB/FSg6SmgLsQRCRYA1uRjaUOu4Je8WEUz1si6cFDBG5WXapGRTpqQqGzrFMLItqA== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=YLi87uHK; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5225F606A325 for ; Tue, 10 Nov 2026 12:38:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=YLi87uHK Received: by smtp.kernel.org (Postfix) with ESMTPSA id D06DE1F0089F; Tue, 10 Nov 2026 12:38:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1794314302; bh=EbKqQtDQ9T+mKe+GoQrWlXczIa8Viv0bnHQEOiibHWY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=YLi87uHKddSywAAhbdPMdD2L3AfSUTVYmlOv8/jA33nhCMC1NQs7D6MhpleRKOSzb 1bxraRlg/vqaDRhYyofSulSWUZCw9JG31EWL847mqnZjd4k5ZW5nTj9sYN6BbcZiqq 2a2IVQfow6doiMrfVftJZLLv3xDsdl0C7ZwwlVBNd2w3Cf58YK41NAREJVQI8h+aF4 VV0zQfEyivqEtIsrs8dvxMX+Fo88HK+sV+XEfBOr6HvopdCIu/KoO/3W40nn7QAREY 8+pa5aekSQrIY8Fw1R1Fj4o2veV+WtKOJffccie6rzLVhdqJkYS3Q2bAH2rFDIy6U4 ti9P1XB3pzuEA== From: Sasha Levin To: stable@vger.kernel.org Cc: Abhishek Dubey , sashiko-bot@kernel.org, Hari Bathini , Saket Kumar Bhaskar , Yeswanth Krishna Tellakula , R Nageswara Sastry , Madhavan Srinivasan , Sasha Levin Subject: [PATCH 6.12.y] powerpc/bpf: fix buffer overflow in JIT for large BPF programs Date: Tue, 10 Nov 2026 07:38:20 -0500 Message-ID: <20261110123745.1405178-1-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261107051815-failed-6.12-78ce55b7913a@kernel.org> References: <20261107051815-failed-6.12-78ce55b7913a@kernel.org> X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Abhishek Dubey [ Upstream commit 78ce55b7913a78e1687306668e078b86bc6508ee ] During size calculation in pass-0, exit_addr is 0 since addrs[fp->len] is not yet populated. bpf_jit_emit_exit_insn() treats a zero exit_addr as in-range and skips bpf_jit_build_epilogue(), so the alternate inline epilogue instructions are not counted in alloclen. In later passes, if the real exit_addr falls outside the 32MB branch range, the full inline epilogue is emitted into the already-allocated buffer, writing past its end and corrupting adjacent memory. Fix by ensuring exit_addr is non-zero before treating it as in-range, so pass-0 always falls through to bpf_jit_build_epilogue() and conservatively accounts for all epilogue instructions in alloclen. Also range check alt_exit_addr directly in the else-if condition. Since exit_addr handling now falls through to the epilogue, two related issues in bpf_int_jit_compile() must also be addressed: 1. Reset cgctx.alt_exit_addr before the second size-calculation pass. Without this, a stale alt_exit_addr from the first pass causes the second pass to emit a single jump instead of the full epilogue, undercounting alloclen and reintroducing the overflow. 2. Recompute addrs[fp->len] at the end of each code-generation pass. The larger pass-0 body can shrink in later passes as out-of-range exits settle into in-range jumps; a stale addrs[fp->len] would leave exit branches targeting past the real (shrunken) epilogue. Because shrinkage in a later pass can move the epilogue offset, the fixed two-pass loop is no longer sufficient: an exit that was out of range in an earlier pass may fall in range once the epilogue offset shrinks, shrinking the body further and overwriting the start of the epilogue. Convert the code-generation loop to iterate until the program size converges, bounded by CODEGEN_MAX_PASSES, and fail the JIT if it does not converge. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92 Fixes: 0ffdbce6f4a8 ("powerpc/bpf: Handle large branch ranges with BPF_EXIT") Cc: stable@vger.kernel.org Signed-off-by: Hari Bathini Signed-off-by: Abhishek Dubey Signed-off-by: Saket Kumar Bhaskar Tested-by: Yeswanth Krishna Tellakula Tested-by: R Nageswara Sastry Link: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92 Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/eebffa997e07781b384c4480e2b03c5b87c1aeb1.1789982239.git.skb99@linux.ibm.com [ sashal: adapted the new error path to restore fp from org_fp and jump to out_addrs instead of out_err ] Signed-off-by: Sasha Levin --- arch/powerpc/net/bpf_jit.h | 7 +++++++ arch/powerpc/net/bpf_jit_comp.c | 35 +++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index cdea5dccaefe7..02a1ea9334d14 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h @@ -13,6 +13,13 @@ #include #include +/* + * We need at least 2 passes for proper code generation, and may need + * additional passes if code size changes between passes. + */ +#define CODEGEN_MIN_PASSES 2 +#define CODEGEN_MAX_PASSES 3 + #ifdef CONFIG_PPC64_ELF_ABI_V1 #define FUNCTION_DESCR_SIZE 24 #else diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 180175e072adc..2d8cecc36b73f 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -29,11 +29,10 @@ static void bpf_jit_fill_ill_insns(void *area, unsigned int size) int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr) { - if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) { + if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) { PPC_JMP(exit_addr); - } else if (ctx->alt_exit_addr) { - if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4)))) - return -1; + } else if (ctx->alt_exit_addr && is_offset_in_branch_range( + (long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4))) { PPC_JMP(ctx->alt_exit_addr); } else { ctx->alt_exit_addr = ctx->idx * 4; @@ -151,6 +150,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) */ if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) { cgctx.idx = 0; + cgctx.alt_exit_addr = 0; if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) { fp = org_fp; goto out_addrs; @@ -187,10 +187,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) code_base = (u32 *)(image + FUNCTION_DESCR_SIZE); fcode_base = (u32 *)(fimage + FUNCTION_DESCR_SIZE); - /* Code generation passes 1-2 */ - for (pass = 1; pass < 3; pass++) { + /* Code generation passes 1-2+, loop until program size converges. */ + for (pass = 1; pass <= CODEGEN_MAX_PASSES; pass++) { + u32 prev_proglen = proglen; + /* Now build the prologue, body code & epilogue for real. */ cgctx.idx = 0; + cgctx.exentry_idx = 0; cgctx.alt_exit_addr = 0; bpf_jit_build_prologue(code_base, &cgctx); if (bpf_jit_build_body(fp, code_base, fcode_base, &cgctx, addrs, pass, @@ -200,11 +203,27 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) fp = org_fp; goto out_addrs; } + addrs[fp->len] = cgctx.idx * 4; bpf_jit_build_epilogue(code_base, &cgctx); + proglen = cgctx.idx * 4; + if (bpf_jit_enable > 1) pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass, - proglen - (cgctx.idx * 4), cgctx.seen); + prev_proglen - proglen, cgctx.seen); + + /* Check if program size has converged, but ensure minimum passes */ + if (pass >= CODEGEN_MIN_PASSES && proglen == prev_proglen) + break; + + if (pass == CODEGEN_MAX_PASSES && proglen != prev_proglen) { + pr_err("BPF JIT: Program did not converge after %d passes\n", + CODEGEN_MAX_PASSES); + bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size)); + bpf_jit_binary_pack_free(fhdr, hdr); + fp = org_fp; + goto out_addrs; + } } if (bpf_jit_enable > 1) @@ -264,7 +283,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass u32 *fixup; /* Populate extable entries only in the last pass */ - if (pass != 2) + if (pass < CODEGEN_MIN_PASSES) return 0; if (!fp->aux->extable || -- 2.53.0