Linux USB
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: syzbot@kernel.org
Cc: syzkaller-bugs@googlegroups.com, jie.wang@intel.com,
	3chas3@gmail.com, accessrunner-general@lists.sourceforge.net,
	gregkh@linuxfoundation.org,
	linux-atm-general@lists.sourceforge.net,
	linux-usb@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, syzbot@lists.linux.dev,
	kuba@kernel.org
Subject: Re: usb: atm: cxacru: fix NULL pointer dereference on uninitialized atm_dev
Date: Wed, 02 Sep 2026 08:42:51 +0000	[thread overview]
Message-ID: <178833857175.3394541.4513055242648047225@kernel.org> (raw)
In-Reply-To: <f3ef0a67-79d7-47da-be6e-7e82d92e68fe@mail.kernel.org>

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:

Critical: 0 · High: 1 · Medium: 0 · Low: 0

- [High] Incomplete fix: the new `instance->usbatm->atm_dev == NULL`
  guards in adsl_state_store() (drivers/usb/atm/cxacru.c:350) and…

--

usb: atm: cxacru: fix NULL pointer dereference on uninitialized atm_dev

The commit adds a NULL test on instance->usbatm->atm_dev at the entry of
adsl_state_store() and adsl_config_store() in drivers/usb/atm/cxacru.c, so
that a sysfs write arriving before usbatm_do_heavy_init() has registered the
ATM device returns -ENODEV instead of dereferencing atm_dev through
atm_err().

Closes: https://syzkaller.appspot.com/bug?extid=9b195c4f412ea5c4e56a
Link: https://syzkaller.appspot.com/ai_job?id=b6352d63-d03a-4e87-92a4-b334c3ebcf97

> diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
> index 429ac20a8999f..dee15f7b70bf6 100644
> --- a/drivers/usb/atm/cxacru.c
> +++ b/drivers/usb/atm/cxacru.c
> @@ -347,7 +347,7 @@ static ssize_t adsl_state_store(struct device *dev,
>  		return -EINVAL;
>  	ret = 0;
>  
> -	if (instance == NULL)
> +	if (instance == NULL || instance->usbatm->atm_dev == NULL)
>  		return -ENODEV;
>  
>  	if (mutex_lock_interruptible(&instance->adsl_state_serialize))

[Severity: High]
Is this check taken early enough to still be valid at the point where
atm_dev is actually dereferenced?

The test reads instance->usbatm->atm_dev once at handler entry, but the
dereference happens later in atm_err(), which expands through atm_printk()
in drivers/usb/atm/usbatm.h:

  #define atm_printk(level, instance, format, arg...)	\
	printk(level "ATM dev %d: " format ,		\
	(instance)->atm_dev->number , ## arg)

Between the check and that dereference the handler sleeps in
mutex_lock_interruptible(&instance->adsl_state_serialize), then in
cxacru_cm() (which takes cm_serialize and performs a USB control transfer),
and on the "restart" path also in msleep(1500).

Meanwhile usbatm_atm_init() in drivers/usb/atm/usbatm.c publishes atm_dev
before calling atm_start, and clears plus frees it if atm_start fails:

  instance->atm_dev = atm_dev;
  ...
  if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
	atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
	goto fail;
  }
  ...
 fail:
	instance->atm_dev = NULL;
	atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */

So a non-NULL atm_dev does not mean initialization completed, and the only
failing leg of cxacru_atm_start() runs before it takes the mutex that
adsl_state_store() uses:

  ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,
		  atm_dev->esi, sizeof(atm_dev->esi));
  if (ret < 0) {
	atm_err(usbatm_instance, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);
	return ret;
  }

  /* start ADSL */
  mutex_lock(&instance->adsl_state_serialize);

With that ordering, can the exact oops quoted in the commit message still
happen?  A writer passes the new check while atm_dev is published but
atm_start has not finished, sleeps in cxacru_cm(), the heavy-init kthread
takes the fail path above, and the writer then evaluates:

  atm_err(instance->usbatm, "change adsl state:"
	  " CHIP_ADSL_LINE_STOP returned %d\n", ret);

which is the reported crash site.  The CHIP_ADSL_LINE_START error leg is
identical.  If the compiler had already loaded the old pointer, is this a
use-after-free instead, given atm_dev_deregister() drops the last reference?

There is a second window of the same shape in usbatm_usb_disconnect():

  if (instance->atm_dev) {
	atm_dev_deregister(instance->atm_dev);
	instance->atm_dev = NULL;
  }

Would it be more robust to drop the atm_dev dereference from these paths
altogether, for example by using usb_err()/dev_err() on
instance->usb_intf->dev, which stays valid for the duration of the sysfs
write?  Alternatively, re-validating and latching atm_dev under a lock that
usbatm_atm_init()'s failure path also takes, holding a reference on the
atm_dev, or publishing instance->atm_dev only after initialization has fully
succeeded would close the window rather than narrow it.

Related to the same window: adsl_state_store() calls cxacru_poll_status()
after dropping adsl_state_serialize, and that function caches the pointer
with no check of its own:

  struct usbatm_data *usbatm = instance->usbatm;
  struct atm_dev *atm_dev = usbatm->atm_dev;

and then uses it via atm_warn(), atm_dev->link_rate and
atm_dev_signal_change().  Can that path fault too once atm_dev has been
cleared behind the writer's back?

> @@ -444,7 +444,7 @@ static ssize_t adsl_config_store(struct device *dev,
>  	if (!capable(CAP_NET_ADMIN))
>  		return -EACCES;
>  
> -	if (instance == NULL)
> +	if (instance == NULL || instance->usbatm->atm_dev == NULL)
>  		return -ENODEV;
>  
>  	pos = 0;

[Severity: High]
The same check-then-use concern applies here, and this handler holds no
mutex at all before the sleeping cxacru_cm() transfers inside the
while (pos < len) loop.  Both the failure and the success legs dereference
atm_dev after that sleep:

  ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
		  (u8 *) data, 4 + num * 8, NULL, 0);
  if (ret < 0) {
	atm_err(instance->usbatm,
		"set card data returned %d\n", ret);
	return -EIO;
  }
  ...
  atm_info(instance->usbatm, "config%s\n", log);

Since atm_info() expands to the same (instance)->atm_dev->number, does this
mean no command failure is even required to hit the dereference here once
usbatm_atm_init() or usbatm_usb_disconnect() has cleared atm_dev?

Would it make sense to also drop the atm_err()/atm_info() uses in this
function in favour of usb_err()/usb_info() on the USB interface?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/f3ef0a67-79d7-47da-be6e-7e82d92e68fe%40mail.kernel.org

      reply	other threads:[~2026-09-02  8:42 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31 14:40 [PATCH] usb: atm: cxacru: fix NULL pointer dereference on uninitialized atm_dev syzbot
2026-09-02  8:42 ` netdev-bot+sashiko [this message]

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=178833857175.3394541.4513055242648047225@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=3chas3@gmail.com \
    --cc=accessrunner-general@lists.sourceforge.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=jie.wang@intel.com \
    --cc=kuba@kernel.org \
    --cc=linux-atm-general@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=syzbot@kernel.org \
    --cc=syzbot@lists.linux.dev \
    --cc=syzkaller-bugs@googlegroups.com \
    /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