Repository navigation
zephyr: sof_dma: validate syscalls against kernel-only DMA state - #11391
Merged
lgirdwood merged 1 commit intoNov 3, 2026
Merged
Conversation
kv2019i
requested review from
abonislawski,
dbaluta,
iuliana-prodan,
lbetlej,
lgirdwood,
lyakh,
mmaka1 and
plbossart
as code owners
November 2, 2026 18:02
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical DMA flaws let user-writable pointers, heap state, and locks control kernel operations, enabling forged-pointer writes, invalid frees, and SMP races.
4 open issues
What changed in this PR
This PR hardens SOF’s Zephyr DMA syscall boundary by introducing kernel-only DMA state and reducing reliance on user-writable fields.
Changes:
- Adds a DMA shadow initialized at boot for device metadata, reference counts, and channel allocation ownership.
- Validates device access, channel bounds, and transfer directions before calling Zephyr DMA APIs.
- Rejects non-HDA peripheral transfers until peripheral addresses can be validated.
| File | Description |
|---|---|
zephyr/syscall/sof_dma.c |
Uses shadow-backed validation and calls DMA APIs on validated devices. |
zephyr/lib/dma.c |
Adds shadow storage, pointer lookup, and boot-time initialization. |
zephyr/include/sof/lib/sof_dma.h |
Defines the kernel-only shadow and its lookup interface. |
src/lib/dma.c |
Moves reference counting and channel allocation ownership into the shadow. |
Review effort: Balanced
| dma->heap = heap; | ||
| memset(dma->chan, 0, sizeof(struct dma_chan_data) * dma->plat_data.channels); | ||
| #ifdef CONFIG_SOF_USERSPACE_INTERFACE_DMA | ||
| sof_dma_kinfo_get(dma)->heap = heap; |
| memset(dma->chan, 0, sizeof(struct dma_chan_data) * dma->plat_data.channels); | ||
| #ifdef CONFIG_SOF_USERSPACE_INTERFACE_DMA | ||
| sof_dma_kinfo_get(dma)->heap = heap; | ||
| sof_dma_kinfo_get(dma)->chan = dma->chan; |
| uint32_t dir; /**< SOF_DMA_DIR_* supported directions */ | ||
| uint32_t caps; /**< SOF_DMA_CAP_* capabilities */ | ||
| uint32_t channels; /**< size of the channel array */ | ||
| int sref; /**< reference count, under dma->lock */ |
| } | ||
|
|
||
| dmin->sref++; | ||
| dmin->sref = ++*dma_sref(dmin); |
PR 11391: test resultsRun date: 2026-11-03 15:34 UTC Tested commit: c038f33d5e84adadea0ee683af3dfc8a9ecd1112 |
lgirdwood
reviewed
Nov 3, 2026
| struct dma_chan_data; | ||
|
|
||
| /** | ||
| * Kernel-only shadow of one struct sof_dma entry. |
Member
There was a problem hiding this comment.
So this is the source of all truth, the userspace object is really the shadow ?
Collaborator
Author
There was a problem hiding this comment.
@lgirdwood Ack, this is misleading, fixed in V2.
kv2019i
force-pushed
the
202611-sofdma-harden-dma-checks
branch
from
November 3, 2026 14:41
fc2a1f7 to
33fd6a9
Compare
Collaborator
Author
|
V2 pushed:
|
The dma[] array is APP_SYSUSER_DATA, so user threads can write every field of it. The DMA syscalls trusted it in several ways: - the implementations re-read dma->z_dev after the validator checked it, so a forged device and its driver API could be swapped in - sof_dma_put() freed dma->chan through dma->heap, and the reference count was protected by dma->lock, all in user-writable memory - dma_init() allocated the channel array from a heap pointer read from user-writable memory, and read the array pointer back from dma->chan - channel numbers were passed to drivers that only assert on them, and asserts are compiled out in production builds - sof_dma_config() picked the address to validate from the user's channel_direction, while HDA DMA programs the buffer address that matches the device's own direction Add kernel-only state for each DMA controller, struct sof_dma_kinfo, filled in dmac_init() before user threads run. It is the authoritative state for the kernel, while dma[] stays as the untrusted user-visible view. It holds the reference count, the lock protecting it, and the channel array with its heap. With CONFIG_SOF_USERSPACE_LL, dma_init() checks the heap with zephyr_ll_user_heap_verify(), and it publishes the channel array only once it is initialized. The validators now look up the kernel state, bound the channel by the driver's kernel-only dma_context, require the transfer direction to match the device, and call the Zephyr DMA API on the validated device. Peripheral directions are refused for non-HDA DMA until FIFO addresses can be checked against a kernel-side list. Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
kv2019i
force-pushed
the
202611-sofdma-harden-dma-checks
branch
from
November 3, 2026 14:53
33fd6a9 to
c038f33
Compare
Collaborator
Author
|
V3 pushed:
|
lgirdwood
approved these changes
Nov 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


The dma[] array is APP_SYSUSER_DATA, so user threads can write every field of it. The DMA syscalls trusted it in several ways:
Add a kernel-only shadow, sof_dma_kinfo, filled in dmac_init() before user threads run, and track the reference count and channel array there. The validators now look up the shadow, bound the channel by the driver's kernel-only dma_context, require the transfer direction to match the device, and call the Zephyr DMA API on the validated device. Peripheral directions are refused for non-HDA DMA until FIFO addresses can be checked against a kernel-side list.