Fix 5 bugs: asn1_encode threshold, MQTT bounds/mid, unpack underflow, null deref#815
Fix 5 bugs: asn1_encode threshold, MQTT bounds/mid, unpack underflow, null deref#815
Conversation
…ck, mqtt_next_mid wrap, unpack underflow, on_close null check Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes several correctness and safety issues in core parsing/protocol paths (ASN.1 length encoding, MQTT packet handling, and generic length-field unpacking) found during a source audit.
Changes:
- Fixes ASN.1 length encoding threshold for 3-byte vs 4-byte encoding.
- Hardens MQTT handling (fixed-header bounds check, non-zero message IDs, null-guard in close callback).
- Prevents unsigned underflow in
hio_unpack_by_length_fieldby validating a signed computed package length.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
mqtt/mqtt_protocol.c |
Adds a minimal-length guard before decoding MQTT fixed header fields. |
mqtt/mqtt_client.c |
Ensures generated MQTT packet identifiers never wrap to 0; adds null-check in on_close. |
event/unpack.c |
Computes package length using signed arithmetic and validates it before converting to unsigned. |
base/hmath.h |
Corrects ASN.1 encode threshold constant to 2^24. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
event/unpack.c
Outdated
| int signed_package_len = (int)head_len + (int)body_len + setting->length_adjustment; | ||
| if (signed_package_len <= 0 || signed_package_len > (int)setting->package_max_length) { | ||
| hloge("Invalid package length %d!", signed_package_len); | ||
| io->error = ERR_OVER_LIMIT; | ||
| hio_close(io); | ||
| return -1; |
There was a problem hiding this comment.
signed_package_len is computed and compared using int casts. This can overflow (or mis-compare) if package_max_length is set above INT_MAX, since (int)setting->package_max_length becomes negative. Consider computing length in int64_t (or long long) and comparing against setting->package_max_length without narrowing; also consider setting io->error to ERR_INVALID_PACKAGE when the computed length is <= 0, reserving ERR_OVER_LIMIT for the > max-length case.
mqtt/mqtt_client.c
Outdated
| static unsigned short mqtt_next_mid() { | ||
| static unsigned short s_mid = 0; | ||
| return ++s_mid; | ||
| if (++s_mid == 0) s_mid = 1; | ||
| return s_mid; | ||
| } |
There was a problem hiding this comment.
mqtt_next_mid() uses a function-static s_mid without any synchronization. Public APIs like mqtt_client_publish/subscribe/unsubscribe call this outside cli->mutex_, so concurrent calls can data-race and produce duplicate/undefined MIDs. Consider making the counter per-client (e.g., a cli->mid field) and incrementing it under cli->mutex_, or using an atomic counter with the same non-zero wrap behavior.
|
@copilot apply changes based on the comments in this thread |
…per-client and thread-safe Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
Applied both review changes in commit 2bdf6f1:
|
Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com>
Bugs found during source code audit of core library paths — parsing, protocol handling, and arithmetic correctness.
asn1_encodewrong constant (base/hmath.h): Threshold16777126is a typo for16777216(2²⁴). Values in [16777126, 16777215] get 4-byte encoding instead of 3-byte.mqtt_head_unpackbuffer over-read (mqtt/mqtt_protocol.c): No bounds check before accessingbuf[0]whenlencould be 0 or 1. Reachable from network input viaon_packet.mqtt_next_mid()wraps to 0 and is not thread-safe (mqtt/mqtt_client.c,mqtt/mqtt_client.h):unsigned shortincrement wraps to 0 every 65536 calls. MQTT spec §2.2.1 requires non-zero packet identifiers. Additionally, the function-static counter had no synchronization, causing data races when concurrent calls tomqtt_client_publish/subscribe/unsubscribeoccurred. Replaced with a per-clientmid_field inmqtt_client_sprotected bycli->mutex_.hio_unpack_by_length_fieldunsigned underflow (event/unpack.c):package_len = head_len + body_len + length_adjustmentwherelength_adjustmentis a signedshortbutpackage_lenisunsigned int. Negative results wrap to ~4GB, causing OOB read or massive allocation. Now computed withlong longsigned arithmetic to avoid overflow even whenpackage_max_length > INT_MAX, and validated before use. UsesERR_INVALID_PACKAGEfor non-positive lengths andERR_OVER_LIMITfor exceeding max length.on_closenull dereference (mqtt/mqtt_client.c):clifromhevent_userdata(io)dereferenced without null check, unlike peer callbacksconnect_timeout_cbandreconnect_timer_cbwhich both guard against this.✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.