All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dave Penkler <dpenkler@gmail.com>
To: Pavitra Jha <jhapavitra98@gmail.com>
Cc: gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH] gpib: fix use-after-free in iboffline() detach path
Date: Mon, 29 Jun 2026 18:10:46 +0200	[thread overview]
Message-ID: <akKZBpW135YW2Def@egonzo> (raw)
In-Reply-To: <20260622111437.852082-1-jhapavitra98@gmail.com>

On Mon, Jun 22, 2026 at 07:14:37AM -0400, Pavitra Jha wrote:
> iboffline() calls board->interface->detach() without holding
> board->big_gpib_mutex. Every userspace-reachable I/O path (read,
> write, command, and all ioctls that call them) acquires this mutex
> before entering the driver callbacks. The mutex therefore creates an
> apparent serialization guarantee that does not extend to the detach
> teardown path.
> 
> The race window is wide and practically exploitable. Board driver
> read callbacks (cb7210, ines_gpib, tnt4882) all delegate to
> nec7210_read() -> pio_read(), which blocks in
> wait_event_interruptible() for up to board->usec_timeout microseconds
> (default 3,000,000 us = 3 seconds) while holding a cached pointer to
> board->private_data on the stack:
> 
>   Thread A (I/O path, holds big_gpib_mutex):
>     cb7210_read()
>       priv = board->private_data        <- cached on stack
>       nec7210_read(board, priv, ...)
>         pio_read()
>           wait_event_interruptible(board->wait, ..., usec_timeout)
>           /* BLOCKS HERE UP TO 3 SECONDS */
>           priv->state ...               <- UAF if detach fires
> 
>   Thread B (detach path, no mutex):
>     gpib_unregister_driver()
>       iboffline()
>         board->interface->detach()
>           kfree(board->private_data)    <- frees what A still holds
> 
> The bug is in the core framework (iboffline() in iblib.c), not in any
> individual board driver. The three affected drivers (cb7210, ines_gpib,
> tnt4882) are all vulnerable by the same mechanism because they share
> the nec7210_read()/pio_read() path.
> 
> The iboffline() comment already flagged this gap:
> 
> 'XXX need to make sure board is generally not in use (grab board lock?)'
> 
> Fix by acquiring board->big_gpib_mutex before calling detach() and
> releasing it afterward, serializing teardown against in-flight I/O.
> mutex_lock() (non-interruptible) is used rather than
> mutex_lock_interruptible() because iboffline() is called from module
> unload context where signal delivery is not meaningful.
> 
> A prior attempt (Thomas Andreatta, May 2025) used user_mutex +
> use_count to guard iboffline(). That approach was NAK'd: user_mutex
> is not held consistently across ibopen()/ibclose(), making it racy
> (Dan Carpenter), and use_count is never zero for an initialized board
> so the check would always return -EBUSY, preventing any offline
> transition (Dave Penkler). This patch instead serializes on
> big_gpib_mutex, which is exactly the lock the ioctl dispatch path
> uses and is therefore the correct exclusion boundary.
> 
> KASAN report (kernel 7.1.0+, QEMU/x86_64, KASLR disabled,
> reproducer: kprobe on read callback + concurrent kfree from detach
> kthread):
> 
>   gpib_common: GPIB core driver
>   gpib_race_harness: loading out-of-tree module taints kernel.
>   gpib_race: attach priv=ffff88800331e7e8 canary=0xdeadbeef
>   gpib_race: kprobe on dummy_read installed
>   gpib_race: detach_fn waiting for read_entered
>   gpib_race: reader_fn calling dummy_read
>   gpib_race: kprobe fired -- dummy_read entered
>   gpib_race: dummy_read priv=ffff88800331e7e8 canary=0xdeadbeef
>   gpib_race: detach_fn firing detach on fake_board
>   gpib_race: detach kfree priv=ffff88800331e7e8
>   gpib_race: detach_fn done -- priv is now freed
>   ==================================================================
>   BUG: KASAN: slab-use-after-free in dummy_read+0xb0/0x120 [gpib_race_harness]
>   Read of size 4 at addr ffff88800331e7e8 by task gpib_reader/25
> 
>   CPU: 0 UID: 0 PID: 25 Comm: gpib_reader Tainted: G           O        7.1.0+ #26 PREEMPTLAZY
>   Tainted: [O]=OOT_MODULE
>   Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
>   Call Trace:
>    <TASK>
>    dump_stack_lvl+0x2b/0x40
>    print_report+0x14f/0x4d0
>    ? timer_delete_sync+0x68/0x90
>    ? dummy_disable_eos+0x10/0x10 [gpib_race_harness]
>    kasan_report+0xd4/0x100
>    ? dummy_read+0xb0/0x120 [gpib_race_harness]
>    ? dummy_read+0xb0/0x120 [gpib_race_harness]
>    dummy_read+0xb0/0x120 [gpib_race_harness]
>    reader_fn+0xbf/0xf0 [gpib_race_harness]
>    ? dummy_disable_eos+0x10/0x10 [gpib_race_harness]
>    ? __kthread_parkme+0x56/0x1a0
>    kthread+0x32a/0x470
>    ? kthread_affine_node+0x280/0x280
>    ret_from_fork+0x32d/0x5a0
>    ? exit_thread+0x70/0x70
>    ? __switch_to+0x83f/0xc30
>    ? kthread_affine_node+0x280/0x280
>    ret_from_fork_asm+0x11/0x20
>    </TASK>
> 
>   Allocated by task 23:
>    kasan_save_stack+0x2c/0x50
>    kasan_save_track+0x10/0x30
>    __kasan_kmalloc+0x77/0x90
>    dummy_attach+0x39/0x90 [gpib_race_harness]
>    0xffffffffa000d073
>    do_one_initcall+0xb0/0x230
>    do_init_module+0x263/0x810
>    load_module+0x3e12/0x51e0
>    init_module_from_file+0x136/0x150
>    __x64_sys_finit_module+0x39f/0x7a0
>    do_syscall_64+0x56/0x3f0
>    entry_SYSCALL_64_after_hwframe+0x4b/0x53
> 
>   Freed by task 24:
>    kasan_save_stack+0x2c/0x50
>    kasan_save_track+0x10/0x30
>    kasan_save_free_info+0x37/0x50
>    __kasan_slab_free+0x3f/0x60
>    kfree+0xf1/0x390
>    detach_fn+0x105/0x130 [gpib_race_harness]
>    kthread+0x32a/0x470
>    ret_from_fork+0x32d/0x5a0
>    ret_from_fork_asm+0x11/0x20
> 
>   The buggy address belongs to the object at ffff88800331e7e8
>    which belongs to the cache kmalloc-8 of size 8
>   The buggy address is located 0 bytes inside of
>    freed 8-byte region [ffff88800331e7e8, ffff88800331e7f0)
> 
>   Memory state around the buggy address:
>    ffff88800331e680: fc fc fc fc fc fc fc fc fc 00 fc fc fc fc fc fc
>    ffff88800331e700: fc fc fc fc fc fc fc fc fc fc fc 00 fc fc fc fc
>   >ffff88800331e780: fc fc fc fc fc fc fc fc fc fc fc fc fc fa fc fc
>                                                             ^
>    ffff88800331e800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>    ffff88800331e880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>   ==================================================================
>   gpib_race: UAF dereference priv=ffff88800331e7e8 canary=0xdeadbeef
>   gpib_race: reader_fn returned
> 
> Note: CVE-2026-31769 (Adam Crosser) fixed a separate UAF between
> IBRD/IBWRT/IBCMD/IBWAIT ioctl handlers and concurrent IBCLOSEDEV
> via descriptor refcounting. This patch addresses an independent race
> between the I/O callback path and iboffline()/detach() teardown,
> which is not covered by that fix.
> 
> Fixes: e6ab504633e4 ("staging: gpib: Destage gpib")
> Cc: stable@vger.kernel.org
> Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
> ---
>  drivers/gpib/common/iblib.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c
> index b672dd6aa..07a30d520 100644
> --- a/drivers/gpib/common/iblib.c
> +++ b/drivers/gpib/common/iblib.c
> @@ -256,9 +256,23 @@ int iboffline(struct gpib_board *board)
>  		board->autospoll_task = NULL;
>  	}
>  
> +	/*
> +	 * Acquire big_gpib_mutex before calling detach() to prevent a
> +	 * use-after-free race. I/O callbacks (read/write/command) hold
> +	 * big_gpib_mutex while caching board->private_data on their stack.
> +	 * Without this lock, iboffline() can kfree(board->private_data)
> +	 * inside detach() while an I/O callback is still running and holds
> +	 * a stale pointer to the freed memory.
> +	 *
> +	 * Affected board drivers: cb7210, ines_gpib, tnt4882 (all delegate
> +	 * to nec7210_read/pio_read which blocks in wait_event_interruptible
> +	 * for up to board->usec_timeout microseconds while holding priv).
> +	 */
> +	mutex_lock(&board->big_gpib_mutex);

Unfortunately this will not work. The read/write and command ioctls
release this lock before calling the drivers because these ioctls can
take a long time. Also the IBONL ioctl for moving the board offline
would hang on the lock as it has already been taken before the call to
online_ioctl(). The board lock is a separate lock that is controllable
from user space with the IBMUTEX ioctl.

>  	board->interface->detach(board);
>  	gpib_deallocate_board(board);
>  	board->online = 0;
> +	mutex_unlock(&board->big_gpib_mutex);
>  	dev_dbg(board->gpib_dev, "board offline\n");
>  
>  	return 0;
> -- 
> 2.53.0
> 

  reply	other threads:[~2026-06-29 16:10 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-22 11:14 [PATCH] gpib: fix use-after-free in iboffline() detach path Pavitra Jha
2026-06-29 16:10 ` Dave Penkler [this message]
2026-07-08  7:36   ` [PATCH v2] gpib: fix use-after-free between iboffline() detach and in-flight I/O Pavitra Jha

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=akKZBpW135YW2Def@egonzo \
    --to=dpenkler@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jhapavitra98@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.