[BUG] scsi: target: use-after-free in PR REGISTER AND MOVE racing PR OUT CLEAR
From: Yu Zhang
Date: Fri Oct 16 2026 - 02:14:07 EST
core_scsi3_emulate_pro_register_and_move() (drivers/target/target_core_pr.c)
drops dev_reservation_lock to allocate the destination registration, then
re-takes the lock and re-locates it:
spin_unlock(&dev->dev_reservation_lock);
if (core_scsi3_alloc_registration(... , 2, 1)) {
ret = TCM_INSUFFICIENT_REGISTRATION_RESOURCES;
goto out;
}
spin_lock(&dev->dev_reservation_lock);
dest_pr_reg = __core_scsi3_locate_pr_reg(dev, dest_node_acl, iport_ptr);
new_reg = 1; /* result used unchecked */
...
__core_scsi3_complete_pro_release(dev, pr_res_nacl,
dev->dev_pr_res_holder, 0, 0);
dev->dev_pr_res_holder = dest_pr_reg;
dest_pr_reg->pr_res_holder = 1;
While the lock is dropped, a PR OUT command from another I_T nexus - most
easily CLEAR, which frees all registrations - runs
__core_scsi3_free_registration() and clears dev->dev_pr_res_holder. The move
path then uses the freed / NULL registration.
On a stock tree this is a NULL dereference (dest_pr_reg / dev_pr_res_holder
are NULL):
BUG: KASAN: null-ptr-deref in __core_scsi3_complete_pro_release+0x9d/0x3b0
Read of size 1 at addr 0000000000000228
Call Trace:
__core_scsi3_complete_pro_release+0x9d/0x3b0
? __core_scsi3_locate_pr_reg+0x13f/0x170
core_scsi3_emulate_pro_register_and_move+0xa95/...
target_scsi3_emulate_pr_out+0x3a4/...
__target_execute_cmd+0x56/0x140
A NULL check after the re-locate is not sufficient; a use-after-free of the
destination registration then shows up - allocated by one REGISTER AND MOVE,
freed by the concurrent CLEAR, still used by the move path (three tasks):
BUG: KASAN: slab-use-after-free in target_scsi3_emulate_pr_out+0xaf5/...
Read of size 8 at addr ffff8880058db670
Allocated by task 23:
__core_scsi3_alloc_registration+0x7c/...
core_scsi3_emulate_pro_register_and_move+0xed7/...
target_scsi3_emulate_pr_out+0x3a4/...
Freed by task 33:
__core_scsi3_free_registration+0x3d2/...
target_scsi3_emulate_pr_out+0xac0/... (PR OUT CLEAR)
Reproducer (KASAN build, panics in ~1 minute)
---------------------------------------------
- Two tcm_loop TPGs (SAS, distinct rtpi), a nexus each, both mapping one
iblock backstore -> two /dev/sdX = two I_T nexuses to the same LU.
- Thread A on nexus 1: loop { PR OUT REGISTER; RESERVE;
REGISTER AND MOVE to nexus 2 (unregistered destination) }.
- Thread B: loop { PR OUT CLEAR }.
C reproducer and full dmesg available on request.
Reported-by: Yu Zhang <yuz08559@xxxxxxxxx>