Rust for Linux List
 help / color / mirror / Atom feed
From: Yuan Tan <ytan089@ucr.edu>
To: a.hindborg@kernel.org, ojeda@kernel.org, boqun@kernel.org,
	rust-for-linux@vger.kernel.org
Cc: zhiyunq@cs.ucr.edu, ardalan@uci.edu, pgovind2@uci.edu,
	dzueck@uci.edu, Yuan Tan <ytan089@ucr.edu>
Subject: [PATCH 0/1] rust: block: mq: make GenDisk Send impl sound
Date: Thu,  4 Jun 2026 21:49:55 -0700	[thread overview]
Message-ID: <cover.1780633578.git.ytan089@ucr.edu> (raw)

Hi Linux kernel maintainers,

We are developing a tool called FerroLens to detect potential unsound
behavior in Rust code in the Linux kernel. FerroLens reported the following
bug in rust/kernel/gendisk.rs.

Gendisk is marked as Send although the fields it contains may not be.
Specifically, the QueueData held in the raw gendisk pointer may not be safe
to send across threads. Therefore, sending the Gendisk from one thread to
another and dropping on a different thread may cause unsound behavior.

Additionally, Gendisk contains an Arc<TagSet<T>>. This Arc would be Send
and Sync if the underlying TagSet<T> were Send and Sync. But this is not
explicitly derived, although it can be, since the API does not modify the
TagSet.

We're not Rust experts, so we may have gotten some things wrong. We'd greatly
appreciate any corrections.


I tried creating a PoC to trigger this bug and make our findings more
solid.

We actually hacked the kernel a bit to enable KCSAN for the Rust pieces,
and it did generate a few crashes, but they are highly unstable.

BUG: KCSAN: data-race in drop_in_place<...GenDisk...> / __srcu_check_read_flavor

Here is the PoC I used. Hopefully, sharing it here in case it can helps
anyone better understand the bug.

---
python3 guest_kcsan_inflight_teardown.py --rounds 20 --inflight-threads 2 --io-threads 2 --io-burst 8 --round-cooldown 0.02

#!/usr/bin/env python3
import os
import threading
import time
import traceback


BASE = "/sys/kernel/config/rnull"
SYS_BLOCK = "/sys/block"
DEVICE_SIZE_MIB = 64
BLOCK_SIZE = 4096
ROUNDS = 220
INFLIGHT_THREADS = 8
POWER_OFF_DELAY_SEC = 0.015
POST_POWEROFF_SPIN_SEC = 0.12


def write_file(path: str, data: str) -> None:
    with open(path, "w", encoding="ascii") as f:
        f.write(data)


def wait_for_path(path: str, timeout_sec: float) -> bool:
    deadline = time.time() + timeout_sec
    while time.time() < deadline:
        if os.path.exists(path):
            return True
        time.sleep(0.001)
    return os.path.exists(path)


def create_device(name: str) -> str:
    path = f"{BASE}/{name}"
    os.mkdir(path)
    write_file(f"{path}/size", f"{DEVICE_SIZE_MIB}\n")
    write_file(f"{path}/blocksize", f"{BLOCK_SIZE}\n")
    write_file(f"{path}/irqmode", "1\n")
    write_file(f"{path}/power", "1\n")
    return path


def inflight_path(name: str) -> str:
    return f"{SYS_BLOCK}/{name}/inflight"


def inflight_worker(name: str, running: threading.Event, errors: list[str]) -> None:
    path = inflight_path(name)
    fd = None
    try:
        if not wait_for_path(path, 1.0):
            errors.append(f"{name}: inflight path not ready")
            return

        fd = os.open(path, os.O_RDONLY)
        while running.is_set():
            try:
                os.lseek(fd, 0, os.SEEK_SET)
                os.read(fd, 128)
            except OSError:
                pass
    except Exception as exc:
        errors.append(f"inflight {name}: {exc!r}\n{traceback.format_exc()}")
    finally:
        if fd is not None:
            try:
                os.close(fd)
            except OSError:
                pass


def destroy_device(path: str, errors: list[str]) -> None:
    for _ in range(1000):
        try:
            os.rmdir(path)
            return
        except OSError:
            time.sleep(0.002)
    errors.append(f"rmdir failed for {path}")


def device_round(name: str, errors: list[str]) -> None:
    path = create_device(name)
    running = threading.Event()
    running.set()

    workers = []
    for _ in range(INFLIGHT_THREADS):
        workers.append(threading.Thread(target=inflight_worker, args=(name, running, errors)))

    for worker in workers:
        worker.start()

    time.sleep(POWER_OFF_DELAY_SEC)
    write_file(f"{path}/power", "0\n")
    time.sleep(POST_POWEROFF_SPIN_SEC)
    running.clear()

    for worker in workers:
        worker.join()

    destroy_device(path, errors)


def main() -> int:
    errors: list[str] = []
    start = time.time()

    for round_id in range(ROUNDS):
        name = f"gi{round_id}"
        try:
            device_round(name, errors)
        except Exception as exc:
            errors.append(f"round {name}: {exc!r}\n{traceback.format_exc()}")

    duration = time.time() - start
    print(f"kcsan_inflight_teardown_done rounds={ROUNDS} duration={duration:.2f}s")
    print(f"errors={len(errors)}")
    for err in errors[:20]:
        print(err)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())



Yuan Tan (1):
  rust: block: mq: make GenDisk Send impl sound

 rust/kernel/block/mq/gen_disk.rs |  8 +++++---
 rust/kernel/block/mq/tag_set.rs  | 11 +++++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

-- 
2.43.2


             reply	other threads:[~2026-06-05  4:50 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-05  4:49 Yuan Tan [this message]
2026-06-05  4:49 ` [PATCH 1/1] rust: block: mq: make GenDisk Send impl sound Yuan Tan
2026-06-05 12:04 ` [PATCH 0/1] " Andreas Hindborg
2026-06-05 13:50 ` Gary Guo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cover.1780633578.git.ytan089@ucr.edu \
    --to=ytan089@ucr.edu \
    --cc=a.hindborg@kernel.org \
    --cc=ardalan@uci.edu \
    --cc=boqun@kernel.org \
    --cc=dzueck@uci.edu \
    --cc=ojeda@kernel.org \
    --cc=pgovind2@uci.edu \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=zhiyunq@cs.ucr.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox