Infinite Loop in UDS DDDI Dissector results in Denial of Service
Jaime Cavero reported the following to the security email address:
## 1. Summary
A malformed UDS service 0x2C (DynamicallyDefineDataIdentifier) request frame with subfunction 0x02 (defineByMemoryAddress) causes an infinite loop in the DDDI request handler within dissect_uds_internal() in epan/dissectors/packet-uds.c. The loop never terminates because the attacker-controlled addressAndLengthFormatIdentifier byte is set to 0x00, yielding zero-length address and size fields that prevent the parsing offset from advancing.
|Field | Value |
|------|-------|
| CWE | CWE-835 (Loop with Unreachable Exit Condition) |
| CVSS 3.1 | 4.3 Medium (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L) |
| Impact | Denial of Service (application hang, 100% CPU on dissection thread) |
## 2. Affected Versions
Confirmed on Wireshark 4.7.0 (development branch, commit c9fa00ebeb). The vulnerable code path has been present since the UDS DDDI dissector was introduced, so all prior versions containing the UDS_DDDI_TYPES_DEFINE_BY_MEM_ADDRESS handler are affected.
## 3. Root Cause
- File: epan/dissectors/packet-uds.c
- Function: dissect_uds_internal()
- Code path: case UDS_SERVICES_DDDI -> non-reply branch -> case UDS_DDDI_TYPES_DEFINE_BY_MEM_ADDRESS
The dissector reads memory_size_length and memory_address_length from a single byte (the addressAndLengthFormatIdentifier per ISO 14229-1) via two proto_tree_add_item_ret_uint() calls that apply masks 0xF0 (high nibble, memory_size_length) and 0x0F (low nibble, memory_address_length) respectively:
```c
uint32_t memory_size_length, memory_address_length;
proto_tree_add_item_ret_uint(uds_tree, hf_uds_memory_size_length,
tvb, offset, 1, ENC_NA, &memory_size_length); // mask 0xF0
proto_tree_add_item_ret_uint(uds_tree, hf_uds_memory_address_length,
tvb, offset, 1, ENC_NA, &memory_address_length); // mask 0x0F
offset += 1;
do {
proto_tree_add_item_ret_uint64(uds_tree, hf_uds_memory_address,
tvb, offset, memory_address_length, ...);
offset += memory_address_length; // +0 when length == 0
proto_tree_add_item_ret_uint64(uds_tree, hf_uds_memory_size,
tvb, offset, memory_size_length, ...);
offset += memory_size_length; // +0 when length == 0
} while (offset + memory_address_length + memory_size_length <= data_length);
```
ISO 14229-1 mandates that both nibbles of the addressAndLengthFormatIdentifier be >= 1, but the dissector does not enforce this constraint. When a crafted packet sets the byte to 0x00, both extracted lengths are zero. The do-while construct guarantees at least one iteration executes unconditionally. Since offset never advances, the loop guard (offset + 0 + 0 <= data_length) trivially holds on every subsequent evaluation, and the loop runs forever.
The protection that failed is input validation: there is no check that memory_address_length > 0 and memory_size_length > 0 before entering the loop.
Related issue: The function dissect_uds_memory_addr_size() (used by services 0x23, 0x34, 0x35, 0x3D) reads the same addressAndLengthFormatIdentifier byte with the same masks and equally lacks validation. That path is not a loop, so it does not produce an infinite hang, but passing length=0 to proto_tree_add_item_ret_uint64() is still a defect (zero-length field read). The same guard should be applied there.
## 4. Exploitation
This is fully reliable and trivial to trigger. No race conditions, heap shaping, or special timing are required.
To trigger: send or supply a pcap containing a single UDS request frame with:
- SID: 0x2C (not 0x6C; this is a request-path-only vulnerability)
- Subfunction: 0x02 (defineByMemoryAddress; or 0x82 with suppress-positive-response bit set)
- 2 bytes: any dynamically defined DID value
- addressAndLengthFormatIdentifier: 0x00 (both nibbles zero)
- At least 1 trailing byte (so data_length > offset after header parsing, ensuring the loop guard holds on entry)
Reliability: 100%. The loop condition is deterministic and depends solely on parsed field values, not on memory layout, timing, or platform.
## 5. Impact
The attacker gains a deterministic application hang against any of:
- Wireshark GUI: becomes completely unresponsive on opening the pcap or capturing the frame live. Must be force-killed.
- tshark CLI: hangs at 100% CPU when processing the file (tshark -r malicious.pcap). Blocks any automated pipeline (CI, IDS log processing, forensics tooling) that invokes tshark.
- Live capture: if a crafted frame appears on a monitored network segment, any Wireshark or tshark instance dissecting that traffic hangs.
Real-world scenarios:
- An attacker on a CAN/DoIP network (automotive diagnostics) injects a single crafted UDS frame to disable a technician's Wireshark session.
- A malicious pcap attached to a bug report or shared in a forensics workflow hangs every analyst who opens it.
- Automated pcap ingestion pipelines (SOC tooling, regression test harnesses) stall indefinitely.
Boundaries: no memory corruption, no code execution, no information disclosure, no privilege escalation.
## 6. Proof of Concept
Attached: uds_infinite_loop.pcap (122 bytes)
Reproduction:
$ timeout 5 tshark -r uds_infinite_loop.pcap
(hangs until killed by timeout; exit code 124)
The pcap contains a single Ethernet/IPv4/TCP/DoIP frame carrying a 16-byte UDS payload:
```
2c 02 08 00 00 00 00 00 00 00 00 00 00 00 00 00
| | | | |
| | | | +-- 11 trailing zero bytes (ensure data_length > offset)
| | | +-- addressAndLengthFormatIdentifier = 0x00 (both nibbles zero)
| | +-- dynamically defined DID = 0x0800
| +-- subfunction 0x02 (defineByMemoryAddress)
+-- SID 0x2C (DDDI request)
```
The DoIP transport (TCP dst port 13400) routes through dissect_uds_doip() into dissect_uds_internal(). After parsing the 5-byte header (SID + subfunction + DID + format identifier), offset=5, data_length=16, and both lengths are 0. The do-while loop guard evaluates 5 + 0 + 0 <= 16 = true on every iteration indefinitely.
## 7. Suggested Fix
Primary fix: validate lengths before entering the do-while loop in the DDDI handler inside `dissect_uds_internal()`:
```c
offset += 1;
/* Guard: addressAndLengthFormatIdentifier with zero-length fields
is malformed per ISO 14229-1 and would infinite-loop below. */
if (memory_address_length == 0 || memory_size_length == 0) {
/* Optionally: proto_tree_add_expert(..., &ei_uds_invalid_length, ...); */
break; /* exits the DDDI subfunction switch */
}
do {
...
} while (...);
```
Secondary fix: apply the same guard in `dissect_uds_memory_addr_size()`, after reading the format identifier and before the zero-length `proto_tree_add_item_ret_uint64()` calls. While that path cannot infinite-loop (no loop exists), a zero-length field read is still malformed input and should be rejected.
Both fixes are minimal, directly applicable, and introduce no behavioral change for well-formed ISO 14229 traffic.
## 8. Mitigations (until a patch is released)
- Disable the UDS dissector if it is not needed: Analyze > Enabled Protocols > uncheck "uds".
- Avoid opening untrusted pcap files in Wireshark/tshark.
- In automated pipelines, run tshark under a timeout wrapper (e.g., timeout 60 tshark -r file.pcap).
[uds_infinite_loop.pcap](/uploads/b70be9e074ec6448491f33b12393a533/uds_infinite_loop.pcap)
task