Skip to content

fix(advert): bounds-check optional fields before memcpy#2531

Open
swaits wants to merge 1 commit into
meshcore-dev:mainfrom
swaits:fix/advert-parser-bounds-check
Open

fix(advert): bounds-check optional fields before memcpy#2531
swaits wants to merge 1 commit into
meshcore-dev:mainfrom
swaits:fix/advert-parser-bounds-check

Conversation

@swaits
Copy link
Copy Markdown

@swaits swaits commented May 12, 2026

Summary

AdvertDataParser::AdvertDataParser (src/helpers/AdvertDataHelpers.cpp) optimistically memcpy'd _lat / _lon / _extra1 / _extra2 from the supplied buffer before validating that the buffer was long enough. A crafted advert with all optional-field flag bits set and app_data_len = 1 over-read up to 12 bytes past the supplied slice. This change validates the length before each memcpy.

Background

The buggy pattern (verbatim, before this PR):

int i = 1;
if (_flags & ADV_LATLON_MASK) {
  memcpy(&_lat, &app_data[i], 4); i += 4;     // reads 8 bytes past app_data[1] regardless of app_data_len
  memcpy(&_lon, &app_data[i], 4); i += 4;
}
if (_flags & ADV_FEAT1_MASK) { memcpy(&_extra1, &app_data[i], 2); i += 2; }
if (_flags & ADV_FEAT2_MASK) { memcpy(&_extra2, &app_data[i], 2); i += 2; }
if (app_data_len >= i) {                       // length check happens AFTER the reads
  ...
  _valid = true;
}

The app_data argument in the live path is &pkt->payload[i] where pkt->payload is the 184-byte packet buffer (src/Packet.h), so OOB reads land inside the same allocation. That's the only thing keeping this from being a remote info-disclosure today. The fix prevents the OOB regardless of where the buffer happens to live, which is the right hygiene for a parser that takes a length parameter.

Change

src/helpers/AdvertDataHelpers.cpp — before each memcpy of an optional field, check that the requested byte count is available. If not, return early; _valid stays false, which is the same outcome callers already get for a too-short payload.

Why this is the minimal fix

The pattern matches the existing build-side AdvertDataBuilder::encodeTo reasoning. Rewriting the parser around a separate validator pass would add code without removing the underlying bug.

Risk / compatibility

  • Wire format: unchanged.
  • Behaviour: bit-for-bit identical for any valid advert (the lengths add up by construction). Only malformed adverts now stop earlier instead of writing junk into _lat / _lon / etc. and then being rejected by the trailing length check.

References

  • CWE-125 — Out-of-bounds Read.
  • CWE-1284 — Improper Validation of Specified Quantity in Input.

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.

1 participant