[PATCH 1/1] scsi: aacraid: Prevent stopping of already finished thread on adapter reset

From: Maksim Kiselev
Date: Fri May 12 2023 - 08:52:02 EST


For some reason `aac_adapter_check_health` may fail what leads to
`aac_command_thread` finish. After that if `scsi_try_host_reset` happens,
it leads to access to 'aac->thread->pid' and attempt to stop already
finished thread (`kthread_stop(aac->thread`). As a result we have
use-after-free warning and NULL pointer dereference crash.

This patch should prevent access to already finished thread.

[ 461.707724] ------------[ cut here ]------------
[ 461.707725] refcount_t: addition on 0; use-after-free.
[ 461.707729] WARNING: CPU: 1 PID: 116 at lib/refcount.c:25 refcount_warn_saturate+0x72/0x100
[ 461.707783] CPU: 1 PID: 116 Comm: scsi_eh_0 Tainted: G W 6.3.0.0-g2cd293ea6f73-dirty #12
[ 461.707785] Hardware name: Supermicro Super Server/X11SSH-LN4F, BIOS 2.7 12/07/2021
[ 461.707786] RIP: 0010:refcount_warn_saturate+0x72/0x100
[ 461.707804] Call Trace:
[ 461.707805] <TASK>
[ 461.707806] kthread_stop+0xdb/0xe0
[ 461.707809] aac_reset_adapter+0x409/0x6f0 [aacraid]
[ 461.707815] aac_eh_host_reset+0x50/0x90 [aacraid]
[ 461.707819] scsi_try_host_reset+0x26/0xc0 [scsi_mod]
[ 461.707825] scsi_eh_ready_devs+0x34e/0x920 [scsi_mod]
[ 461.707831] ? scsi_eh_get_sense+0x160/0x160 [scsi_mod]
[ 461.707838] scsi_error_handler+0x355/0x360 [scsi_mod]
[ 461.707845] kthread+0xda/0x100
[ 461.707847] ? kthread_complete_and_exit+0x20/0x20
[ 461.707849] ret_from_fork+0x1f/0x30
[ 461.707852] </TASK>
[ 461.707852] ---[ end trace 0000000000000000 ]---

[ 461.707855] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 461.708215] #PF: supervisor write access in kernel mode
[ 461.708552] #PF: error_code(0x0002) - not-present page
[ 461.708900] PGD 0 P4D 0
[ 461.709234] Oops: 0002 [#1] PREEMPT SMP
[ 461.709558] CPU: 1 PID: 116 Comm: scsi_eh_0 Tainted: G W 6.3.0.0-g2cd293ea6f73-dirty #12
[ 461.710521] RIP: 0010:kthread_stop+0x3f/0xe0
[ 461.715884] Call Trace:
[ 461.716267] <TASK>
[ 461.716651] aac_reset_adapter+0x409/0x6f0 [aacraid]
[ 461.717052] aac_eh_host_reset+0x50/0x90 [aacraid]
[ 461.717455] scsi_try_host_reset+0x26/0xc0 [scsi_mod]
[ 461.717856] scsi_eh_ready_devs+0x34e/0x920 [scsi_mod]
[ 461.718262] ? scsi_eh_get_sense+0x160/0x160 [scsi_mod]
[ 461.718656] scsi_error_handler+0x355/0x360 [scsi_mod]
[ 461.719044] kthread+0xda/0x100
[ 461.719429] ? kthread_complete_and_exit+0x20/0x20
[ 461.719823] ret_from_fork+0x1f/0x30
[ 461.720216] </TASK>
[ 461.724589] CR2: 0000000000000000
[ 461.725038] ---[ end trace 0000000000000000 ]---

[ 461.732223] note: scsi_eh_0[116] exited with irqs disabled

Signed-off-by: Maksim Kiselev <bigunclemax@xxxxxxxxx>
---
drivers/scsi/aacraid/commsup.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index deb32c9f4b3e..9f253a360e9f 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1494,9 +1494,10 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced, u8 reset_type)
* commands are completing in the interrupt service.
*/
aac_adapter_disable_int(aac);
- if (aac->thread && aac->thread->pid != current->pid) {
+ if (!aac->aif_thread || (aac->thread && aac->thread->pid != current->pid)) {
spin_unlock_irq(host->host_lock);
- kthread_stop(aac->thread);
+ if (aac->aif_thread)
+ kthread_stop(aac->thread);
aac->thread = NULL;
jafo = 1;
}
--
2.39.2