Re: [LTP] [PATCH 2/2] device-drivers/rdma: Add ucma_uaf01 test

Petr Vorel
2026-04-07T13:24:33Z

Hi Andrea,

it's been long time since this use-after-free was fixed, but IMHO still useful
to have a test (it's also kind of smoke test for rdma_cm).
Anyway, LGTM, but it'd be nice to reproduce the bug.

Reviewed-by: Petr Vorel <pvorel@suse.cz>

> Test for use-after-free in RDMA UCMA triggered by racing CREATE_ID,
> BIND_IP, and LISTEN operations. Three threads concurrently issue
> these commands to /dev/infiniband/rdma_cm and the test checks for
> kernel taint (KASAN use-after-free detection).

> The bug was fixed by kernel commit 5fe23f262e05
> ("ucma: fix a use-after-free in ucma_resolve_ip()").

> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> runtest/kernel_misc | 1 +
> testcases/kernel/device-drivers/Makefile | 1 +
> testcases/kernel/device-drivers/rdma/.gitignore | 1 +
> testcases/kernel/device-drivers/rdma/Makefile | 7 +
> testcases/kernel/device-drivers/rdma/ucma_uaf01.c | 208 ++++++++++++++++++++++
> 5 files changed, 218 insertions(+)

> diff --git a/runtest/kernel_misc b/runtest/kernel_misc
> index 78f00d305fea10367fb4fd2845f25dd151a833ea..dcc3c0a44fb52a968f91a52758dbd43a3ce7a9ec 100644
> --- a/runtest/kernel_misc
> +++ b/runtest/kernel_misc
> @@ -3,6 +3,7 @@ kmsg01 kmsg01
> fw_load fw_load
> rtc01 rtc01
> rtc02 rtc02
> +ucma_uaf01 ucma_uaf01
> block_dev block_dev
> tpci tpci
> tbio tbio
> diff --git a/testcases/kernel/device-drivers/Makefile b/testcases/kernel/device-drivers/Makefile
> index 229a50683f5f629904ff591daa6fcd4f1c35fdf1..538df555395bf21062906ffa4125da4c767c1e24 100644
> --- a/testcases/kernel/device-drivers/Makefile
> +++ b/testcases/kernel/device-drivers/Makefile
> @@ -11,6 +11,7 @@ SUBDIRS := acpi \
> locking \
> pci \
> rcu \
> + rdma \
> rtc \
> tbio \
> uaccess \
> diff --git a/testcases/kernel/device-drivers/rdma/.gitignore b/testcases/kernel/device-drivers/rdma/.gitignore
> new file mode 100644
> index 0000000000000000000000000000000000000000..399ea290e4f9abd6b66800b21f4aea3eb33d3799
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/rdma/.gitignore
> @@ -0,0 +1 @@
> +/ucma_uaf01
> diff --git a/testcases/kernel/device-drivers/rdma/Makefile b/testcases/kernel/device-drivers/rdma/Makefile
> new file mode 100644
> index 0000000000000000000000000000000000000000..5df01972aeab257b6ef24a928204b6b722c1cdef
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/rdma/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# Copyright (c) 2026 Linux Test Project
> +
> +top_srcdir ?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/device-drivers/rdma/ucma_uaf01.c b/testcases/kernel/device-drivers/rdma/ucma_uaf01.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..313e2aee0ea0114ce37f006eca93ea66d86ddeea
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/rdma/ucma_uaf01.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2026 Linux Test Project
> + */
> +
> +/*\
> + * Test for use-after-free in RDMA UCMA triggered by concurrent CREATE_ID,
> + * BIND_IP, and LISTEN operations via /dev/infiniband/rdma_cm.
> + *
> + * Requires root to open /dev/infiniband/rdma_cm.
> + *
> + * Three threads race to create, bind, and listen on RDMA connection manager
> + * IDs. On vulnerable kernels, this triggers a use-after-free in
> + * cma_listen_on_all() detected by KASAN.
> + *
> + * Based on a syzbot reproducer:
> + * syzbot+db1c219466daac1083df@syzkaller.appspotmail.com

Maybe link simplified C source from Eric on which you base LTP test (according
to the cover letter)?
https://lore.kernel.org/lkml/20180513230237.GG677@sol.localdomain/

NOTE C reproducer [1] on db1c219466daac1083df page [2] is more complicated than
the one From Eric.

[1] https://syzkaller.appspot.com/text?tag=ReproC&x=1258d593800000
[2] https://syzkaller.appspot.com/bug?extid=db1c219466daac1083df

Kind regards,
Petr

> + *
> + * Fixed in:
> + *
> + * commit 5fe23f262e05
> + * ucma: fix a use-after-free in ucma_resolve_ip()
> + */
> +
> +#include "tst_test.h"
> +#include "tst_safe_pthread.h"
> +#include "lapi/rdma_user_cm.h"
> +
> +#define RDMA_CM_DEV "/dev/infiniband/rdma_cm"
> +
> +static int cmfd = -1;
> +static volatile uint32_t shared_id;
> +static volatile int stop_threads;
> +
> +static void destroy_id(uint32_t id)
> +{
> + ssize_t ret;
> +
> + struct {
> + struct rdma_ucm_cmd_hdr hdr;
> + struct rdma_ucm_destroy_id destroy;
> + } msg = {
> + .hdr = {
> + .cmd = RDMA_USER_CM_CMD_DESTROY_ID,
> + .out = sizeof(struct rdma_ucm_create_id_resp),
> + },
> + .destroy = {
> + .id = id,
> + },
> + };
> + struct rdma_ucm_create_id_resp resp;
> +
> + msg.destroy.response = (uintptr_t)&resp;
> +
> + /* Errors expected due to racing with stale IDs */
> + ret = write(cmfd, &msg, sizeof(msg));
> + (void)ret;
> +}
> +
> +static void *thread_create(void *arg)
> +{
> + uint32_t id, prev_id = 0;
> + int has_prev = 0;
> +
> + while (!stop_threads) {
> + struct {
> + struct rdma_ucm_cmd_hdr hdr;
> + struct rdma_ucm_create_id create;
> + } msg = {
> + .hdr = {
> + .cmd = RDMA_USER_CM_CMD_CREATE_ID,
> + .out = sizeof(id),
> + },
> + .create = {
> + .response = (uintptr_t)&id,
> + .ps = RDMA_PS_IPOIB,
> + },
> + };
> +
> + if (write(cmfd, &msg, sizeof(msg)) > 0) {
> + if (has_prev)
> + destroy_id(prev_id);
> + prev_id = id;
> + has_prev = 1;
> + shared_id = id;
> + }
> + }
> +
> + if (has_prev)
> + destroy_id(prev_id);
> +
> + return arg;
> +}
> +
> +static void *thread_bind(void *arg)
> +{
> + ssize_t ret;
> +
> + while (!stop_threads) {
> + struct {
> + struct rdma_ucm_cmd_hdr hdr;
> + struct rdma_ucm_bind_ip bind;
> + } msg = {
> + .hdr = {
> + .cmd = RDMA_USER_CM_CMD_BIND_IP,
> + },
> + .bind = {
> + .addr = {
> + .sin6_family = AF_INET6,
> + .sin6_addr = {
> + .s6_addr = { 0xff },
> + },
> + },
> + .id = shared_id,
> + },
> + };
> +
> + /* Errors expected due to racing with stale IDs */
> + ret = write(cmfd, &msg, sizeof(msg));
> + (void)ret;
> + }
> +
> + return arg;
> +}
> +
> +static void *thread_listen(void *arg)
> +{
> + ssize_t ret;
> +
> + while (!stop_threads) {
> + struct {
> + struct rdma_ucm_cmd_hdr hdr;
> + struct rdma_ucm_listen listen;
> + } msg = {
> + .hdr = {
> + .cmd = RDMA_USER_CM_CMD_LISTEN,
> + },
> + .listen = {
> + .id = shared_id,
> + },
> + };
> +
> + /* Errors expected due to racing with stale IDs */
> + ret = write(cmfd, &msg, sizeof(msg));
> + (void)ret;
> + }
> +
> + return arg;
> +}
> +
> +static void setup(void)
> +{
> + cmfd = open(RDMA_CM_DEV, O_WRONLY);
> + if (cmfd < 0) {
> + if (errno == ENOENT || errno == ENXIO)
> + tst_brk(TCONF, RDMA_CM_DEV " not available");
> + tst_brk(TBROK | TERRNO, "open(" RDMA_CM_DEV ")");
> + }
> +}
> +
> +static void cleanup(void)
> +{
> + if (cmfd != -1)
> + SAFE_CLOSE(cmfd);
> +}
> +
> +static void run(void)
> +{
> + pthread_t threads[3];
> +
> + stop_threads = 0;
> +
> + SAFE_PTHREAD_CREATE(&threads[0], NULL, thread_create, NULL);
> + SAFE_PTHREAD_CREATE(&threads[1], NULL, thread_bind, NULL);
> + SAFE_PTHREAD_CREATE(&threads[2], NULL, thread_listen, NULL);
> +
> + while (tst_remaining_runtime())
> + sleep(1);
> +
> + stop_threads = 1;
> +
> + SAFE_PTHREAD_JOIN(threads[0], NULL);
> + SAFE_PTHREAD_JOIN(threads[1], NULL);
> + SAFE_PTHREAD_JOIN(threads[2], NULL);
> +
> + if (tst_taint_check())
> + tst_res(TFAIL, "Kernel is vulnerable (use-after-free in UCMA)");
> + else
> + tst_res(TPASS, "No kernel taint detected");
> +}
> +
> +static struct tst_test test = {
> + .test_all = run,
> + .setup = setup,
> + .cleanup = cleanup,
> + .runtime = 300,
> + .needs_root = 1,
> + .taint_check = TST_TAINT_W | TST_TAINT_D,
> + .needs_kconfigs = (const char *[]) {
> + "CONFIG_INFINIBAND",
> + "CONFIG_INFINIBAND_USER_ACCESS",
> + NULL
> + },
> + .tags = (const struct tst_tag[]) {
> + {"linux-git", "5fe23f262e05"},
> + {}
> + },
> +};