qr: fix left shift beyond type size
What changed, and why it matters
This commit fixes a subtle type bug in the QR code display code of the Blockstream Jade hardware wallet. A value returned from a function was stored as a `bool` (true/false), but later used in a bit-shift operation. In C, shifting a `bool` by 32 or more bits is undefined behavior and was caught by an undefined-behavior sanitizer. The fix changes the variable type to `uint32_t` so the shift is well-defined. The practical security impact is likely low: it affects only QR rendering, not private keys or transaction signing, and hardware-wallet QR output is not attacker-controlled input. However, undefined behavior in embedded firmware is worth cleaning up because compilers may optimize it unpredictably.
Apply the patch. Consider running UBSan/ASan builds in CI for the QR and display code paths. No immediate incident response is indicated because the issue is in a non-privileged output path and no exploit has been demonstrated.
Security signals we found
Undefined behavior sanitizer (UBSan) finding
Left shift beyond type width
Type promotion bug in bit manipulation
QR code rendering path only
No input validation bypass or memory corruption directly demonstrated
Evidence from the diff
In main/qrcode.c, qrcode_getModule() returns a value that is later shifted left by dest_bit bits (0-31) and ORed into a uint32_t icon buffer. The return value was being assigned to a bool, which is then promoted to int for the shift. A left shift where the promoted type’s width is exceeded (e.g., shifting a 32-bit value by 32 or more, or a narrower promoted value in ways that invoke UB) is undefined behavior in C. UBSan flagged this. The patch changes the local variable from bool paint to uint32_t paint in two functions (qrcode_toIcon and qrcode_toFragmentsIcons) so the shift operand has the correct width and the behavior is defined. No other logic changes.
Changed components
main/qrcode.cqrcode_toIcon()qrcode_toFragmentsIcons()QR code icon generation / displayInspect captured patch +2 / −2
diff --git a/main/qrcode.c b/main/qrcode.c
index 0693a85..46e2d10 100644
--- a/main/qrcode.c
+++ b/main/qrcode.c
@@ -968,7 +968,7 @@ void qrcode_toIcon(QRCode* qrcode, Icon* icon, const uint8_t scale)
// scaling
for (uint8_t j = 0; j < scale; j++) {
for (uint8_t x = 0; x < qrcode->size; x++) {
- const bool paint = qrcode_getModule(qrcode, x, y);
+ const uint32_t paint = qrcode_getModule(qrcode, x, y);
// scaling
for (uint8_t i = 0; i < scale; i++) {
@@ -1058,7 +1058,7 @@ bool qrcode_toFragmentsIcons(
const uint8_t dest_bit = dest_pixel % 32;
JADE_ASSERT(dest_elem < num_uints);
- bool paint = qrcode_getModule(qrcode, src_x, src_y);
+ uint32_t paint = qrcode_getModule(qrcode, src_x, src_y);
if (show_grid) {
// Invert if on a grid-line
const bool gridline = (dest_x == icon->width - 1) || (dest_x % scale == 0)
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.