Finder16 opened a new issue, #19554:
URL: https://github.com/apache/nuttx/issues/19554
### Description / Steps to reproduce the issue
`adc_sampletime_cfg()` in `arch/arm/src/common/stm32/stm32_adc_m0_v1.c`
passes the generic ADC object, `struct adc_dev_s *dev`, directly to the
sample-time helper functions. Both helpers then cast the received pointer back
to `struct stm32_dev_s *`.
However, `g_adcdev1` and `g_adcpriv1` are separate objects. As a result, the
sample-time values are written into the wrong object, and the register base
address is also read from the wrong object.
## Problematic Code
Lines 1461–1462 of `stm32_adc_m0_v1.c`, in the branch enabled by
`CONFIG_STM32_ADC_CHANGE_SAMPLETIME`, contain the following calls:
```c
adc_sampletime_set((struct stm32_adc_dev_s *)dev, &time_samples);
adc_sampletime_write((struct stm32_adc_dev_s *)dev);
```
The two called functions cast the argument back to the private type at lines
2754 and 2791:
```c
static void adc_sampletime_set(struct stm32_adc_dev_s *dev,
struct adc_sample_time_s *time_samples)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
priv->sample_rate[0] = time_samples->smp1;
...
}
```
The `#else` branch of the same function, at line 1464, already retrieves the
private object correctly through `dev->ad_priv`:
```c
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
```
## Field-to-Field Mapping
The following table shows which locations in `struct adc_dev_s` are accessed
when fields of `struct stm32_dev_s` are referenced through the incorrect
pointer:
| `struct stm32_dev_s` field | Offset | Corresponding location in `struct
adc_dev_s` |
|---|---:|---|
| `sample_rate[0..1]` | 20–21 | `ad_lock` |
| `smpsel` | 24 | `ad_lock` |
| `base` read | 32 | `ad_recvsem` |
Therefore, the sample-time values overwrite synchronization-related fields
in the generic ADC object, while part of `ad_recvsem` is interpreted as the
peripheral register base address.
## Reproduction Procedure
1. Configure the in-tree ADC DMA configuration:
```sh
./tools/configure.sh -l nucleo-g0b1re:adc_dma
```
2. Build NuttX:
```sh
make -j8
```
3. Execute the generated ELF's Cortex-M0 code under Unicorn, starting from
`adc_sampletime_cfg()`.
The observed results are shown below.
```text
g_adcdev1 = 0x2000011c
g_adcpriv1 = 0x200000e0
[Unmodified upstream code]
Modified offsets in g_adcdev1: [20, 21]
Modified offsets in g_adcpriv1: []
Base read from g_adcdev1+32: 0x00000000
Attempted register write: 0x00000014
[Synchronization fields initialized with marker values]
Modified offsets in g_adcdev1: [20, 21, 24, 25, 26, 27]
Modified offsets in g_adcpriv1: []
Base read from g_adcdev1+32: 0xb6b6b6b6
Attempted register write: 0xb6b6b6ca
[Control: adc_sampletime_cfg(g_adcpriv1)]
Modified offsets in g_adcdev1: []
Modified offsets in g_adcpriv1: [20, 21, 24, 25, 26, 27]
Base read from g_adcpriv1+32: 0x40012400
Attempted register write: 0x40012414
```
The expected destination is:
```text
STM32_ADC1_BASE + STM32_ADC_SMPR_OFFSET
= 0x40012400 + 0x14
= 0x40012414
```
Instead, the unmodified code attempts to write to `0x00000014`.
The second execution initializes the synchronization fields with marker
values to demonstrate the origin of the corrupted base address explicitly. The
third execution is a control run in which the correct private pointer is passed.
## Comparison with Sibling Drivers
At the equivalent call site, other STM32-family ADC drivers pass
`dev->ad_priv` rather than the generic ADC object:
- `stm32_adc_m3m4_v1v2.c:2571`
- `stm32f7/stm32_adc.c:1591`
- `at32/at32_adc.c:2112`
Only the M0 variant passes the generic pointer, which appears to deviate
from the intended driver pattern.
## Impact
In builds where `CONFIG_STM32_ADC_CHANGE_SAMPLETIME=y`, the issue is
triggered when the ADC device is opened for the first time through the
following call path:
```text
adc_setup()
-> adc_configure()
-> adc_sampletime_cfg()
-> adc_sampletime_set()
-> adc_sampletime_write()
-> adc_putreg()
```
Among the in-tree configurations, `nucleo-g0b1re:adc_dma` enables this
option.
The issue corrupts the mutex or synchronization state stored in the generic
ADC object and causes an MMIO write to an address derived from unrelated
synchronization-object contents.
## Proposed Fix
The private object can be retrieved before invoking the helper functions:
```c
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
adc_sampletime_set((struct stm32_adc_dev_s *)priv, &time_samples);
adc_sampletime_write((struct stm32_adc_dev_s *)priv);
```
Alternatively, the helper function signatures could be changed to accept
`struct stm32_dev_s *` directly, eliminating the intermediate casts entirely.
## Additional Context
I initially reported this issue to `[email protected]`.
ASF Security confirmed the defect in the following `master` revision:
```text
e35129b98d79885f31c6bcbaac5cfe3142377e3f
```
They also confirmed that the sibling drivers pass `dev->ad_priv` at the
corresponding call sites. However, because the issue is considered a functional
defect rather than a vulnerability under the NuttX security model, they advised
reporting it through the normal public channel.
### On which OS does this issue occur?
[OS: Linux]
### What is the version of your OS?
Ubuntu 24.04.3 LTS
### NuttX Version
master
### Issue Architecture
[Arch: all]
### Issue Area
[Area: Drivers]
### Host information
_No response_
### Verification
- [x] I have verified before submitting the report.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]