assets: improve precision checking
What changed, and why it matters
This commit tightens how Blockstream Jade handles the 'precision' value in asset contracts for Liquid assets. Previously, a very large precision value could be accepted and later used to read past the end of an internal lookup table (POW_10), which could cause a crash or undefined behavior when displaying or signing a transaction. The patch caps precision at 8 and adds a compile-time check that the lookup table is sized correctly for that cap.
Treat this as a security hardening fix with likely crash/DoS relevance. Review whether any other call sites consume asset->precision without bounds checks, and verify that firmware update distribution covers devices that may process untrusted Liquid asset contracts.
Security signals we found
Out-of-bounds read mitigation: bounds-checking of user-controlled precision before indexing fixed-size POW_10 array
Integer truncation safety: explicit cast of validated precision to uint8_t after range check
Compile-time invariant enforcement: JADE_STATIC_ASSERT ties array size to ASSET_PRECISION_MAX
Input validation: parsing now rejects asset contracts with precision > 8 and logs the error
Potential DoS/crash vector in transaction signing/display path addressed
Evidence from the diff
The firmware parses asset contract CBOR containing an optional ‘precision’ field. Before the patch, rpc_get_sizet_or() returned a size_t that was stored directly into asset->precision (a uint8_t) without range validation. The UI/signing code then indexes POW_10[asset_info->precision], an array of only 9 entries (0..8). A malicious or malformed contract with precision >= 9 would cause an out-of-bounds array read. The patch introduces ASSET_PRECISION_MAX 8, rejects larger values during parsing, casts the accepted value to uint8_t, and adds a JADE_STATIC_ASSERT ensuring POW_10 has exactly ASSET_PRECISION_MAX+1 entries. It also includes assets.h in sign_tx.c so the constant is visible there.
Changed components
main/assets.c - asset contract parsing (assets_get_allocate)main/assets.h - new ASSET_PRECISION_MAX constantmain/ui/sign_tx.c - asset scale factor lookup (get_asset_scale_factor)Inspect captured patch +13 / −1
diff --git a/main/assets.c b/main/assets.c
index f16fe2c..46102f5 100644
--- a/main/assets.c
+++ b/main/assets.c
@@ -169,7 +169,13 @@ bool assets_get_allocate(const char* field, const CborValue* value, asset_info_t
}
// "precision" field is optional in the asset contract and defaults to 0
- asset->precision = rpc_get_sizet_or("precision", &contract, 0);
+ const size_t precision = rpc_get_sizet_or("precision", &contract, 0);
+ if (precision > ASSET_PRECISION_MAX) {
+ JADE_LOGE("Invalid asset precision %zu", precision);
+ free(assets);
+ return false;
+ }
+ asset->precision = (uint8_t)precision;
}
CborError err = cbor_value_advance(&arrayItem);
diff --git a/main/assets.h b/main/assets.h
index 1b715bf..927866c 100644
--- a/main/assets.h
+++ b/main/assets.h
@@ -5,6 +5,9 @@
#include "utils/cbor_rpc.h"
#include "utils/network.h"
+// Maximum allowed precision for supported assets
+#define ASSET_PRECISION_MAX 8
+
// NOTE: strings here may not be nul-terminated as may directly reference message fields
typedef struct _asset_info {
const char* asset_id;
diff --git a/main/ui/sign_tx.c b/main/ui/sign_tx.c
index 3b5833c..73f2789 100644
--- a/main/ui/sign_tx.c
+++ b/main/ui/sign_tx.c
@@ -4,6 +4,7 @@
#include <wally_elements.h>
#include <wally_transaction.h>
+#include "../assets.h"
#include "../button_events.h"
#include "../jade_assert.h"
#include "../jade_wally_verify.h"
@@ -33,6 +34,8 @@ static const uint32_t POW_10[9] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10
static uint32_t get_asset_scale_factor(const asset_info_t* asset_info)
{
+ // Sanity check to ensure the scale factor array is large enough to hold all possible precisions
+ JADE_STATIC_ASSERT(sizeof(POW_10) / sizeof(POW_10[0]) == ASSET_PRECISION_MAX + 1);
JADE_ASSERT(asset_info && asset_info->precision < sizeof(POW_10) / sizeof(POW_10[0]));
return POW_10[asset_info->precision];
}
Why this scored 59/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.