The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [BUG] RDMA/rxe: memory leak in rxe_qp_from_attr
@ 2026-07-30  8:40 Peiyang He
  2026-07-30 18:07 ` yanjun.zhu
  0 siblings, 1 reply; 2+ messages in thread
From: Peiyang He @ 2026-07-30  8:40 UTC (permalink / raw)
  To: jgg, yanjun.zhu, leon
  Cc: syzkaller@googlegroups.com, linux-rdma,
	linux-kernel@vger.kernel.org

Hello Linux kernel developers and maintainers,

We hit a memory leak in rxe_qp_from_attr when fuzzing RDMA/RXE with Syzkaller.

The leak is caused by two concurrent MODIFY_QP commands which both modify
IB_QP_MAX_DEST_RD_ATOMIC on the same RXE QP. They are not serialized by a
QP-wide lock. The uverbs layer only holds shared UVERBS_LOOKUP_READ access
to the QP, so two threads may enter rxe_modify_qp() and then rxe_qp_from_attr()
for the same object at the same time.
It can leak a whole qp->resp.resources array each time triggered.

The version of the kernel under test is commit 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 (tag v7.1).
We believe this OOB bug is still possible in the latest kernel.

The relevant kernel config is:

CONFIG_NET=y
CONFIG_INET=y
CONFIG_INFINIBAND=y
CONFIG_INFINIBAND_USER_ACCESS=y
CONFIG_INFINIBAND_ADDR_TRANS=y
CONFIG_RDMA_RXE=y
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN=y

=========================
Original Syzkaller report
=========================

BUG: memory leak
unreferenced object 0xffff8880120ef800 (size 1024):
  comm "syz.0.1478", pid 15130, jiffies 4295203468
  hex dump (first 32 bytes):
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  backtrace (crc 0):
    kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
    slab_post_alloc_hook mm/slub.c:4575 [inline]
    slab_alloc_node mm/slub.c:4899 [inline]
    __do_kmalloc_node mm/slub.c:5295 [inline]
    __kmalloc_noprof+0x552/0x850 mm/slub.c:5308
    kmalloc_noprof include/linux/slab.h:954 [inline]
    kzalloc_noprof include/linux/slab.h:1188 [inline]
    alloc_rd_atomic_resources drivers/infiniband/sw/rxe/rxe_qp.c:155 [inline]
    rxe_qp_from_attr+0x3f8/0x2150 drivers/infiniband/sw/rxe/rxe_qp.c:714
    rxe_modify_qp+0x1e2/0x530 drivers/infiniband/sw/rxe/rxe_verbs.c:623
    ib_security_modify_qp+0x223/0xfa0 drivers/infiniband/core/security.c:625
    _ib_modify_qp+0x333/0xec0 drivers/infiniband/core/verbs.c:1915
    modify_qp+0x13ca/0x1940 drivers/infiniband/core/uverbs_cmd.c:1932
    ib_uverbs_modify_qp+0xcb/0x120 drivers/infiniband/core/uverbs_cmd.c:1958
    ib_uverbs_write+0xb86/0x1030 drivers/infiniband/core/uverbs_main.c:680
    vfs_write+0x2aa/0x1070 fs/read_write.c:686
    ksys_write+0x1f8/0x250 fs/read_write.c:740
    do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
    do_syscall_64+0x116/0x800 arch/x86/entry/syscall_64.c:94
    entry_SYSCALL_64_after_hwframe+0x77/0x7f

==========
Root cause
==========

modify_qp() obtains the QP through uobj_get_obj_read() and retains the
UVERBS_LOOKUP_READ access. This is a shared lookup, since UVERBS_LOOKUP_READ
increments a positive use count and only rejects an object whose count is -1,
whereas UVERBS_LOOKUP_WRITE is exclusive. Therefore, the two MODIFY_QP commands
may execute rxe_modify_qp() concurrently. rxe_modify_qp() directly calls
rxe_qp_from_attr() without a QP-wide lock.

For IB_QP_MAX_DEST_RD_ATOMIC, rxe_qp_from_attr() updates qp->attr.max_dest_rd_atomic,
frees the current qp->resp.resources pointer and calls alloc_rd_atomic_resources().
The allocator initializes the responder indices and assigns the result of kzalloc_objs()
to qp->resp.resources. Neither the resource replacement sequence nor the pointer assignment
is serialized.

The relevant QP starts with qp->resp.resources == NULL. Let P0 and P1 be the
two resp_res arrays allocated by the concurrent commands. The following
race interleaving leaks P1:

CPU0                                           CPU1
====                                           ====

MODIFY_QP()
-> rxe_qp_from_attr()
   -> alloc_rd_atomic_resources()
      -> P0 = kzalloc_objs()
      /* preempted after kzalloc_objs() returns P0,
         before the assignment to qp->resp.resources */

                                               MODIFY_QP()
                                               -> rxe_qp_from_attr()
                                                -> alloc_rd_atomic_resources()
                                                   -> P1 = kzalloc_objs()
                                                   -> qp->resp.resources = P1

      -> qp->resp.resources = P0
      /* overwrites the only reference to P1 */

When the QP is later cleaned up, rxe_qp_do_cleanup() calls free_rd_atomic_resources()
and can only free the pointer currently stored in qp->resp.resources, namely P0.
P1 has no remaining reference to it and is reported by kmemleak.

=============
Suggested fix
=============

First of all, I wonder is concurrent modify_qp() on the same QP supposed
to be supported by design? If yes, then the rd_atomic resource replacement
path needs explicit serialization. We could add a rd_atomic_mutex in
struct rxe_qp, init it in rxe_qp_init_misc(). Then, the IB_QP_MAX_DEST_RD_ATOMIC
branch in rxe_qp_from_attr() and rxe_qp_reset() should be protected by this lock.

I am happy to send a patch if this fix is needed and reasonable.

Thanks,
Peiyang


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-30 18:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30  8:40 [BUG] RDMA/rxe: memory leak in rxe_qp_from_attr Peiyang He
2026-07-30 18:07 ` yanjun.zhu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox