mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jakub Kicinski <kuba@kernel.org>
To: jie.wang@intel.com
Cc: Jakub Kicinski <kuba@kernel.org>,
	gregkh@linuxfoundation.org, 3chas3@gmail.com,
	linux-usb@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	linux-atm-general@lists.sourceforge.net,
	accessrunner-general@lists.sourceforge.net,
	syzbot+9b195c4f412ea5c4e56a@syzkaller.appspotmail.com
Subject: Re: [PATCH v3] usb: atm: cxacru: fix NULL deref of atm_dev on sysfs writes
Date: Fri, 25 Sep 2026 19:35:17 -0700	[thread overview]
Message-ID: <20260926023517.1568681-1-kuba@kernel.org> (raw)
In-Reply-To: <20260922092953.4048507-1-jie.wang@intel.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
usb: atm: cxacru: fix NULL deref of atm_dev on sysfs writes

This commit changes the adsl_state and adsl_config sysfs store handlers
to log with usb_err()/usb_info() instead of atm_err()/atm_info(). A
write that arrives before usbatm_atm_init() has set atm_dev therefore no
longer dereferences a NULL atm_dev. It also rejects poll/start requests
with -ENODEV while atm_dev is NULL, before poll_state is advanced.

Closes: https://syzkaller.appspot.com/bug?extid=9b195c4f412ea5c4e56a

> diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
> index 636f7886fc260..501a51b27e466 100644
> --- a/drivers/usb/atm/cxacru.c
> +++ b/drivers/usb/atm/cxacru.c

[ ... ]

> @@ -396,6 +396,15 @@ static ssize_t adsl_state_store(struct device *dev,
>  		poll = -1;
>  	}
>  
> +	/* cxacru_poll_status() below dereferences atm_dev, which may not be
> +	 * set up yet; reject before poll_state is advanced so that
> +	 * cxacru_atm_start() can still start polling once it is.
> +	 */
> +	if (poll == CXPOLL_POLLING && !instance->usbatm->atm_dev) {
> +		ret = -ENODEV;
> +		poll = -1;
> +	}

[Severity: High]

Is this check enough to keep cxacru_poll_status() away from an invalid
atm_dev? The read is not locked, and nothing keeps atm_dev valid after
the check passes.

usbatm_atm_init() publishes atm_dev before atm_start() runs. If
atm_start() fails, it clears atm_dev and frees it:

drivers/usb/atm/usbatm.c:usbatm_atm_init() {
    ...
	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 */
	return ret;
}

The device controls whether cxacru_atm_start() fails on the MAC address
read. That failure returns before adsl_state_serialize is taken and
before poll_state is looked at:

drivers/usb/atm/cxacru.c:cxacru_atm_start() {
    ...
	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;
	}
    ...
}

A "poll" write sends no CM request before this check. If one arrives
while CARD_GET_MAC_ADDRESS is in flight, it sees a non-NULL atm_dev,
moves poll_state from CXPOLL_STOPPED to CXPOLL_POLLING, and calls
cxacru_poll_status() directly. "start" and "restart" can do the same.

cxacru_poll_status() then goes one of two ways.

(a) It saves the still-valid pointer in a local:

	struct atm_dev *atm_dev = usbatm->atm_dev;

It then blocks on cm_serialize behind the MAC request in
cxacru_cm_get_array(). By the time it writes through
atm_dev_signal_change() or atm_dev->link_rate, atm_dev_deregister() has
freed the atm_dev.

(b) It loads NULL and dereferences it in atm_dev_signal_change(), or in
atm_warn()/atm_info(), which expand to usbatm->atm_dev->number.

cxacru_atm_start() never resets poll_state on that failure, and the
heavy-init thread exits with the interface still bound. So poll_work
keeps rescheduling itself:

	if (keep_polling)
		schedule_delayed_work(&instance->poll_work,
				round_jiffies_relative(POLL_INTERVAL*HZ));

After that, every poll error or line status change dereferences the
NULL atm_dev, with no further writes needed.

The v2 notes dropped an earlier atm_dev gate as "an unsynchronized
TOCTOU". Does this gate have the same problem? Would closing it need
real synchronization, for example gating on heavy init completion, or
serializing the atm_start() failure path with the sysfs writers?

> +
>  	if (poll == CXPOLL_POLLING) {
>  		mutex_lock(&instance->poll_state_serialize);
>  		switch (instance->poll_state) {

[Severity: Medium]

This is a pre-existing issue, not one introduced by this patch.
mac_address_show() uses the same unlocked check and then reads the
pointer again:

	if (instance == NULL || instance->usbatm->atm_dev == NULL)
		return -ENODEV;

	return sprintf(buf, "%pM\n", instance->usbatm->atm_dev->esi);

Suppose the usbatm_atm_init() failure path clears and frees atm_dev
after the reader loads the pointer but before vsprintf copies the six
bytes. Can this hand freed slab memory to userspace? The attribute has
no CAP_NET_ADMIN check.

A NULL atm_dev here only gives a small pointer, which vsprintf prints as
"(efault)". So the concern is the use-after-free read, not an oops. The
window has no sleeping point, so it is narrow.

[ ... ]

> @@ -481,7 +490,7 @@ static ssize_t adsl_config_store(struct device *dev,
>  			ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
>  				(u8 *) data, 4 + num * 8, NULL, 0);

[Severity: Medium]

This is also a pre-existing issue, not one introduced by this patch.
Since the dev_groups conversion in e605c30977bb, adsl_config_store() and
adsl_state_store() can send CM requests (CARD_DATA_SET,
CHIP_ADSL_LINE_STOP, CHIP_ADSL_LINE_START) through cxacru_cm() while the
heavy-init thread is still running cxacru_upload_firmware().

At that point the chip may still be in boot ROM or part way through the
FW_WRITE_MEM sequence. cxacru_upload_firmware() later clears halt on the
command endpoints, and a URB submitted from sysfs may still be queued
there:

drivers/usb/atm/cxacru.c:cxacru_upload_firmware() {
    ...
	usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, CXACRU_EP_CMD));
	usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, CXACRU_EP_CMD));
    ...
}

Is a CARD_DATA_SET config accepted before the firmware boots silently
dropped once the new firmware starts? Should these writers also wait
for heavy init to finish?

>  			if (ret < 0) {
> -				atm_err(instance->usbatm,
> +				usb_err(instance->usbatm,
>  					"set card data returned %d\n", ret);
>  				return -EIO;
>  			}

[Severity: Medium]

This is another pre-existing issue, not one introduced by this patch,
but it relates to the adsl_config format. adsl_config_store() parses
both fields as hex:

	ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp);

cxacru.rst also documents the format as hexadecimal. The converter that
cxacru.rst recommends, cxacru-cf.py in
Documentation/networking/device_drivers/atm/, prints the value in
decimal:

	sys.stdout.write("{0:x}={1}".format(i, struct.unpack("<I", buf)[0]))

A value of 10 comes out as "0=10", and the driver reads that as 0x10.
Should the script use {1:x} for the value?

[ ... ]
-- 
pw-bot: cr

      reply	other threads:[~2026-09-26  2:35 UTC|newest]

Thread overview: 6+ 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
2026-09-08 15:02 ` [PATCH v2] usb: atm: cxacru: fix NULL deref of atm_dev on sysfs writes Jie Wang
2026-09-10 12:47   ` Greg Kroah-Hartman
2026-09-22  9:29     ` [PATCH v3] " Jie Wang
2026-09-26  2:35       ` Jakub Kicinski [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=20260926023517.1568681-1-kuba@kernel.org \
    --to=kuba@kernel.org \
    --cc=3chas3@gmail.com \
    --cc=accessrunner-general@lists.sourceforge.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=jie.wang@intel.com \
    --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+9b195c4f412ea5c4e56a@syzkaller.appspotmail.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

all inboxes | Powered by JetHome®