Skip to content

Harden untrusted input rpmsg virtio - #702

Merged
arnopo merged 9 commits into
OpenAMP:mainfrom
bentheredonethat:Harden-untrusted-input-rpmsg-virtio
Oct 19, 2026
Merged

arnopo merged 9 commits into
OpenAMP:mainfrom
bentheredonethat:Harden-untrusted-input-rpmsg-virtio

Conversation

@bentheredonethat

Copy link
Copy Markdown
Contributor

No description provided.

@arnopo arnopo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one extra comment else LGTM

Comment thread lib/rpmsg/rpmsg_virtio.c Outdated
@bentheredonethat
bentheredonethat force-pushed the Harden-untrusted-input-rpmsg-virtio branch from 95f52bd to be4d485 Compare September 15, 2026 14:02

@arnopo arnopo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be too much AI verbosity in inline comment, but that’s just my feeling. If other reviewers don’t report it, we can keep it as is.

The remote peer controls every byte of a name service message name.
If the name fills the wire field without a NUL terminator, endpoint
lookup and application callbacks read past the copied name when they
treat it as a C string.

Terminate the copied name inside the name field before it is used,
as upstream Linux RPMsg does.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
When the source is smaller than the destination and contains a NUL,
safe_strcpy() includes the copied terminator in its zero-fill length.
The resulting memset() writes one byte beyond the destination buffer.

Calculate the fill length from the advanced destination pointer. This
ensures only bytes remaining inside the destination are cleared.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
virtqueue_create() takes the vring alignment from its caller without
checking it. vring_init() rounds the used ring address with a
~(align - 1) mask, so a zero alignment places the used ring at NULL.

Reject a zero alignment in the virtqueue constructor and document the
requirement. Check the ring pointer unconditionally as well, since
VQ_PARAM_CHK() compiles out unless VQ_DEBUG is enabled.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
The remote peer controls both the used-ring length and the payload
length in the rpmsg header. Trusting either value can make cache
invalidation and endpoint callbacks access beyond the receive buffer.

Cap the driver-side length to the locally allocated buffer size before
cache invalidation. Deliver a buffer to an endpoint only when the
header and the announced payload fit in the reported length, and
release the buffer through the existing path otherwise.

Move the endpoint pointer from function scope into the receive loop
body and initialize it at its declaration. The endpoint lookup is now
conditional, so a function-scope variable would retain the endpoint
resolved by a previous iteration whenever the length checks reject a
buffer, and that already unreferenced pointer would be dispatched to
and unreferenced a second time.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
Name service messages are allocated on the stack and copied in full to
the remote peer. safe_strcpy() does not clear bytes after a short name
when source and destination sizes match. This can disclose stack
contents.

Zero-initialize the message before populating its fields. Unused name
bytes then never cross the shared-memory trust boundary.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
virtqueue_create() rejects a zero vring alignment but accepts any other
value. vring_init() rounds the used ring address with a ~(align - 1)
mask, which only clears low order bits when the alignment is a power of
two. A value such as 0x80000001 makes the mask clear high order bits of
the computed address instead, placing the used ring outside the vring
memory while the local core keeps dereferencing it.

Require the alignment to be a power of two in the virtqueue constructor
and document the requirement.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
virtqueue_get_buffer() takes the descriptor index from the used ring,
which the remote processor writes, and uses it to index vq_descx[]
without any bounds check. A remote that reports an index above
vq_nentries makes the driver read a cookie from past the end of the
array and then write NULL over that same location.

vq_ring_free_chain() is reached first and only guards the index with
VQ_RING_ASSERT_VALID_IDX(), which expands to nothing unless VQ_DEBUG is
enabled, so it does not prevent the access in normal builds.

Drop buffers whose descriptor index is out of range, and bound the
descriptor chain walk so it stays inside the descriptor table when the
assert is compiled out.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-5
In the device role, the peer supplies the descriptor address and length.
An address near the end of shared memory with an oversized length can
invalidate memory outside that region. A buffer shorter than 12 bytes
also lets the reserved field assignment write beyond the buffer before
the existing complete-header and payload checks run.

Require device receive buffers to fit entirely within shared memory and
contain a complete header before invalidating or accessing their data.
Return malformed descriptors with zero consumed length without touching
their buffers. Recycle short driver buffers before writing the header,
and reject available descriptor indices outside the descriptor table.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-6
@bentheredonethat
bentheredonethat force-pushed the Harden-untrusted-input-rpmsg-virtio branch from e90b35f to 0ea29fd Compare October 5, 2026 14:43
The receive buffer validation change left two consecutive blank lines
before cache invalidation. The strict checkpatch run used by CI reports
LINE_SPACING for this section.

Remove the extra blank line to satisfy the compliance style check.

Signed-off-by: Ben Levinsky <ben.levinsky@amd.com>
Assisted-by: Codex:GPT-6
@arnopo
arnopo merged commit fca8e74 into OpenAMP:main Oct 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants