rpc: annotate functions returning error codes to ensure they are checked
What changed, and why it matters
This commit adds compiler annotations (WARN_UNUSED_RESULT) to functions that return success/failure codes, so the compiler will warn if a caller ignores the result. It also fixes a few places where return values were being ignored, replacing them with safer default-value helpers. The changes reduce the chance that a malformed or missing RPC parameter silently causes wrong behavior, such as using an empty wallet path or a false success flag. This is a defensive hardening patch rather than a confirmed exploit fix.
Treat as a hardening improvement. Review other callers of the annotated RPC helpers to ensure no additional ignored-return warnings remain, and consider enabling -Werror=unused-result in CI. No urgent user action is required absent a disclosed exploit chain.
Security signals we found
Functions returning error/success codes annotated with WARN_UNUSED_RESULT
Callers now check return values of rpc_get_bip32_path() and rpc_get_boolean()
Use of default-value helpers (rpc_get_boolean_or) to ensure predictable behavior on missing fields
Potential silent bypass of user confirmation or path validation before patch
Evidence from the diff
The patch annotates CBOR RPC helper functions with WARN_UNUSED_RESULT, forcing callers to handle their boolean return codes. It updates four call sites: (1) get_master_blinding_key.c now uses rpc_get_boolean_or() with an explicit default instead of ignoring the return of rpc_get_boolean(); (2) get_receive_address.c now rejects the request if rpc_get_bip32_path() fails or returns an empty path; (3) sign_utils.c uses rpc_get_boolean_or() for the is_partial flag; (4) usbmode.c uses rpc_get_boolean_or() when reading an OTA result flag. These changes prevent silent failures when expected fields are missing or malformed.
Changed components
main/process/get_master_blinding_key.cmain/process/get_receive_address.cmain/process/sign_utils.cmain/usbhmsc/usbmode.cmain/utils/cbor_rpc.hInspect captured patch +24 / −21
diff --git a/main/process/get_master_blinding_key.c b/main/process/get_master_blinding_key.c
index 4a46252..fb59630 100644
--- a/main/process/get_master_blinding_key.c
+++ b/main/process/get_master_blinding_key.c
@@ -20,16 +20,17 @@ void get_master_blinding_key_process(void* process_ptr)
if (keychain_get_confirm_export_blinding_key()) {
// Optional field to suppress asking user for permission and instead
// error in the cases where we would normally need to ask the user.
- bool onlyIfSilent = false;
+ bool only_if_silent = false;
CborValue params;
const CborError cberr = cbor_value_map_find_value(&process->ctx.value, CBOR_RPC_TAG_PARAMS, ¶ms);
if (cberr == CborNoError || cbor_value_is_valid(¶ms) || cbor_value_is_map(¶ms)) {
- rpc_get_boolean("only_if_silent", ¶ms, &onlyIfSilent);
+ // This field is optional and defaults to false if not present (initialized above)
+ only_if_silent = rpc_get_boolean_or("only_if_silent", ¶ms, false);
}
const char* question[] = { "Export master", "blinding key?" };
- if (onlyIfSilent || !await_yesno_activity("Blinding Key", question, 2, true, "blkstrm.com/blindingkey")) {
+ if (only_if_silent || !await_yesno_activity("Blinding Key", question, 2, true, "blkstrm.com/blindingkey")) {
JADE_LOGW("User declined to export master blinding key");
jade_process_reject_message(
process, CBOR_RPC_USER_CANCELLED, "User declined to export master blinding key");
diff --git a/main/process/get_receive_address.c b/main/process/get_receive_address.c
index 9d4c57b..f307293 100644
--- a/main/process/get_receive_address.c
+++ b/main/process/get_receive_address.c
@@ -180,8 +180,7 @@ void get_receive_address_process(void* process_ptr)
}
} else if (is_singlesig(script_variant)) {
// For single-sig the path is explicit in the params
- rpc_get_bip32_path("path", ¶ms, path, max_path_len, &path_len);
- if (path_len == 0) {
+ if (!rpc_get_bip32_path("path", ¶ms, path, max_path_len, &path_len) || path_len == 0) {
jade_process_reject_message(
process, CBOR_RPC_BAD_PARAMETERS, "Failed to extract valid path from parameters");
goto cleanup;
diff --git a/main/process/sign_utils.c b/main/process/sign_utils.c
index 25ee20e..22d0084 100644
--- a/main/process/sign_utils.c
+++ b/main/process/sign_utils.c
@@ -222,8 +222,8 @@ bool params_additional_info(jade_process_t* process, CborValue* params, const st
rpc_get_asset_summary(process, "wallet_input_summary", &additional_info, in_sums, num_in_sums);
rpc_get_asset_summary(process, "wallet_output_summary", &additional_info, out_sums, num_out_sums);
- // 'partial' flag (defaults to false, set above)
- rpc_get_boolean("is_partial", &additional_info, is_partial);
+ // 'partial' flag (defaults to false, initially also defaulted above)
+ *is_partial = rpc_get_boolean_or("is_partial", &additional_info, false);
// Tx Type
if (!rpc_get_txtype(process, &additional_info, txtype)) {
diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c
index 6680e83..7a041e5 100644
--- a/main/usbhmsc/usbmode.c
+++ b/main/usbhmsc/usbmode.c
@@ -536,7 +536,8 @@ static bool handle_ota_reply(const uint8_t* msg, const size_t len, void* ctx)
if (cberr != CborNoError || !rpc_message_valid(&message)) {
JADE_LOGE("Invalid cbor message");
} else {
- rpc_get_boolean("result", &message, ok);
+ // Optional field, but we expect it to be present and true for a positive response
+ *ok = rpc_get_boolean_or("result", &message, false);
}
// We return true in all cases to indicate that a message was received
diff --git a/main/utils/cbor_rpc.h b/main/utils/cbor_rpc.h
index e546104..05d302f 100644
--- a/main/utils/cbor_rpc.h
+++ b/main/utils/cbor_rpc.h
@@ -29,8 +29,8 @@
// Plenty for green wallets, bip44 etc. with plenty to spare.
#define MAX_PATH_LEN 16
-bool cbor_print_error_for(const char* id, int code, const char* message, const uint8_t* data, size_t datalen,
- uint8_t* buffer, size_t buffer_len, size_t* towrite);
+WARN_UNUSED_RESULT bool cbor_print_error_for(const char* id, int code, const char* message, const uint8_t* data,
+ size_t datalen, uint8_t* buffer, size_t buffer_len, size_t* towrite);
// Parse input
bool rpc_message_valid(const CborValue* message);
@@ -49,20 +49,22 @@ void rpc_get_string(const char* field, size_t max, const CborValue* value, char*
void rpc_get_raw_bytes_ptr(const CborValue* value, const uint8_t** data, size_t* size);
void rpc_get_bytes_ptr(const char* field, const CborValue* value, const uint8_t** data, size_t* size);
void rpc_get_bytes(const char* field, size_t max, const CborValue* value, uint8_t* data, size_t* written);
-bool rpc_get_n_bytes(const char* field, const CborValue* value, size_t expected_size, uint8_t* data);
-bool rpc_get_sizet(const char* field, const CborValue* value, size_t* res);
-size_t rpc_get_sizet_or(const char* field, const CborValue* value, size_t default_value);
-bool rpc_get_uint64_t(const char* field, const CborValue* value, uint64_t* res);
-uint64_t rpc_get_uint64_t_or(const char* field, const CborValue* value, uint64_t default_value);
-bool rpc_get_boolean(const char* field, const CborValue* value, bool* res);
-bool rpc_get_boolean_or(const char* field, const CborValue* value, bool default_value);
-bool rpc_get_bip32_path(
+WARN_UNUSED_RESULT bool rpc_get_n_bytes(const char* field, const CborValue* value, size_t expected_size, uint8_t* data);
+WARN_UNUSED_RESULT bool rpc_get_sizet(const char* field, const CborValue* value, size_t* res);
+WARN_UNUSED_RESULT size_t rpc_get_sizet_or(const char* field, const CborValue* value, size_t default_value);
+WARN_UNUSED_RESULT bool rpc_get_uint64_t(const char* field, const CborValue* value, uint64_t* res);
+WARN_UNUSED_RESULT uint64_t rpc_get_uint64_t_or(const char* field, const CborValue* value, uint64_t default_value);
+WARN_UNUSED_RESULT bool rpc_get_boolean(const char* field, const CborValue* value, bool* res);
+WARN_UNUSED_RESULT bool rpc_get_boolean_or(const char* field, const CborValue* value, bool default_value);
+
+WARN_UNUSED_RESULT bool rpc_get_bip32_path(
const char* field, const CborValue* value, uint32_t* path_ptr, size_t max_path_len, size_t* written);
-bool rpc_get_bip32_path_from_value(CborValue* value, uint32_t* path_ptr, size_t max_path_len, size_t* written);
+WARN_UNUSED_RESULT bool rpc_get_bip32_path_from_value(
+ CborValue* value, uint32_t* path_ptr, size_t max_path_len, size_t* written);
-bool rpc_get_array(const char* field, const CborValue* value, CborValue* result);
-bool rpc_get_map(const char* field, const CborValue* value, CborValue* result);
+WARN_UNUSED_RESULT bool rpc_get_array(const char* field, const CborValue* value, CborValue* result);
+WARN_UNUSED_RESULT bool rpc_get_map(const char* field, const CborValue* value, CborValue* result);
// Build response objects
void rpc_init_cbor(CborEncoder* container, const char* id, size_t id_len);
Why this scored 48/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.