Summary
Any authentication material appended after a typed DDS/CDR payload is silently
stripped at CDR deserialization — by standard subscribers, bridges, and RMW
implementations. This is the same structural flaw as MAVLink relay-stripping
(CVE pending, GHSA-f5rj-mrxh-r7vm), adapted to the DDS type system.
Root Cause
CDR deserialization reads exactly the bytes defined by the IDL type schema.
Bytes appended after the schema boundary are never transferred into the typed
struct. Re-serialization produces schema-size bytes only. Auth material gone.
Two failure modes depending on RMW:
| Middleware |
Effect |
| CycloneDDS 11.0.1 |
Auth bytes arrive in raw RTPS payload, discarded at CDR deserialization — silent strip |
| FastDDS / rmw_fastrtps_cpp |
Reader history pre-allocated to type max CDR size; oversized payloads rejected — sample never reaches callback |
The silent-strip case is worse: the subscriber receives a valid message with no
indication that auth material was attached and removed.
Tested Configuration
CycloneDDS 11.0.1 Python bindings
FastDDS (rmw_fastrtps_cpp) — ROS2 Humble default RMW
geometry_msgs/msg/Twist (52-byte CDR: 4B header + 6×float64)
Ubuntu 22.04 LTS, ROS2 Humble, Python 3.10
PoC Output (--simulate mode, no ROS2 install required)
Auth scheme Sent Received Stripped Result
──────────────────────────── ────── ──────── ───────── ────────────────
HMAC-SHA3-256 (32 B) 84 52 32 FAIL — auth gone
Ed25519 sig (64 B) 116 52 64 FAIL — auth gone
ML-DSA-87 sig (4627 B) 4679 52 4627 FAIL — auth gone
PoC: tools/ros2_bridge_strip_poc.py in https://github.com/cleitonaugusto/CleitonQ
Run with --simulate (no ROS2 install) or against real CycloneDDS endpoints.
Multi-hop Compounding
Multi-domain deployments using domain_bridge, ros1_bridge, or zenoh-bridge-ros2dds
re-serialize at each hop. Auth bytes stripped at hop 1 cannot be recovered at hop 2+.
Comparison with MAVLink
| Protocol |
Boundary marker |
Strip mechanism |
| MAVLink v2 |
STX + LEN frame header |
Relay reads exactly those bytes, discards rest |
| ROS2/DDS |
CDR IDL type schema |
Deserializer reads schema fields only, ignores trailing bytes |
Same vulnerability class. Different boundary marker. Both protocol implementations
are correct per their respective specs — the flaw is in the architectural
assumption that bytes appended outside a typed boundary survive to the application.
The Fix
Authentication material must be a separate, typed ROS2 message on a parallel topic.
Example:
/cmd_vel — geometry_msgs/Twist (command)
/cmd_vel/auth — cleitonq_msgs/Auth (ML-DSA-87 sig + nonce, typed)
The subscriber verifies /cmd_vel/auth before acting on /cmd_vel.
DDS middleware forwards typed messages as-is — their byte boundary is defined
by their own schema, not appended to another message.
Related
I am Cleiton Augusto Correa Bezerra, who found and documented the MAVLink variant
of this vulnerability class. The DDS/ROS2 variant follows the same architectural
root cause. Happy to provide additional test data or collaborate on a mitigation proposal.
Summary
Any authentication material appended after a typed DDS/CDR payload is silently
stripped at CDR deserialization — by standard subscribers, bridges, and RMW
implementations. This is the same structural flaw as MAVLink relay-stripping
(CVE pending, GHSA-f5rj-mrxh-r7vm), adapted to the DDS type system.
Root Cause
CDR deserialization reads exactly the bytes defined by the IDL type schema.
Bytes appended after the schema boundary are never transferred into the typed
struct. Re-serialization produces schema-size bytes only. Auth material gone.
Two failure modes depending on RMW:
The silent-strip case is worse: the subscriber receives a valid message with no
indication that auth material was attached and removed.
Tested Configuration
PoC Output (--simulate mode, no ROS2 install required)
PoC:
tools/ros2_bridge_strip_poc.pyin https://github.com/cleitonaugusto/CleitonQRun with
--simulate(no ROS2 install) or against real CycloneDDS endpoints.Multi-hop Compounding
Multi-domain deployments using
domain_bridge,ros1_bridge, orzenoh-bridge-ros2ddsre-serialize at each hop. Auth bytes stripped at hop 1 cannot be recovered at hop 2+.
Comparison with MAVLink
STX + LENframe headerSame vulnerability class. Different boundary marker. Both protocol implementations
are correct per their respective specs — the flaw is in the architectural
assumption that bytes appended outside a typed boundary survive to the application.
The Fix
Authentication material must be a separate, typed ROS2 message on a parallel topic.
Example:
/cmd_vel— geometry_msgs/Twist (command)/cmd_vel/auth— cleitonq_msgs/Auth (ML-DSA-87 sig + nonce, typed)The subscriber verifies
/cmd_vel/authbefore acting on/cmd_vel.DDS middleware forwards typed messages as-is — their byte boundary is defined
by their own schema, not appended to another message.
Related
I am Cleiton Augusto Correa Bezerra, who found and documented the MAVLink variant
of this vulnerability class. The DDS/ROS2 variant follows the same architectural
root cause. Happy to provide additional test data or collaborate on a mitigation proposal.