Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Kurt Borja" <kuurtb@gmail.com>
Cc: robh@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH 2/5] iio: adc: Add ti-ads1262 driver
Date: Fri, 12 Jun 2026 23:01:32 +0000	[thread overview]
Message-ID: <20260612230132.E04DB1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260612-ads126x-v1-2-894c788d03ed@gmail.com>

Thank you for your contribution! Sashiko AI review found 15 potential issue(s) to consider:
- [High] Architecture Compatibility / Undefined Behavior due to direct bitfield mapping.
- [High] Out-of-Bounds Memory Access / Hardware Misconfiguration during active channel iteration.
- [High] Infinite Loop / Denial of Service in IDAC microamp parsing.
- [High] Logic Error: Inverted bounds check rejects valid pin numbers.
- [High] Race Condition / Lost Wakeup due to reinitializing completion after hardware trigger.
- [High] Missing Error Handling / Type Mismatch: Unsigned check of negative error code.
- [High] DMA Memory Corruption: Unaligned DMA buffer in union.
- [High] Uninitialized Memory Read (KMSAN) on stack variable.
- [High] Illegal DMA from Stack in regmap bus implementation.
- [High] Logic Error: GPIO get_multiple returns pin states incorrectly.
- [High] Hardware State / Timing violation during device reset.
- [High] Logic Error / Performance: Unconditional stall without IRQ.
- [Medium] Memory Leak of fwnode_handle reference on the error path.
- [Medium] Resource Leak of runtime PM usage counter on the error path.
- [Medium] API Contract Violation: Buffer mode lock released with direct mode API.
--

commit bf3bd63a7b09cf7986792e14a3a80853f12e425d
Author: Kurt Borja <kuurtb@gmail.com>

    iio: adc: Add ti-ads1262 driver
    
    Add ti-ads1262 driver for TI ADS1262 and ADS1263 ADCs with initial
    support for various features including IIO modes and channel configuration.

> diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
> new file mode 100644
> index 0000000000000..fd1911cf65ace
> --- /dev/null
> +++ b/drivers/iio/adc/ti-ads1262.c
> @@ -0,0 +1,1816 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
[ ... ]
> +struct ads1262_channel {
> +	/* MODE0 */
> +	u8 conv_delay:4;
> +	u8 chop_mode:1;
> +	u8 idac_rot_mode:1;
> +	u8 runmode:1;
> +	u8 rev_vref_pol:1;
[ ... ]
> +static int ads1262_channel_enable_and_read(struct ads1262 *st,
> +					   struct ads1262_channel *chan,
> +					   __be32 *val)
> +{
[ ... ]
> +	mutex_lock(&st->chan_lock);
> +	memcpy(&st->tx[2], chan, sizeof(*chan));
> +	mutex_unlock(&st->chan_lock);

[Severity: High]
Does this code map a C bitfield directly to the SPI TX buffer? 

C bitfield layout is implementation-defined, so this can send garbage
configuration bits to the hardware on big-endian architectures.

> +struct ads1262 {
[ ... ]
> +	u8 tx[6] __aligned(IIO_DMA_MINALIGN);
> +	union {
> +		u8 rx[6];
> +		struct {
> +			__be32 data;
> +		} __packed shift_reg;
> +		struct {
> +			u8 dummy;
> +			__be32 data;
> +		} __packed holding_reg;
> +	};
> +};

[Severity: High]
Can this union cause DMA memory corruption?

Since the anonymous union lacks an __aligned(IIO_DMA_MINALIGN) attribute,
the RX buffers can share a cache line with surrounding struct members,
leading to memory corruption during DMA on non-cache-coherent architectures.

> +static int ads1262_dev_reset(struct ads1262 *st)
> +{
> +	int ret;
> +
> +	if (st->reset_gpiod)
> +		ret = gpiod_set_value_cansleep(st->reset_gpiod, 1);
> +	else
> +		ret = ads1262_dev_cmd(st, ADS1262_OPCODE_RESET);
> +	if (ret)
> +		return ret;
> +
> +	fsleep(1);

[Severity: High]
Is the 1 microsecond delay sufficient here? 

This short delay might violate hardware timing constraints for the device
reset, causing subsequent register writes to be ignored.

> +static int ads1262_read_calib(struct ads1262 *st, unsigned int reg, u32 *val)
> +{
> +	__le32 lval;
> +	int ret;
> +
> +	/*
> +	 * The calibration word is a signed 24 bit LSB-first value.
> +	 */
> +	ret = regmap_bulk_read(st->regmap, reg, &lval, 3);
> +	if (ret)
> +		return ret;
> +	*val = sign_extend32(le32_to_cpu(lval), 23);

[Severity: High]
Does this code evaluate an uninitialized byte?

The lval variable is 4 bytes long, but regmap_bulk_read only writes
3 bytes to it. The subsequent call to le32_to_cpu reads the
uninitialized 4th byte from the stack, which can trigger KMSAN warnings
and result in an incorrect sign extension.

> +static int ads1262_write_calib(struct ads1262 *st, unsigned int reg, u32 val)
> +{
> +	__le32 lval = cpu_to_le32(val);
> +
> +	/*
> +	 * The calibration word is a signed 24 bit LSB-first value.
> +	 */
> +	return regmap_bulk_write(st->regmap, reg, &lval, 3);
> +}

[Severity: High]
Can this result in an illegal DMA from the stack?

Calling regmap_bulk_write passes the lval stack variable to the
underlying SPI controller. This violates VMAP_STACK constraints and can
corrupt memory if the SPI controller uses DMA.

> +static void ads1262_wait_for_conversion(struct ads1262 *st)
> +{
> +	reinit_completion(&st->drdy);
> +
> +	/*
> +	 * The first conversion latency is affected by the channel's data rate,
[ ... ]
> +	wait_for_completion_timeout(&st->drdy, msecs_to_jiffies(6436));
> +}

[Severity: High]
Can wait_for_completion_timeout stall unconditionally if no IRQ is present?

Without a polling fallback, raw reads might block for the entire 6.4 second
timeout if the device isn't wired to an interrupt.

> +static int ads1262_channel_hot_reload(struct ads1262 *st,
> +				      const struct iio_chan_spec *chan)
> +{
[ ... ]
> +	weight = bitmap_weight(st->indio_dev->active_scan_mask,
> +			       iio_get_masklength(st->indio_dev));
> +	if (weight != 1) {
> +		iio_device_release_direct(st->indio_dev);
> +		return 0;
> +	}

[Severity: Medium]
Does this code release the wrong type of lock?

The iio_device_try_claim_buffer_mode API was used earlier to acquire
the buffer mode lock, but this path releases it using
iio_device_release_direct, violating the IIO locking contract.

> +static int ads1262_channel_read(struct ads1262 *st,
> +				struct ads1262_channel *chan_data,
> +				__be32 *val)
> +{
[ ... ]
> +	ret = ads1262_dev_start_one(st, runmode);
> +	if (ret)
> +		return ret;
> +
> +	ads1262_wait_for_conversion(st);

[Severity: High]
Is there a race condition with the completion here?

By calling ads1262_dev_start_one before ads1262_wait_for_conversion
(which re-initializes the completion), the hardware might fire the DRDY
interrupt before the completion is re-initialized, leading to a lost
wakeup.

> +static int ads1262_parse_idac_pins(struct ads1262 *st, u32 *pins,
> +				   unsigned int num_pins)
> +{
[ ... ]
> +		ret = device_property_read_u32(dev, propname, &val);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "Failed to read property %s\n",
> +					     propname);
> +		if (val < ADS1262_IDACMUX_COUNT)
> +			return dev_err_probe(dev, -EINVAL,
> +					     "%s: Pin number out of range %d\n",
> +					     propname, val);

[Severity: High]
Is the bounds check logic inverted here?

Checking if val < ADS1262_IDACMUX_COUNT causes valid pin numbers to be
rejected with an error instead of catching out-of-range ones.

> +static int ads1262_parse_idac_mags(struct ads1262 *st, u32 *mags,
> +				   unsigned int num_mags)
> +{
[ ... ]
> +		for (mode = 0; mode < ARRAY_SIZE(idac_microamps); i++) {
> +			if (val == idac_microamps[mode])
> +				break;
> +		}

[Severity: High]
Can this create an infinite loop?

The loop iterates the mode variable but increments i (the outer loop's
induction variable), resulting in a lockup during probe.

> +static int ads1262_regulator_setup(struct ads1262 *st)
> +{
[ ... ]
> +	reg_id = "vref";
> +	st->vref_uV = devm_regulator_get_enable_read_voltage(dev, reg_id);
> +	if (st->vref_uV < 0)
> +		goto err_regulator_get;

[Severity: High]
Will this error check ever succeed?

The return value of devm_regulator_get_enable_read_voltage is stored
in st->vref_uV, which is an unsigned 32-bit integer. The negative
error code becomes a large positive number, making st->vref_uV < 0
statically false.

> +static int ads1262_regmap_read(void *context, const void *reg_buf,
> +			       size_t reg_size, void *val_buf, size_t val_size)
> +{
> +	struct ads1262 *st = context;
> +	struct spi_transfer xfer[3] = {
[ ... ]
> +		{
> +			.rx_buf = val_buf,
> +			.len = val_size,
> +		},
> +	};
[ ... ]
> +	return spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
> +}

[Severity: High]
Does this custom regmap bus allow SPI DMA transfers directly to user-provided
buffers?

Passing val_buf directly into rx_buf might result in DMA to stack
memory if the caller passes a stack variable, violating VMAP_STACK constraints
and causing memory corruption.

> +static int ads1262_gpiochip_get_multiple(struct gpio_chip *gc,
> +					 unsigned long *mask,
> +					 unsigned long *bits)
> +{
> +	struct ads1262 *st = gpiochip_get_data(gc);
> +	unsigned int val;
> +	int ret;
> +
> +	ret = regmap_read(st->regmap, ADS1262_GPIODAT_REG, &val);
> +	if (ret)
> +		return ret;
> +
> +	return val & *mask;
> +}

[Severity: High]
Does this code return the pin states incorrectly?

The GPIO get_multiple callback is expected to write the state to the
bits bitmap pointer, but instead it returns the state via the integer
return code.

> +static int ads1262_buffer_preenable(struct iio_dev *indio_dev)
> +{
[ ... ]
> +	iio_for_each_active_channel(indio_dev, i)
> +		ads1262_channel_set_runmode(st, &st->channels[i],
> +					    ADS1262_RUNMODE_CONTINUOUS);

[ ... ]
> +static int ads1262_fill_buffer_mult(struct ads1262 *st)
> +{
[ ... ]
> +	iio_for_each_active_channel(st->indio_dev, chan) {
> +		ret = ads1262_channel_enable_and_read(st, &st->channels[chan],
> +						      &val);

[Severity: High]
Can these loops access memory out of bounds?

iio_for_each_active_channel iterates over all active channels, including
the software timestamp channel if it is enabled. This can result in an
out-of-bounds array access on st->channels[chan] and misconfiguring the
hardware.

> +static int ads1262_aux_device_setup(struct ads1262 *st)
> +{
[ ... ]
> +out_node_put:
> +	fwnode_handle_put(node);
> +
> +	return ret;
> +}

[Severity: Medium]
Does this code leak the fwnode reference on the error path?

Because node is consumed and set to NULL by no_free_ptr(node) during
device_set_node, any subsequent failures jumping to out_node_put will
call fwnode_handle_put(NULL), leaving the reference leaked.

> +static int ads1262_gpiochip_request(struct gpio_chip *gc, unsigned int offset)
> +{
> +	struct ads1262 *st = gpiochip_get_data(gc);
> +	struct device *dev = &st->spi->dev;
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret)
> +		return ret;
> +
> +	return regmap_update_bits(st->regmap, ADS1262_GPIOCON_REG,
> +				  BIT(offset), BIT(offset));
> +}

[Severity: Medium]
Does this leak the runtime PM usage counter on failure?

If regmap_update_bits fails, the function returns the error directly
without calling pm_runtime_put_autosuspend(dev) to balance the resume.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260612-ads126x-v1-0-894c788d03ed@gmail.com?part=2

  reply	other threads:[~2026-06-12 23:01 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-12 22:46 [PATCH 0/5] iio: adc: Add TI ADS126X ADC family support Kurt Borja
2026-06-12 22:46 ` [PATCH 1/5] dt-bindings: iio: adc: Add TI ADS126x ADC family Kurt Borja
2026-06-12 22:53   ` sashiko-bot
2026-06-12 22:46 ` [PATCH 2/5] iio: adc: Add ti-ads1262 driver Kurt Borja
2026-06-12 23:01   ` sashiko-bot [this message]
2026-06-12 22:46 ` [PATCH 3/5] iio: adc: ti-ads1262: Add GPIO controller support Kurt Borja
2026-06-12 22:59   ` sashiko-bot
2026-06-12 22:46 ` [PATCH 4/5] iio: adc: ti-ads1262: Add calibration support Kurt Borja
2026-06-12 23:02   ` sashiko-bot
2026-06-12 22:46 ` [PATCH 5/5] iio: adc: Add ti-ads1263-adc2 driver Kurt Borja
2026-06-12 23:11   ` sashiko-bot
2026-06-12 23:50 ` [PATCH 0/5] iio: adc: Add TI ADS126X ADC family support David Lechner
2026-06-13  0:06   ` Kurt Borja

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=20260612230132.E04DB1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=kuurtb@gmail.com \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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