wr_opcode_mask() indexes the global rxe_wr_opcode_info[] array with a caller-supplied opcode and no bounds check. For a user QP the WQE comes from an mmap'd SQ ring, so wqe->wr.opcode is an attacker-controlled __u32 read back by the requester (req_next_wqe() -> rxe_requester()); rxe_post_send() takes the qp->is_user branch and never runs validate_send_wr(), whose only opcode check is itself !wr_opcode_mask() (it indexes before it checks). rxe_wr_opcode_info[] has entries only up to IB_WR_REG_MR, so an out-of-range opcode reads out of bounds and the result is used as a mask and dereferenced; observed as a KASAN global-out-of-bounds "Read of size 4" and a wild-pointer oops. Return a zero mask for an out-of-range opcode, matching how validate_send_wr() already treats a zero mask (an invalid WR), so no path indexes the array out of bounds. Fixes: 8700e3e7c485 ("Soft RoCE driver") Cc: stable@vger.kernel.org Signed-off-by: Youngsung Ahn Assisted-by: LLM --- Notes (not part of the commit): Reproduced on a KASAN x86-64 build of 7.3-rc4 as uid 1000. Present unchanged in mainline 551c722f4080 (2026-09-29) and rdma for-next. Compile-tested (KASAN+RDMA_RXE), not runtime-tested. Per security-bugs.rst this is public and a reproducer can be shared on request. IB_WR_REG_MR is the highest index the array initializes; an ARRAY_SIZE(rxe_wr_opcode_info) bound would be equivalent but the array is extern to this header. v2: no code change. The irregular indentation in v1 was email mangling (HTML), not a coding-style problem -- as Willy Tarreau noted on the ODP thread; resent as plain text with tabs preserved via git send-email. Added Cc: stable, fixed the author/Signed-off-by match, and added Assisted-by: LLM. drivers/infiniband/sw/rxe/rxe_loc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 64d636bf80fd..dcc0788db90a 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -184,6 +184,9 @@ void rxe_comp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb); static inline unsigned int wr_opcode_mask(int opcode, struct rxe_qp *qp) { + if (unlikely(opcode < 0 || opcode > IB_WR_REG_MR)) + return 0; + return rxe_wr_opcode_info[opcode].mask[qp->ibqp.qp_type]; } -- 2.43.0