Skip to content

SN_Decode_GWInfo: reject extended-length frame too short for gwId#542

Open
dxbjavid wants to merge 1 commit into
wolfSSL:masterfrom
dxbjavid:sn-gwinfo-bounds-check
Open

SN_Decode_GWInfo: reject extended-length frame too short for gwId#542
dxbjavid wants to merge 1 commit into
wolfSSL:masterfrom
dxbjavid:sn-gwinfo-bounds-check

Conversation

@dxbjavid
Copy link
Copy Markdown

What

SN_Decode_GWInfo reads the message-type and gateway-ID bytes from the
caller-supplied receive buffer via *rx_payload++ (src/mqtt_sn_packet.c:306,
src/mqtt_sn_packet.c:313). When the GWINFO frame uses the extended-length
encoding (first byte = SN_PACKET_LEN_IND = 0x01, length in the next two
bytes), the function has already consumed three header bytes by the time
it reaches these reads — but the existing minimum-size guard

if (total_len < 3) {
    return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
}

is sized for the short-form header (one byte). An extended-length frame
whose decoded total_len is <= (rx_payload - rx_buf) slips past it, and
the subsequent message-type / gateway-ID reads run past the caller's
rx_buf bound. SN_Packet_Read accepts both header forms and forwards
the buffer to SN_Decode_GWInfo via MqttClient_DecodePacket, so this
is reachable from the wire.

How to reproduce

Place a four-byte frame at the tail of a page-sized region whose next
page is PROT_NONE:

[0] 0x01            (SN_PACKET_LEN_IND)
[1] 0x00  [2] 0x04  (total_len = 4)
[3] 0x02            (SN_MSG_TYPE_GWINFO)

With rx_buf_len = 4:

  • total_len = 4, three bytes are consumed for the extended-length
    header, leaving rx_payload at offset 3.
  • 4 > 4 is false (passes total_len > rx_buf_len).
  • 4 < 3 is false (passes the existing minimum).
  • type = *rx_payload++ reads offset 3 (OK).
  • gw_info->gwId = *rx_payload++ reads offset 4 → SIGBUS on the
    PROT_NONE guard page.

Fix

Replace the fixed < 3 minimum with a check against
(rx_payload - rx_buf) + 2, which is the bytes the function still needs
to read after the length-indicator block (one message-type byte plus one
gateway-ID byte). The check covers both header layouts uniformly: the
short form (rx_payload - rx_buf == 1) keeps the prior >= 3 minimum,
and the extended-length form (rx_payload - rx_buf == 3) requires
total_len >= 5. The fix is in the callee; callers do not need to
change.

Test / build evidence

A small reproducer with the page-guarded frame above plus positive
controls was built against libwolfmqtt from this tree:

test 1 ext-len-truncated  rc=-3 (expect MQTT_CODE_ERROR_MALFORMED_DATA)
test 2 ext-len-le-header  rc=-3 (expect MQTT_CODE_ERROR_MALFORMED_DATA)
test 3 short-form         rc=3  gwId=0x42 (expect 3, 0x42)
test 4 ext-len-with-addr  rc=7  gwId=0x77 (expect 7, 0x77)
ALL PASS

Before the patch the page-guarded reproducer exits with signal 138
(SIGBUS / SIGSEGV depending on the OS) on the offset-4 read; after the
patch it returns MQTT_CODE_ERROR_MALFORMED_DATA and the existing
short-form and extended-length-with-address inputs continue to decode.
./configure --enable-sn && make -j builds clean.

An MQTT-SN GWINFO frame with the 0x01 SN_PACKET_LEN_IND prefix consumes
three header bytes (indicator + two-byte length) before the message-type
and gateway-ID bytes that SN_Decode_GWInfo reads via *rx_payload++. The
existing minimum-size guard (total_len < 3) was sized for the short-form
header (one byte) and accepts an extended-length frame whose decoded
total_len equals the bytes the function has already consumed for the
header (3) or sits one byte short of also covering the gateway-ID byte
(4). In both cases the type / gwId reads run past the caller-supplied
rx_buf bound; a page-guarded reproducer (mmap + adjacent PROT_NONE page)
shows SIGBUS on the *rx_payload++ at offset 4.

Replace the fixed "< 3" minimum with a check against (rx_payload - rx_buf)
+ 2, which is the number of bytes the function must still read after the
length-indicator block (one message-type byte plus one gateway-ID byte).
This covers both header layouts uniformly: the short form keeps the
prior >= 3 minimum, and the extended-length form requires >= 5.

After the patch the page-guarded frame returns MQTT_CODE_ERROR_MALFORMED_DATA
cleanly and the existing valid short-form and extended-length-with-address
inputs continue to decode.
@wolfSSL-Bot
Copy link
Copy Markdown

Can one of the admins verify this patch?

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.

4 participants