Summary
CubedOS.Transport_UDP.Messages.Send_Network_Message sends a fixed-size UDP buffer after copying five fewer payload bytes than the buffer requires. With the shipped 512-byte message size, every outbound datagram omits payload bytes 507–511 and replaces them with five bytes of uninitialized stack memory.
A receiving peer can therefore observe process memory that was never intended for transmission, while the sender silently corrupts the end of every payload. The issue was confirmed on CubedOS v1branch commit c402301.
Cause and Impact
The six-byte protocol header occupies buffer offsets 0–5. The faulty loop uses buffer indices 6 through Payload'Length, copying only 507 of 512 payload bytes, but Send_Socket transmits all 518 buffer bytes. Offsets 513–517 are consequently uninitialized stack data.
This is an information disclosure and message-integrity failure (CWE-200, CWE-908, and CWE-665). The proof of concept captured all three affected datagrams and confirmed that their final five bytes did not contain the payload sentinel.
How Pragmatic Found It
Pragmatic 0.2.1's EmptySync checker (CWE-585) put the UDP transport package under review. The checker looks for synchronization bodies that do no observable work, a common sign of incomplete implementation or refactoring. On CubedOS master, it reported the null-bodied Send_Lock.Wait entry:
$ ./bin/pragmatic -E=EmptySync -e=adb \
cubedos/src/modules/cubedos-transport_udp-messages.adb
CWE,Checker,Filename,Line,Tool,Comments
585,EmptySync,cubedos/src/modules/cubedos-transport_udp-messages.adb,60,
Pragmatic Scanner 0.2.1,"Empty entry body imposes synchronization cost while
doing nothing observable. Likely incomplete refactoring; add a comment if the
empty body is intentional (e.g., a barrier-only sync)."
The synchronization finding was a review lead, not the eventual vulnerability claim. Following the message path through the flagged package exposed an uninitialized output Buffer, a payload-copy loop, and a Send_Socket call that transmits the whole buffer. Comparing their ranges showed the security defect: the six-byte header plus 512-byte payload requires 518 initialized bytes, but the loop performs only 507 payload copies and leaves five transmitted positions unwritten.
Fix
A patch is proposed upstream in pull request #68 and is available in the reporter's fork at commit 58f6bdc. It iterates over Message.Payload'Range and copies each byte to its corresponding buffer offset after the six-byte header. This preserves the existing datagram size and wire layout while ensuring that the complete payload is transmitted and every buffer byte is initialized.
The production-linked regression test rebuilt successfully and captured three 518-byte datagrams; each contained the expected c1 c2 c3 c4 c5 sentinel at offsets 513–517.
Project Status
The upstream project appears to be retired. Developer Peter Chapin noted that the planned SPARK cleanup had never occurred which would have also surfaced this issue. Pull request #68 makes the remediation available to any remaining upstream maintainers.
Severity
CVSS 4.0 Base Score: 6.9 (Medium)
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
References
Disclosure Timeline
- 2026-05-18: Pragmatic Scanner identified the faulty UDP copy loop. The report and production-linked proof of concept were sent privately to Peter Chapin.
- 2026-05-19: PVD ID reserved while vendor coordination continued.
- 2026-05-23: Status follow-up sent to Peter Chapin and Carl Brandon.
- 2026-06-01: A further follow-up was sent to additional CubeSat project contacts after the earlier messages received no response.
- 2026-08-25: Peter Chapin replied that he had retired and that the planned SPARK cleanup had never occurred.
- 2026-09-07: The reporter asked whether there was any objection to publishing the findings given that development had stalled.
- 2026-10-21: A minimal fix was committed to the reporter's fork and verified with the production-linked proof of concept.
- 2026-10-21: Upstream pull request #68 was opened with the fix.