Skip to content

Fix unsafe ByteString bounds on wire protocol data#76

Merged
jappeace merged 3 commits intowinterland1989:masterfrom
jappeace-sloth:fix-unsafe-bounds-upstream
Mar 31, 2026
Merged

Fix unsafe ByteString bounds on wire protocol data#76
jappeace merged 3 commits intowinterland1989:masterfrom
jappeace-sloth:fix-unsafe-bounds-upstream

Conversation

@jappeace-sloth
Copy link
Copy Markdown

Summary

  • Replace unsafeDrop, unsafeTail, and unsafeIndex with safe bounded alternatives in text protocol parsers (MySQLValue.hs) — adds guard checks before B.tail/B.drop in dateParser, timeParser, and timestamp/time field decoders
  • Add precision >= scale guard for NEWDECIMAL in BinLogValue.hs to prevent Word8 underflow causing out-of-bounds sizeTable access
  • Add bounds checking to eventHeaderLen in BinLogEvent.hs to handle unknown event types (negative index) gracefully by returning 0

Motivation

The unsafe ByteString operations cause undefined behavior (reading garbage memory, segfaults, silent wrong results) when receiving malformed data from the MySQL wire protocol. The safe alternatives are also O(1) — the only overhead is a single integer comparison per call. This is negligible compared to the cost of a network round-trip.

Test plan

  • Added 8 regression tests in test/BoundsCheck.hs covering all fixed code paths
  • Tests crash/segfault before fix, pass cleanly after
  • All 137 unit tests pass

🤖 Generated with Claude Code

jappeace-sloth and others added 3 commits March 25, 2026 11:08
Replace unsafeDrop, unsafeTail, and unsafeIndex with safe bounded
alternatives in text protocol parsers (MySQLValue.hs), add a
precision >= scale guard for NEWDECIMAL in BinLogValue.hs, and add
bounds checking to eventHeaderLen in BinLogEvent.hs.

The unsafe operations caused undefined behavior (reading garbage memory,
segfaults) when receiving malformed data from the MySQL wire protocol.
The safe alternatives are also O(1) — the only cost is a single integer
comparison per call.

Changes:
- MySQLValue.hs: Add guards before B.tail/B.drop in dateParser,
  timeParser, timestamp/datetime, and time field parsers
- BinLogValue.hs: Guard precision >= scale in NEWDECIMAL to prevent
  Word8 underflow causing out-of-bounds sizeTable access
- BinLogEvent.hs: Bounds check in eventHeaderLen to handle unknown
  event types (negative index) gracefully by returning 0
- Add test/BoundsCheck.hs with 8 regression tests covering all
  fixed code paths

Prompt: Implement plan to fix unsafe bounds in mysql-haskell (TDD)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old timestamp/datetime tests used "abc" and "" as inputs.
readDecimal returns Nothing on non-numeric input, which short-circuits
via Maybe's Applicative instance before unsafeDrop is ever forced
(lazy evaluation). These tests passed even without the fix.

Replace with inputs where dateParser succeeds but the string is too
short for the subsequent unsafeDrop 11:
- "2024-01-01" (10 bytes) — dateParser succeeds, unsafeDrop 11 is UB
- "2024-01-01 1" (12 bytes) — dateParser succeeds, unsafeDrop 11
  gives "1", timeParser reads hour, then unsafeTail on "" is UB

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jappeace jappeace merged commit 3f15b3b into winterland1989:master Mar 31, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants