Bound wakeword payload length to prevent DoS
Added a 10MB limit and lower bound to `payload_length` read from network payloads in `wakeword.py` and `wakeword_bench.py` to prevent unbounded memory allocation and infinite waiting for malicious/corrupt frames. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
parent
8f056e8cf0
commit
16f155ff7b
3
.jules/sentinel.md
Normal file
3
.jules/sentinel.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## 2025-02-14 — Bound wakeword payload length
|
||||||
|
**Learning:** `payload_length` read from network JSON payloads in `wakeword.py` and `wakeword_bench.py` lacked bounds checks, allowing a malicious Wyoming server (or MITM) to cause a DoS by providing a huge length and freezing the read loop or consuming all memory.
|
||||||
|
**Action:** Added `if not (0 <= payload_len <= 10 * 1024 * 1024):` bounds check to safely drop oversized or negative `payload_length` frames.
|
||||||
@ -112,6 +112,9 @@ class WakewordListener:
|
|||||||
self._handle_detection()
|
self._handle_detection()
|
||||||
|
|
||||||
payload_len = msg.get("payload_length", 0)
|
payload_len = msg.get("payload_length", 0)
|
||||||
|
if not (0 <= payload_len <= 10 * 1024 * 1024):
|
||||||
|
logbuffer.log(f"[wakeword] Disconnecting: payload length {payload_len} out of bounds", level="WARNING")
|
||||||
|
break
|
||||||
if payload_len > 0:
|
if payload_len > 0:
|
||||||
# Consume payload
|
# Consume payload
|
||||||
remaining = payload_len
|
remaining = payload_len
|
||||||
@ -258,6 +261,9 @@ class WakewordActionListener:
|
|||||||
self._stop_event.set() # one-shot: stop after first fire
|
self._stop_event.set() # one-shot: stop after first fire
|
||||||
cb()
|
cb()
|
||||||
payload_len = msg.get("payload_length", 0)
|
payload_len = msg.get("payload_length", 0)
|
||||||
|
if not (0 <= payload_len <= 10 * 1024 * 1024):
|
||||||
|
logbuffer.log(f"[wakeword-action] Disconnecting: payload length {payload_len} out of bounds", level="WARNING")
|
||||||
|
break
|
||||||
if payload_len > 0:
|
if payload_len > 0:
|
||||||
remaining = payload_len
|
remaining = payload_len
|
||||||
while remaining > 0:
|
while remaining > 0:
|
||||||
|
|||||||
@ -322,6 +322,9 @@ def _drain_detections(buf: bytes) -> tuple[bytes, int]:
|
|||||||
except (ValueError, UnicodeDecodeError):
|
except (ValueError, UnicodeDecodeError):
|
||||||
return rest, found
|
return rest, found
|
||||||
plen = msg.get("payload_length", 0) or 0
|
plen = msg.get("payload_length", 0) or 0
|
||||||
|
if not (0 <= plen <= 10 * 1024 * 1024):
|
||||||
|
# Abort this buffer completely if framing is corrupt or size is unreasonable
|
||||||
|
return b"", found
|
||||||
if len(rest) < plen:
|
if len(rest) < plen:
|
||||||
return buf, found # payload not fully arrived yet; wait for more
|
return buf, found # payload not fully arrived yet; wait for more
|
||||||
rest = rest[plen:]
|
rest = rest[plen:]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user