From a2d68e20158bfc4c48a82faf227e08d2c8d21920 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 3 Jul 2026 00:06:33 -0400 Subject: [PATCH] Fixes for all trees Signed-off-by: Sasha Levin --- ...ce-between-icreq-handling-and-queue-.patch | 108 ++++++++++++++++++ staging-5.10/series | 1 + ...ce-between-icreq-handling-and-queue-.patch | 108 ++++++++++++++++++ staging-5.15/series | 1 + ...4-reject-out-of-range-b.cond-targets.patch | 58 ++++++++++ ...ce-between-icreq-handling-and-queue-.patch | 108 ++++++++++++++++++ staging-6.1/series | 2 + ...4-reject-out-of-range-b.cond-targets.patch | 59 ++++++++++ staging-6.12/series | 1 + ...4-reject-out-of-range-b.cond-targets.patch | 59 ++++++++++ staging-6.18/series | 1 + ...4-reject-out-of-range-b.cond-targets.patch | 58 ++++++++++ staging-6.6/series | 1 + 13 files changed, 565 insertions(+) create mode 100644 staging-5.10/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch create mode 100644 staging-5.10/series create mode 100644 staging-5.15/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch create mode 100644 staging-5.15/series create mode 100644 staging-6.1/bpf-arm64-reject-out-of-range-b.cond-targets.patch create mode 100644 staging-6.1/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch create mode 100644 staging-6.1/series create mode 100644 staging-6.12/bpf-arm64-reject-out-of-range-b.cond-targets.patch create mode 100644 staging-6.12/series create mode 100644 staging-6.18/bpf-arm64-reject-out-of-range-b.cond-targets.patch create mode 100644 staging-6.18/series create mode 100644 staging-6.6/bpf-arm64-reject-out-of-range-b.cond-targets.patch create mode 100644 staging-6.6/series diff --git a/staging-5.10/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch b/staging-5.10/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch new file mode 100644 index 0000000000..81edf743f7 --- /dev/null +++ b/staging-5.10/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch @@ -0,0 +1,108 @@ +From 139b4b67e98feb4931dd2e853ed707b908b2ac78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 20:38:39 +0800 +Subject: nvmet-tcp: fix race between ICReq handling and queue teardown + +From: Chaitanya Kulkarni + +commit 5293a8882c549fab4a878bc76b0b6c951f980a61 upstream. + +nvmet_tcp_handle_icreq() updates queue->state after sending an +Initialization Connection Response (ICResp), but it does so without +serializing against target-side queue teardown. + +If an NVMe/TCP host sends an Initialization Connection Request +(ICReq) and immediately closes the connection, target-side teardown +may start in softirq context before io_work drains the already +buffered ICReq. In that case, nvmet_tcp_schedule_release_queue() +sets queue->state to NVMET_TCP_Q_DISCONNECTING and drops the queue +reference under state_lock. + +If io_work later processes that ICReq, nvmet_tcp_handle_icreq() can +still overwrite the state back to NVMET_TCP_Q_LIVE. That defeats the +DISCONNECTING-state guard in nvmet_tcp_schedule_release_queue() and +allows a later socket state change to re-enter teardown and issue a +second kref_put() on an already released queue. + +The ICResp send failure path has the same problem. If teardown has +already moved the queue to DISCONNECTING, a send error can still +overwrite the state with NVMET_TCP_Q_FAILED, again reopening the +window for a second teardown path to drop the queue reference. + +Fix this by serializing both post-send state transitions with +state_lock and bailing out if teardown has already started. + +Use -ESHUTDOWN as an internal sentinel for that bail-out path rather +than propagating it as a transport error like -ECONNRESET. Keep +nvmet_tcp_socket_error() setting rcv_state to NVMET_TCP_RECV_ERR before +honoring that sentinel so receive-side parsing stays quiesced until the +existing release path completes. + +Fixes: c46a6465bac2 ("nvmet-tcp: add NVMe over TCP target driver") +Cc: stable@vger.kernel.org +Reported-by: Shivam Kumar +Tested-by: Shivam Kumar +Signed-off-by: Chaitanya Kulkarni +Signed-off-by: Keith Busch +[ context diff adaptation: drop `queue->state = NVMET_TCP_Q_FAILED` since + the enum introduced in 6.7, 675b453e0241 ("nvmet-tcp: enable TLS handshake + upcall" ] +Signed-off-by: Philo Lu +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/tcp.c | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c +index edcc0662c3e571..4bd82e9b113ff5 100644 +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -345,6 +345,19 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) + + static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue) + { ++ /* ++ * Keep rcv_state at RECV_ERR even for the internal -ESHUTDOWN path. ++ * nvmet_tcp_handle_icreq() can return -ESHUTDOWN after the ICReq has ++ * already been consumed and queue teardown has started. ++ * ++ * If nvmet_tcp_data_ready() or nvmet_tcp_write_space() queues ++ * nvmet_tcp_io_work() again before nvmet_tcp_release_queue_work() ++ * cancels it, the queue must not keep that old receive state. ++ * Otherwise the next nvmet_tcp_io_work() run can reach ++ * nvmet_tcp_done_recv_pdu() and try to handle the same ICReq again. ++ * ++ * That is why queue->rcv_state needs to be updated before we return. ++ */ + queue->rcv_state = NVMET_TCP_RECV_ERR; + if (queue->nvme_sq.ctrl) + nvmet_ctrl_fatal_error(queue->nvme_sq.ctrl); +@@ -888,10 +901,24 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) + iov.iov_base = icresp; + iov.iov_len = sizeof(*icresp); + ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len); +- if (ret < 0) ++ if (ret < 0) { ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ return -ESHUTDOWN; ++ } ++ spin_unlock_bh(&queue->state_lock); + return ret; /* queue removal will cleanup */ ++ } + ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ /* Tell nvmet_tcp_socket_error() teardown is in progress. */ ++ return -ESHUTDOWN; ++ } + queue->state = NVMET_TCP_Q_LIVE; ++ spin_unlock_bh(&queue->state_lock); + nvmet_prepare_receive_pdu(queue); + return 0; + } +-- +2.53.0 + diff --git a/staging-5.10/series b/staging-5.10/series new file mode 100644 index 0000000000..3e69bb8311 --- /dev/null +++ b/staging-5.10/series @@ -0,0 +1 @@ +nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch diff --git a/staging-5.15/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch b/staging-5.15/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch new file mode 100644 index 0000000000..46d6cf1ffd --- /dev/null +++ b/staging-5.15/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch @@ -0,0 +1,108 @@ +From 3ab972c533b488a9780ce67058a3c592165c5748 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 20:37:57 +0800 +Subject: nvmet-tcp: fix race between ICReq handling and queue teardown + +From: Chaitanya Kulkarni + +commit 5293a8882c549fab4a878bc76b0b6c951f980a61 upstream. + +nvmet_tcp_handle_icreq() updates queue->state after sending an +Initialization Connection Response (ICResp), but it does so without +serializing against target-side queue teardown. + +If an NVMe/TCP host sends an Initialization Connection Request +(ICReq) and immediately closes the connection, target-side teardown +may start in softirq context before io_work drains the already +buffered ICReq. In that case, nvmet_tcp_schedule_release_queue() +sets queue->state to NVMET_TCP_Q_DISCONNECTING and drops the queue +reference under state_lock. + +If io_work later processes that ICReq, nvmet_tcp_handle_icreq() can +still overwrite the state back to NVMET_TCP_Q_LIVE. That defeats the +DISCONNECTING-state guard in nvmet_tcp_schedule_release_queue() and +allows a later socket state change to re-enter teardown and issue a +second kref_put() on an already released queue. + +The ICResp send failure path has the same problem. If teardown has +already moved the queue to DISCONNECTING, a send error can still +overwrite the state with NVMET_TCP_Q_FAILED, again reopening the +window for a second teardown path to drop the queue reference. + +Fix this by serializing both post-send state transitions with +state_lock and bailing out if teardown has already started. + +Use -ESHUTDOWN as an internal sentinel for that bail-out path rather +than propagating it as a transport error like -ECONNRESET. Keep +nvmet_tcp_socket_error() setting rcv_state to NVMET_TCP_RECV_ERR before +honoring that sentinel so receive-side parsing stays quiesced until the +existing release path completes. + +Fixes: c46a6465bac2 ("nvmet-tcp: add NVMe over TCP target driver") +Cc: stable@vger.kernel.org +Reported-by: Shivam Kumar +Tested-by: Shivam Kumar +Signed-off-by: Chaitanya Kulkarni +Signed-off-by: Keith Busch +[ context diff adaptation: drop `queue->state = NVMET_TCP_Q_FAILED` since + the enum introduced in 6.7, 675b453e0241 ("nvmet-tcp: enable TLS handshake + upcall" ] +Signed-off-by: Philo Lu +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/tcp.c | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c +index 29ed707e76e06d..06cfcf0850076b 100644 +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -357,6 +357,19 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) + + static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue) + { ++ /* ++ * Keep rcv_state at RECV_ERR even for the internal -ESHUTDOWN path. ++ * nvmet_tcp_handle_icreq() can return -ESHUTDOWN after the ICReq has ++ * already been consumed and queue teardown has started. ++ * ++ * If nvmet_tcp_data_ready() or nvmet_tcp_write_space() queues ++ * nvmet_tcp_io_work() again before nvmet_tcp_release_queue_work() ++ * cancels it, the queue must not keep that old receive state. ++ * Otherwise the next nvmet_tcp_io_work() run can reach ++ * nvmet_tcp_done_recv_pdu() and try to handle the same ICReq again. ++ * ++ * That is why queue->rcv_state needs to be updated before we return. ++ */ + queue->rcv_state = NVMET_TCP_RECV_ERR; + if (queue->nvme_sq.ctrl) + nvmet_ctrl_fatal_error(queue->nvme_sq.ctrl); +@@ -900,10 +913,24 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) + iov.iov_base = icresp; + iov.iov_len = sizeof(*icresp); + ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len); +- if (ret < 0) ++ if (ret < 0) { ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ return -ESHUTDOWN; ++ } ++ spin_unlock_bh(&queue->state_lock); + return ret; /* queue removal will cleanup */ ++ } + ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ /* Tell nvmet_tcp_socket_error() teardown is in progress. */ ++ return -ESHUTDOWN; ++ } + queue->state = NVMET_TCP_Q_LIVE; ++ spin_unlock_bh(&queue->state_lock); + nvmet_prepare_receive_pdu(queue); + return 0; + } +-- +2.53.0 + diff --git a/staging-5.15/series b/staging-5.15/series new file mode 100644 index 0000000000..3e69bb8311 --- /dev/null +++ b/staging-5.15/series @@ -0,0 +1 @@ +nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch diff --git a/staging-6.1/bpf-arm64-reject-out-of-range-b.cond-targets.patch b/staging-6.1/bpf-arm64-reject-out-of-range-b.cond-targets.patch new file mode 100644 index 0000000000..23ab426e54 --- /dev/null +++ b/staging-6.1/bpf-arm64-reject-out-of-range-b.cond-targets.patch @@ -0,0 +1,58 @@ +From c77d935ccc7dcc6381a5f77ccc49b45d6457d124 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Apr 2026 14:14:02 +0200 +Subject: bpf, arm64: Reject out-of-range B.cond targets + +From: Daniel Borkmann + +[ Upstream commit 48d83d94930eb4db4c93d2de44838b9455cff626 ] + +aarch64_insn_gen_cond_branch_imm() calls label_imm_common() to +compute a 19-bit signed byte offset for a conditional branch, +but unlike its siblings aarch64_insn_gen_branch_imm() and +aarch64_insn_gen_comp_branch_imm(), it does not check whether +label_imm_common() returned its out-of-range sentinel (range) +before feeding the value to aarch64_insn_encode_immediate(). + +aarch64_insn_encode_immediate() unconditionally masks the value +with the 19-bit field mask, so an offset that was rejected by +label_imm_common() gets silently truncated. With the sentinel +value SZ_1M, the resulting field ends up with bit 18 (the sign +bit of the 19-bit signed displacement) set, and the CPU decodes +it as a ~1 MiB *backward* branch, producing an incorrectly +targeted B.cond instruction. For code-gen locations like the +emit_bpf_tail_call() this function is the only barrier between +an overflowing displacement and a silently miscompiled branch. + +Fix it by returning AARCH64_BREAK_FAULT when the offset is out +of range, so callers see a loud failure instead of a silently +misencoded branch. validate_code() scans the generated image +for any AARCH64_BREAK_FAULT and then lets the JIT fail. + +Fixes: 345e0d35ecdd ("arm64: introduce aarch64_insn_gen_cond_branch_imm()") +Fixes: c94ae4f7c5ec ("arm64: insn: remove BUG_ON from codegen") +Signed-off-by: Daniel Borkmann +Reviewed-by: Puranjay Mohan +Link: https://lore.kernel.org/r/20260415121403.639619-1-daniel@iogearbox.net +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +--- + arch/arm64/lib/insn.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c +index 44bb90ee2f414b..a50f6c061c27a2 100644 +--- a/arch/arm64/lib/insn.c ++++ b/arch/arm64/lib/insn.c +@@ -423,6 +423,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, + long offset; + + offset = label_imm_common(pc, addr, SZ_1M); ++ if (offset >= SZ_1M) ++ return AARCH64_BREAK_FAULT; + + insn = aarch64_insn_get_bcond_value(); + +-- +2.53.0 + diff --git a/staging-6.1/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch b/staging-6.1/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch new file mode 100644 index 0000000000..2e09e3d7a9 --- /dev/null +++ b/staging-6.1/nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch @@ -0,0 +1,108 @@ +From 86e1af6d17772cb8dac1e21f804485898136464e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 20:36:44 +0800 +Subject: nvmet-tcp: fix race between ICReq handling and queue teardown + +From: Chaitanya Kulkarni + +commit 5293a8882c549fab4a878bc76b0b6c951f980a61 upstream. + +nvmet_tcp_handle_icreq() updates queue->state after sending an +Initialization Connection Response (ICResp), but it does so without +serializing against target-side queue teardown. + +If an NVMe/TCP host sends an Initialization Connection Request +(ICReq) and immediately closes the connection, target-side teardown +may start in softirq context before io_work drains the already +buffered ICReq. In that case, nvmet_tcp_schedule_release_queue() +sets queue->state to NVMET_TCP_Q_DISCONNECTING and drops the queue +reference under state_lock. + +If io_work later processes that ICReq, nvmet_tcp_handle_icreq() can +still overwrite the state back to NVMET_TCP_Q_LIVE. That defeats the +DISCONNECTING-state guard in nvmet_tcp_schedule_release_queue() and +allows a later socket state change to re-enter teardown and issue a +second kref_put() on an already released queue. + +The ICResp send failure path has the same problem. If teardown has +already moved the queue to DISCONNECTING, a send error can still +overwrite the state with NVMET_TCP_Q_FAILED, again reopening the +window for a second teardown path to drop the queue reference. + +Fix this by serializing both post-send state transitions with +state_lock and bailing out if teardown has already started. + +Use -ESHUTDOWN as an internal sentinel for that bail-out path rather +than propagating it as a transport error like -ECONNRESET. Keep +nvmet_tcp_socket_error() setting rcv_state to NVMET_TCP_RECV_ERR before +honoring that sentinel so receive-side parsing stays quiesced until the +existing release path completes. + +Fixes: c46a6465bac2 ("nvmet-tcp: add NVMe over TCP target driver") +Cc: stable@vger.kernel.org +Reported-by: Shivam Kumar +Tested-by: Shivam Kumar +Signed-off-by: Chaitanya Kulkarni +Signed-off-by: Keith Busch +[ context diff adaptation: drop `queue->state = NVMET_TCP_Q_FAILED` since + the enum introduced in 6.7, 675b453e0241 ("nvmet-tcp: enable TLS handshake + upcall" ] +Signed-off-by: Philo Lu +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/tcp.c | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c +index 01d685499b97de..a0751ca359f57e 100644 +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -353,6 +353,19 @@ static int nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) + + static void nvmet_tcp_fatal_error(struct nvmet_tcp_queue *queue) + { ++ /* ++ * Keep rcv_state at RECV_ERR even for the internal -ESHUTDOWN path. ++ * nvmet_tcp_handle_icreq() can return -ESHUTDOWN after the ICReq has ++ * already been consumed and queue teardown has started. ++ * ++ * If nvmet_tcp_data_ready() or nvmet_tcp_write_space() queues ++ * nvmet_tcp_io_work() again before nvmet_tcp_release_queue_work() ++ * cancels it, the queue must not keep that old receive state. ++ * Otherwise the next nvmet_tcp_io_work() run can reach ++ * nvmet_tcp_done_recv_pdu() and try to handle the same ICReq again. ++ * ++ * That is why queue->rcv_state needs to be updated before we return. ++ */ + queue->rcv_state = NVMET_TCP_RECV_ERR; + if (queue->nvme_sq.ctrl) + nvmet_ctrl_fatal_error(queue->nvme_sq.ctrl); +@@ -896,10 +909,24 @@ static int nvmet_tcp_handle_icreq(struct nvmet_tcp_queue *queue) + iov.iov_base = icresp; + iov.iov_len = sizeof(*icresp); + ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len); +- if (ret < 0) ++ if (ret < 0) { ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ return -ESHUTDOWN; ++ } ++ spin_unlock_bh(&queue->state_lock); + return ret; /* queue removal will cleanup */ ++ } + ++ spin_lock_bh(&queue->state_lock); ++ if (queue->state == NVMET_TCP_Q_DISCONNECTING) { ++ spin_unlock_bh(&queue->state_lock); ++ /* Tell nvmet_tcp_socket_error() teardown is in progress. */ ++ return -ESHUTDOWN; ++ } + queue->state = NVMET_TCP_Q_LIVE; ++ spin_unlock_bh(&queue->state_lock); + nvmet_prepare_receive_pdu(queue); + return 0; + } +-- +2.53.0 + diff --git a/staging-6.1/series b/staging-6.1/series new file mode 100644 index 0000000000..403a0254b1 --- /dev/null +++ b/staging-6.1/series @@ -0,0 +1,2 @@ +nvmet-tcp-fix-race-between-icreq-handling-and-queue-.patch +bpf-arm64-reject-out-of-range-b.cond-targets.patch diff --git a/staging-6.12/bpf-arm64-reject-out-of-range-b.cond-targets.patch b/staging-6.12/bpf-arm64-reject-out-of-range-b.cond-targets.patch new file mode 100644 index 0000000000..797db78b48 --- /dev/null +++ b/staging-6.12/bpf-arm64-reject-out-of-range-b.cond-targets.patch @@ -0,0 +1,59 @@ +From 808214007d7d6854270a3740dc78e83c54524002 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 16:07:56 +0800 +Subject: bpf, arm64: Reject out-of-range B.cond targets + +From: Daniel Borkmann + +commit 48d83d94930eb4db4c93d2de44838b9455cff626 upstream. + +aarch64_insn_gen_cond_branch_imm() calls label_imm_common() to +compute a 19-bit signed byte offset for a conditional branch, +but unlike its siblings aarch64_insn_gen_branch_imm() and +aarch64_insn_gen_comp_branch_imm(), it does not check whether +label_imm_common() returned its out-of-range sentinel (range) +before feeding the value to aarch64_insn_encode_immediate(). + +aarch64_insn_encode_immediate() unconditionally masks the value +with the 19-bit field mask, so an offset that was rejected by +label_imm_common() gets silently truncated. With the sentinel +value SZ_1M, the resulting field ends up with bit 18 (the sign +bit of the 19-bit signed displacement) set, and the CPU decodes +it as a ~1 MiB *backward* branch, producing an incorrectly +targeted B.cond instruction. For code-gen locations like the +emit_bpf_tail_call() this function is the only barrier between +an overflowing displacement and a silently miscompiled branch. + +Fix it by returning AARCH64_BREAK_FAULT when the offset is out +of range, so callers see a loud failure instead of a silently +misencoded branch. validate_code() scans the generated image +for any AARCH64_BREAK_FAULT and then lets the JIT fail. + +Fixes: 345e0d35ecdd ("arm64: introduce aarch64_insn_gen_cond_branch_imm()") +Fixes: c94ae4f7c5ec ("arm64: insn: remove BUG_ON from codegen") +Signed-off-by: Daniel Borkmann +Reviewed-by: Puranjay Mohan +Link: https://lore.kernel.org/r/20260415121403.639619-1-daniel@iogearbox.net +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + arch/arm64/lib/insn.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c +index 36d33e064ea01b..b7cabd64bcc860 100644 +--- a/arch/arm64/lib/insn.c ++++ b/arch/arm64/lib/insn.c +@@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, + long offset; + + offset = label_imm_common(pc, addr, SZ_1M); ++ if (offset >= SZ_1M) ++ return AARCH64_BREAK_FAULT; + + insn = aarch64_insn_get_bcond_value(); + +-- +2.53.0 + diff --git a/staging-6.12/series b/staging-6.12/series new file mode 100644 index 0000000000..5702e744cc --- /dev/null +++ b/staging-6.12/series @@ -0,0 +1 @@ +bpf-arm64-reject-out-of-range-b.cond-targets.patch diff --git a/staging-6.18/bpf-arm64-reject-out-of-range-b.cond-targets.patch b/staging-6.18/bpf-arm64-reject-out-of-range-b.cond-targets.patch new file mode 100644 index 0000000000..b1ab5ae93e --- /dev/null +++ b/staging-6.18/bpf-arm64-reject-out-of-range-b.cond-targets.patch @@ -0,0 +1,59 @@ +From 82e716a5a657bb911c85431dd213220c0bda4213 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 2 Jul 2026 16:07:56 +0800 +Subject: bpf, arm64: Reject out-of-range B.cond targets + +From: Daniel Borkmann + +commit 48d83d94930eb4db4c93d2de44838b9455cff626 upstream. + +aarch64_insn_gen_cond_branch_imm() calls label_imm_common() to +compute a 19-bit signed byte offset for a conditional branch, +but unlike its siblings aarch64_insn_gen_branch_imm() and +aarch64_insn_gen_comp_branch_imm(), it does not check whether +label_imm_common() returned its out-of-range sentinel (range) +before feeding the value to aarch64_insn_encode_immediate(). + +aarch64_insn_encode_immediate() unconditionally masks the value +with the 19-bit field mask, so an offset that was rejected by +label_imm_common() gets silently truncated. With the sentinel +value SZ_1M, the resulting field ends up with bit 18 (the sign +bit of the 19-bit signed displacement) set, and the CPU decodes +it as a ~1 MiB *backward* branch, producing an incorrectly +targeted B.cond instruction. For code-gen locations like the +emit_bpf_tail_call() this function is the only barrier between +an overflowing displacement and a silently miscompiled branch. + +Fix it by returning AARCH64_BREAK_FAULT when the offset is out +of range, so callers see a loud failure instead of a silently +misencoded branch. validate_code() scans the generated image +for any AARCH64_BREAK_FAULT and then lets the JIT fail. + +Fixes: 345e0d35ecdd ("arm64: introduce aarch64_insn_gen_cond_branch_imm()") +Fixes: c94ae4f7c5ec ("arm64: insn: remove BUG_ON from codegen") +Signed-off-by: Daniel Borkmann +Reviewed-by: Puranjay Mohan +Link: https://lore.kernel.org/r/20260415121403.639619-1-daniel@iogearbox.net +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Sasha Levin +--- + arch/arm64/lib/insn.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c +index 4e298baddc2e56..e9e9a70ac155c8 100644 +--- a/arch/arm64/lib/insn.c ++++ b/arch/arm64/lib/insn.c +@@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, + long offset; + + offset = label_imm_common(pc, addr, SZ_1M); ++ if (offset >= SZ_1M) ++ return AARCH64_BREAK_FAULT; + + insn = aarch64_insn_get_bcond_value(); + +-- +2.53.0 + diff --git a/staging-6.18/series b/staging-6.18/series new file mode 100644 index 0000000000..5702e744cc --- /dev/null +++ b/staging-6.18/series @@ -0,0 +1 @@ +bpf-arm64-reject-out-of-range-b.cond-targets.patch diff --git a/staging-6.6/bpf-arm64-reject-out-of-range-b.cond-targets.patch b/staging-6.6/bpf-arm64-reject-out-of-range-b.cond-targets.patch new file mode 100644 index 0000000000..460e212fd4 --- /dev/null +++ b/staging-6.6/bpf-arm64-reject-out-of-range-b.cond-targets.patch @@ -0,0 +1,58 @@ +From 0971f2e442a3fdb8d14229d89c380c62cbf6543b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Apr 2026 14:14:02 +0200 +Subject: bpf, arm64: Reject out-of-range B.cond targets + +From: Daniel Borkmann + +[ Upstream commit 48d83d94930eb4db4c93d2de44838b9455cff626 ] + +aarch64_insn_gen_cond_branch_imm() calls label_imm_common() to +compute a 19-bit signed byte offset for a conditional branch, +but unlike its siblings aarch64_insn_gen_branch_imm() and +aarch64_insn_gen_comp_branch_imm(), it does not check whether +label_imm_common() returned its out-of-range sentinel (range) +before feeding the value to aarch64_insn_encode_immediate(). + +aarch64_insn_encode_immediate() unconditionally masks the value +with the 19-bit field mask, so an offset that was rejected by +label_imm_common() gets silently truncated. With the sentinel +value SZ_1M, the resulting field ends up with bit 18 (the sign +bit of the 19-bit signed displacement) set, and the CPU decodes +it as a ~1 MiB *backward* branch, producing an incorrectly +targeted B.cond instruction. For code-gen locations like the +emit_bpf_tail_call() this function is the only barrier between +an overflowing displacement and a silently miscompiled branch. + +Fix it by returning AARCH64_BREAK_FAULT when the offset is out +of range, so callers see a loud failure instead of a silently +misencoded branch. validate_code() scans the generated image +for any AARCH64_BREAK_FAULT and then lets the JIT fail. + +Fixes: 345e0d35ecdd ("arm64: introduce aarch64_insn_gen_cond_branch_imm()") +Fixes: c94ae4f7c5ec ("arm64: insn: remove BUG_ON from codegen") +Signed-off-by: Daniel Borkmann +Reviewed-by: Puranjay Mohan +Link: https://lore.kernel.org/r/20260415121403.639619-1-daniel@iogearbox.net +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +--- + arch/arm64/lib/insn.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/lib/insn.c b/arch/arm64/lib/insn.c +index 7232b1e70a125f..cb845b3649a188 100644 +--- a/arch/arm64/lib/insn.c ++++ b/arch/arm64/lib/insn.c +@@ -338,6 +338,8 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, + long offset; + + offset = label_imm_common(pc, addr, SZ_1M); ++ if (offset >= SZ_1M) ++ return AARCH64_BREAK_FAULT; + + insn = aarch64_insn_get_bcond_value(); + +-- +2.53.0 + diff --git a/staging-6.6/series b/staging-6.6/series new file mode 100644 index 0000000000..5702e744cc --- /dev/null +++ b/staging-6.6/series @@ -0,0 +1 @@ +bpf-arm64-reject-out-of-range-b.cond-targets.patch -- 2.47.3