What changed, and why it matters
This commit fixes a small but meaningful bug in how Jade, a hardware wallet, reads key paths from Bitcoin PSBT data. The code was passing the byte size of a buffer when it should have passed the number of 32-bit entries. Depending on how the underlying library interprets that value, this could lead to reading truncated paths, rejecting valid transactions, or possibly reading past the intended buffer. It is a defensive fix in security-sensitive parsing code, but the commit itself does not claim a specific exploitable vulnerability.
Treat as a security-hardening fix and include in the next firmware release. Review libwally's implementation of wally_map_keypath_get_item_path() to confirm whether the oversized count could cause an out-of-bounds write, and add regression tests covering long key paths in PSBTs. No independent CVE should be assigned without further vendor or researcher analysis.
Security signals we found
Buffer-size semantic mismatch in security-critical PSBT parsing
Use of sizeof() on uint32_t array where element count is expected
Code path involved in 2of3 multisig recovery key identification
Fix is narrowly scoped to a single function call pattern
Evidence from the diff
In main/utils/psbt.c, two calls to wally_map_keypath_get_item_path() were changed from passing sizeof(path) (total bytes) to sizeof(path)/sizeof(path[0]) (number of uint32_t elements). The function expects a path buffer measured in elements, not bytes. The previous code therefore reported a capacity four times larger than the actual array (on 32-bit platforms). The practical effect depends on libwally behavior: it may write at most the real buffer size, in which case the bug only caused path_len to be capped incorrectly and could reject valid paths; or it may trust the supplied count and write beyond the buffer. Either way, the fix corrects a type/semantic mismatch in key-path extraction used to identify Green 2of3 recovery keys and server paths.
Changed components
main/utils/psbt.cwally_map_keypath_get_item_path() callersGreen 2of3 recovery key iterationPSBT key iteration (key_iter_next)Inspect captured patch +2 / −2
diff --git a/main/utils/psbt.c b/main/utils/psbt.c
index fa8de89..4353454 100644
--- a/main/utils/psbt.c
+++ b/main/utils/psbt.c
@@ -100,7 +100,7 @@ static bool key_iter_get_green_2of3_recovery_key(const key_iter* iter, const str
const size_t start_idx = 1; // Ignore the Green server key at key index 0
for (size_t index = start_idx; index < keypaths->num_items; ++index) {
size_t path_len = 0;
- int ret = wally_map_keypath_get_item_path(keypaths, index, path, sizeof(path), &path_len);
+ int ret = wally_map_keypath_get_item_path(keypaths, index, path, sizeof(path) / sizeof(path[0]), &path_len);
JADE_WALLY_VERIFY(ret);
uint32_t subaccount = 0;
if (!is_potential_green_user_path(path, path_len, &subaccount) || subaccount != server_subaccount) {
@@ -137,7 +137,7 @@ bool key_iter_next(key_iter* iter)
// Didn't match any of the 3 keys present: check Green 2of3 parent recovery key
size_t path_len = 0;
uint32_t path[MAX_GASERVICE_PATH_LEN];
- ret = wally_map_keypath_get_item_path(keypaths, 0, path, sizeof(path), &path_len);
+ ret = wally_map_keypath_get_item_path(keypaths, 0, path, sizeof(path) / sizeof(path[0]), &path_len);
JADE_WALLY_VERIFY(ret);
uint32_t server_subaccount = 0;
if (is_potential_green_server_path(path, path_len, &server_subaccount) && server_subaccount != 0) {
Why this scored 42/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.