DLMS/COSEM dissector infinite loop denial of service
## Summary
The `dlms_dissect_data()` → `dlms_dissect_compact_array_content()` function in the `cosem` dissector hangs `tshark` with ~100% CPU usage until the process is killed - `kill -9 $(pidof tshark)`.
Impact:
- **CPU DoS**: tshark consumes 100% CPU indefinitely
- **No timeout mechanism**: Process must be killed externally (SIGKILL)
- **Single UDP packet**: 63-byte crafted packet is sufficient
- **No user configuration needed**: Port 4059 is registered by default
## AI assistance
Identified using Claude Opus 4.6. I've verified the bug, but not the root cause or mitigation.
## Sample capture file
[generate_cosem.py](/uploads/ec3dd25295765452780fa5cdf71271b1/generate_cosem.py)
[cosem_infinite_loop.pcap](/uploads/88f0af457a37615577cb4ab77297e1bc/cosem_infinite_loop.pcap)
[cosem_infinite_loop_send.py](/uploads/70915a4aa2c625d56fd312764ec89709/cosem_infinite_loop_send.py)
## Steps to reproduce
```
$ ../.venv/bin/python3 generate_cosem.py
[+] Generated cosem_infinite_loop.pcap
Bug: packet-cosem.c:2203 - compact array with 0-element struct type
Test: ./build/run/tshark -r pocs/cosem_infinite_loop.pcap
Expected: tshark hangs (infinite loop, 100% CPU, must be killed)
$ ../build/run/tshark -r cosem_infinite_loop.pcap
** (tshark:397963) 19:13:43.398763 [(none) MESSAGE] -- JSON Dictionary: No config.txt or jsonmain.xml found (using generic mode)
```
```
$ top | grep tshark
397963 user 20 0 262168 158692 96528 R 100.0 1.3 0:07.96 tshark
```
Alternatively, use `cosem_infinite_loop_send.py` to send a UDP crashing packet Wireshark:
```
$ ../.venv/bin/python3 cosem_infinite_loop_send.py 127.0.0.1
[*] Sending COSEM/DLMS infinite-loop trigger to 127.0.0.1:4059
Payload: 21 bytes
Bug: packet-cosem.c:2203 — compact array with 0-element struct
(no root — using plain UDP socket instead of raw)
[+] Sent. Any Wireshark/tshark instance capturing on port 4059 will hang.
```
## What is the current bug behavior?
**AI suggested root cause:**
**File:** `epan/dissectors/packet-cosem.c:2203`
**Function:** `dlms_dissect_data()` → `dlms_dissect_compact_array_content()`
In `dlms_dissect_data()`, the compact-array case (choice == 19) loops:
```c
while (*offset < content_end) {
subitem = dlms_dissect_compact_array_content(tvb, pinfo, subtree,
description_offset, offset);
}
```
If the type description is a structure with 0 elements (`[0x02, 0x00]`), `dlms_dissect_compact_array_content()` enters the structure branch:
```c
if (choice == 2) { /* structure */
unsigned elements = dlms_get_length(tvb, &description_offset);
while (elements != 0) { /* SKIPPED when elements=0 */ }
}
```
When `elements == 0`, the inner loop is skipped and `*content_offset` is **never modified**. The outer `while (*offset < content_end)` loop runs forever.
The `increment_dissection_depth` / `decrement_dissection_depth` calls inside `dlms_dissect_compact_array_content` cancel each other out per iteration, so the depth check never triggers.
## What is the expected correct behavior?
**AI suggested fix:**
Detect when `*content_offset` hasn't advanced after `dlms_dissect_compact_array_content` returns, and break the loop:
```c
while (*offset < content_end) {
int saved_offset = *offset;
subitem = dlms_dissect_compact_array_content(..., offset);
if (*offset <= saved_offset) break; // prevent infinite loop
}
```
## Build information
Built from master branch.
```
** (tshark:396187) 19:08:22.771852 [(none) MESSAGE] -- JSON Dictionary: No config.txt or jsonmain.xml found (using generic mode)
TShark (Wireshark) 4.7.0 (v4.7.0rc0-2101-g6fd3e409475e).
Copyright 1998-2026 Gerald Combs <gerald@wireshark.org> and contributors.
Licensed under the terms of the GNU General Public License (version 2 or later).
This is free software; see the file named COPYING in the distribution. There is
NO WARRANTY; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Compile-time info:
Bit width: 64-bit
Compiler: GCC 13.3.0
GLib: 2.80.0
With:
+brotli +libxml2 2.9.14 +PCRE2 10.42 2022-12-11
+Gcrypt 1.10.3 +LZ4 1.9.4 +Snappy 1.1.10
+GnuTLS 3.8.3 and PKCS#11 +MaxMind +zlib 1.3
+Kerberos (MIT) +nghttp2 1.59.0 +Zstandard 1.5.5
+libpcap +nghttp3 0.8.0
Without:
-libnl -Lua -xxhash
-libsmi -POSIX capabilities -zlib-ng
Runtime info:
OS: Linux 6.8.0-100-generic
CPU: [redacted]
Memory: 11914 MB of physical memory
GLib: 2.80.0
Locale: LC_TYPE=en_US.UTF-8
Plugins: supported, 0 loaded
With:
+brotli 1.1.0 +nghttp2 1.59.0
+c-ares 1.27.0 +nghttp3 0.8.0
+Gcrypt 1.10.3 +PCRE2 10.42 2022-12-11
+GnuTLS 3.8.3 +zlib 1.3
+libpcap 1.10.4 (with TPACKET_V3) +Zstandard 1.5.5
+LZ4 1.9.4
```
(Also tested on Wireshark 4.6.4 on Windows 10 x64.)
issue