Skip to content

Rewrite mac@index normalization to avoid UB and a release-build misco…#689

Merged
DigiH merged 1 commit intodevelopmentfrom
fix-mac-index-miscompile
Apr 26, 2026
Merged

Rewrite mac@index normalization to avoid UB and a release-build misco…#689
DigiH merged 1 commit intodevelopmentfrom
fix-mac-index-miscompile

Conversation

@1technophile
Copy link
Copy Markdown
Member

Description:

The previous implementation built a std::string from mac_id, then walked it with for (int x = 0; x < mac_string.length(); x++) while calling mac_string.erase(x, 1) and mac_string[x] = tolower(mac_string[x]) in the same iteration. Two latent issues:

  1. Passing nullptr to std::string's const char* constructor (when mac_id is null) is undefined behaviour.

  2. Reading mac_string[x] immediately after mac_string.erase(x, 1) relies on subtle ordering: the erase shifts characters left, so the value at index x changes between the colon check and the tolower read. With aggressive optimization, the compiler is free to cache either the length or the character value across the erase call, which produces a corrupted normalized MAC.

Observed in the wild: TheengsApp built with Android NDK Clang 19 (release pipeline: -O3 + LTO + BOLT) silently returned false from the entire mac@index/revmac@index branch for IBT-2X(S) and MB/SW advertisements, even though the Linux/Python build of the same source decoded them correctly. The pattern above is a plausible trigger.

Rewrite:

  • Strip colons + lowercase into a fixed 13-byte stack buffer in a single forward pass over mac_id.
  • Bail out cleanly if mac_id is null or contains fewer than 12 hex chars after stripping (a 12-char comparison cannot match anyway).
  • Reuse cond_len (12) for the buffer index limit, the reverse length, and the strncmp length, removing the duplicated magic literal.

Verified:

  • ctest (BLE + BLE_fail) - 553 + 18 cases, no regressions.
  • TheengsApp Android (Qt 6.10.3, NDK 28.2 / Clang 19): IBT-2X(S) and MB/SW vectors that previously decoded as no-match now publish to MQTT correctly.

Checklist:

  • The pull request is done against the latest development branch
  • Only one feature/fix was added per PR and the code change compiles without warnings
  • I accept the DCO.

…mpile

The previous implementation built a std::string from mac_id, then walked
it with `for (int x = 0; x < mac_string.length(); x++)` while calling
`mac_string.erase(x, 1)` and `mac_string[x] = tolower(mac_string[x])` in
the same iteration. Two latent issues:

1. Passing nullptr to std::string's `const char*` constructor (when
   mac_id is null) is undefined behaviour.

2. Reading `mac_string[x]` immediately after `mac_string.erase(x, 1)`
   relies on subtle ordering: the erase shifts characters left, so the
   value at index x changes between the colon check and the tolower
   read. With aggressive optimization, the compiler is free to cache
   either the length or the character value across the erase call,
   which produces a corrupted normalized MAC.

Observed in the wild: TheengsApp built with Android NDK Clang 19
(release pipeline: -O3 + LTO + BOLT) silently returned false from the
entire mac@index/revmac@index branch for IBT-2X(S) and MB/SW
advertisements, even though the Linux/Python build of the same source
decoded them correctly. The pattern above is a plausible trigger.

Rewrite:
- Strip colons + lowercase into a fixed 13-byte stack buffer in a
  single forward pass over `mac_id`.
- Bail out cleanly if `mac_id` is null or contains fewer than 12 hex
  chars after stripping (a 12-char comparison cannot match anyway).
- Reuse `cond_len` (12) for the buffer index limit, the reverse length,
  and the strncmp length, removing the duplicated magic literal.

Verified:
- ctest (BLE + BLE_fail) - 553 + 18 cases, no regressions.
- TheengsApp Android (Qt 6.10.3, NDK 28.2 / Clang 19): IBT-2X(S) and
  MB/SW vectors that previously decoded as no-match now publish to
  MQTT correctly.
@DigiH
Copy link
Copy Markdown
Contributor

DigiH commented Apr 26, 2026

Thanks

@DigiH DigiH merged commit d63d9c2 into development Apr 26, 2026
14 checks passed
@DigiH DigiH deleted the fix-mac-index-miscompile branch April 27, 2026 18:21
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