Skip to content

Commit dbba2fb

Browse files
Merge pull request #760 from LedgerHQ/security-review-1-16-0
Security Review 1.16.0
2 parents d7f34d1 + 1ff7182 commit dbba2fb

File tree

37 files changed

+201
-94
lines changed

37 files changed

+201
-94
lines changed

.github/workflows/cflite_cron.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ jobs:
3535
uses: google/clusterfuzzlite/actions/run_fuzzers@v1
3636
with:
3737
github-token: ${{ secrets.GITHUB_TOKEN }}
38-
fuzz-seconds: 300 # 5 minutes
38+
fuzz-seconds: 900 # 15 minutes
3939
mode: ${{ matrix.mode }}
4040
sanitizer: ${{ matrix.sanitizer }}

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) {
271271

272272
#ifdef HAVE_WEB3_CHECKS
273273
case INS_PROVIDE_TX_SIMULATION:
274-
sw = handleTxSimulation(cmd->p1, cmd->p2, cmd->data, cmd->lc, flags);
274+
sw = handle_tx_simulation(cmd->p1, cmd->p2, cmd->data, cmd->lc, flags);
275275
break;
276276
#endif
277277

src/mem.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,16 @@ void mem_reset(void) {
4343
* @return Allocated memory pointer; \ref NULL if not enough space left.
4444
*/
4545
void *mem_alloc(size_t size) {
46+
size_t new_idx;
47+
size_t free_size;
48+
49+
if (__builtin_add_overflow((size_t) mem_idx, size, &new_idx) ||
50+
__builtin_sub_overflow(sizeof(mem_buffer), (size_t) mem_rev_idx, &free_size)) {
51+
PRINTF("Error: overflow detected!\n");
52+
return NULL;
53+
}
4654
// Buffer exceeded
47-
if ((mem_idx + size) > (sizeof(mem_buffer) - mem_rev_idx)) {
55+
if (new_idx > free_size) {
4856
PRINTF("Error: mem_alloc(%u) failed!\n", size);
4957
return NULL;
5058
}
@@ -74,8 +82,16 @@ void mem_dealloc(size_t size) {
7482
* @return Allocated memory pointer; \ref NULL if not enough space left.
7583
*/
7684
void *mem_rev_alloc(size_t size) {
85+
size_t free_size;
86+
size_t new_rev_idx;
87+
88+
if (__builtin_add_overflow((size_t) mem_rev_idx, size, &new_rev_idx) ||
89+
__builtin_sub_overflow(sizeof(mem_buffer), new_rev_idx, &free_size)) {
90+
PRINTF("Error: overflow detected!\n");
91+
return NULL;
92+
}
7793
// Buffer exceeded
78-
if ((sizeof(mem_buffer) - (mem_rev_idx + size)) < mem_idx) {
94+
if (free_size < mem_idx) {
7995
PRINTF("Error: mem_rev_alloc(%u) failed!\n", size);
8096
return NULL;
8197
}

src/utils.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <ctype.h>
12
#include <string.h>
23
#include "utils.h"
34

@@ -29,3 +30,20 @@ void str_cpy_explicit_trunc(const char *src, size_t src_size, char *dst, size_t
2930
memcpy(&dst[off], trunc_marker, sizeof(trunc_marker));
3031
}
3132
}
33+
34+
/**
35+
* @brief Check the name is printable.
36+
*
37+
* @param[in] data buffer received
38+
* @param[in] name Name to check
39+
* @param[in] len Length of the name
40+
* @return True/False
41+
*/
42+
bool check_name(const uint8_t *name, uint16_t len) {
43+
for (uint16_t i = 0; i < len; i++) {
44+
if (!isprint(name[i])) {
45+
return false;
46+
}
47+
}
48+
return true;
49+
}

src/utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
#define UTILS_H_
33

44
#include <stdint.h>
5+
#include <stdbool.h>
56

67
#define SET_BIT(a) (1 << a)
78

89
void buf_shrink_expand(const uint8_t *src, size_t src_size, uint8_t *dst, size_t dst_size);
910
void str_cpy_explicit_trunc(const char *src, size_t src_size, char *dst, size_t dst_size);
11+
bool check_name(const uint8_t *name, uint16_t len);
1012

1113
#endif // !UTILS_H_

src_features/generic_tx_parser/calldata.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ static bool compress_chunk(s_calldata *calldata) {
8080
start_idx = strip_left;
8181
} else {
8282
strip_dir = CHUNK_STRIP_RIGHT;
83-
stripped_size = strip_right;
83+
stripped_size = CALLDATA_CHUNK_SIZE - strip_right;
8484
start_idx = 0;
8585
}
8686
chunk_info |= strip_dir << CHUNK_INFO_DIR_OFFSET;
@@ -171,10 +171,12 @@ bool calldata_append(const uint8_t *buffer, size_t size) {
171171
}
172172

173173
void calldata_cleanup(void) {
174-
mem_dealloc(g_calldata->chunks_size);
175-
mem_dealloc(sizeof(*g_calldata));
176-
g_calldata = NULL;
177-
mem_dealloc(g_calldata_alignment);
174+
if (g_calldata != NULL) {
175+
mem_dealloc(g_calldata->chunks_size);
176+
mem_dealloc(sizeof(*g_calldata));
177+
g_calldata = NULL;
178+
mem_dealloc(g_calldata_alignment);
179+
}
178180
}
179181

180182
static bool has_valid_calldata(const s_calldata *calldata) {

src_features/generic_tx_parser/gtp_field_table.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,15 @@ void field_table_cleanup(void) {
6161
bool add_to_field_table(e_param_type type, const char *key, const char *value) {
6262
int offset = 0;
6363
uint8_t *ptr;
64-
uint8_t key_len = strlen(key) + 1;
65-
uint16_t value_len = strlen(value) + 1;
64+
uint8_t key_len;
65+
uint16_t value_len;
6666

6767
if ((key == NULL) || (value == NULL)) {
6868
PRINTF("Error: NULL key/value!\n");
6969
return false;
7070
}
71+
key_len = strlen(key) + 1;
72+
value_len = strlen(value) + 1;
7173
PRINTF(">>> \"%s\": \"%s\"\n", key, value);
7274
if ((ptr = mem_alloc(sizeof(type) + sizeof(uint8_t) + sizeof(uint16_t) + key_len +
7375
value_len)) == NULL) {

src_features/provide_network_info/network_info.c

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#ifdef HAVE_DYNAMIC_NETWORKS
22

3-
#include <ctype.h>
43
#include "network_info.h"
54
#include "utils.h"
65
#include "read.h"
@@ -109,23 +108,6 @@ static bool handle_chain_id(const s_tlv_data *data, s_network_info_ctx *context)
109108
return true;
110109
}
111110

112-
/**
113-
* @brief Check the name is printable.
114-
*
115-
* @param[in] data buffer received
116-
* @param[in] name Name to check
117-
* @param[in] len Length of the name
118-
* @return True/False
119-
*/
120-
static bool check_name(const uint8_t *name, uint16_t len) {
121-
for (uint16_t i = 0; i < len; i++) {
122-
if (!isprint(name[i])) {
123-
return false;
124-
}
125-
}
126-
return true;
127-
}
128-
129111
/**
130112
* @brief Parse the NETWORK_NAME value.
131113
*
@@ -237,7 +219,7 @@ bool handle_network_info_struct(const s_tlv_data *data, s_network_info_ctx *cont
237219
break;
238220
default:
239221
PRINTF(TLV_TAG_ERROR_MSG, data->tag);
240-
ret = true; // TODO: shouldn't it be false ?
222+
ret = true;
241223
}
242224
if (ret && (data->tag != TAG_DER_SIGNATURE)) {
243225
hash_nbytes(data->raw, data->raw_size, (cx_hash_t *) &context->hash_ctx);

src_features/provide_tx_simulation/cmd_get_tx_simulation.c

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#ifdef HAVE_WEB3_CHECKS
22

3-
#include <ctype.h>
43
#include "cmd_get_tx_simulation.h"
54
#include "apdu_constants.h"
65
#include "hash_bytes.h"
@@ -108,23 +107,6 @@ tx_simulation_t TX_SIMULATION = {0};
108107
memmove((void *) field, data->value, data->length); \
109108
} while (0)
110109

111-
/**
112-
* @brief Check the name is printable.
113-
*
114-
* @param[in] data buffer received
115-
* @param[in] name Name to check
116-
* @param[in] len Length of the name
117-
* @return True/False
118-
*/
119-
static bool check_name(const uint8_t *name, uint16_t len) {
120-
for (uint16_t i = 0; i < len; i++) {
121-
if (!isprint(name[i])) {
122-
return false;
123-
}
124-
}
125-
return true;
126-
}
127-
128110
/**
129111
* @brief Parse the STRUCTURE_TYPE value.
130112
*
@@ -379,18 +361,25 @@ static bool verify_signature(s_tx_simu_ctx *context) {
379361
*
380362
* Check the mandatory fields are present
381363
*
382-
* @param[in] rcv_bit indicates received fields
364+
* @param[in] context TX Simu context
383365
* @return whether it was successful
384366
*/
385-
static bool verify_fields(uint32_t rcv_bit) {
367+
static bool verify_fields(s_tx_simu_ctx *context) {
386368
uint32_t expected_fields;
387369

388370
expected_fields = (1 << BIT_STRUCTURE_TYPE) | (1 << BIT_STRUCTURE_VERSION) |
389371
(1 << BIT_TX_HASH) | (1 << BIT_ADDRESS) | (1 << BIT_W3C_NORMALIZED_RISK) |
390372
(1 << BIT_W3C_NORMALIZED_CATEGORY) | (1 << BIT_W3C_TINY_URL) |
391373
(1 << BIT_W3C_SIMU_TYPE) | (1 << BIT_DER_SIGNATURE);
392374

393-
return ((rcv_bit & expected_fields) == expected_fields);
375+
if (context->simu->type == SIMU_TYPE_TRANSACTION) {
376+
expected_fields |= (1 << BIT_CHAIN_ID);
377+
}
378+
if (context->simu->type == SIMU_TYPE_TYPED_DATA) {
379+
expected_fields |= (1 << BIT_DOMAIN_HASH);
380+
}
381+
382+
return ((context->rcv_flags & expected_fields) == expected_fields);
394383
}
395384

396385
/**
@@ -411,8 +400,10 @@ static void print_simulation_info(s_tx_simu_ctx *context) {
411400
u64_to_string(context->simu->chain_id, chain_str, sizeof(chain_str));
412401
PRINTF("[TX SIMU] - ChainID: %s\n", chain_str);
413402
}
414-
PRINTF("[TX SIMU] - Risk: %d -> %s\n", context->simu->risk, getTxSimuRiskStr());
415-
PRINTF("[TX SIMU] - Category: %d -> %s\n", context->simu->category, getTxSimuCategoryStr());
403+
PRINTF("[TX SIMU] - Risk: %d -> %s\n", context->simu->risk, get_tx_simulation_risk_str());
404+
PRINTF("[TX SIMU] - Category: %d -> %s\n",
405+
context->simu->category,
406+
get_tx_simulation_category_str());
416407
PRINTF("[TX SIMU] - Provider Msg: %s\n", context->simu->provider_msg);
417408
PRINTF("[TX SIMU] - Tiny URL: %s\n", context->simu->tiny_url);
418409
}
@@ -489,14 +480,14 @@ static bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_fr
489480

490481
ctx.simu = &TX_SIMULATION;
491482
// Reset the structures
492-
explicit_bzero(&TX_SIMULATION, sizeof(tx_simulation_t));
483+
explicit_bzero(&TX_SIMULATION, sizeof(TX_SIMULATION));
493484
// Initialize the hash context
494485
cx_sha256_init(&ctx.hash_ctx);
495486

496487
parsing_ret = tlv_parse(payload, size, (f_tlv_data_handler) &handle_tx_simu_tlv, &ctx);
497488
if (to_free) mem_dealloc(size);
498-
if (!parsing_ret || !verify_fields(ctx.rcv_flags) || !verify_signature(&ctx)) {
499-
explicit_bzero(&TX_SIMULATION, sizeof(tx_simulation_t));
489+
if (!parsing_ret || !verify_fields(&ctx) || !verify_signature(&ctx)) {
490+
explicit_bzero(&TX_SIMULATION, sizeof(TX_SIMULATION));
500491
explicit_bzero(&ctx, sizeof(s_tx_simu_ctx));
501492
return false;
502493
}
@@ -513,7 +504,7 @@ static bool handle_tlv_payload(const uint8_t *payload, uint16_t size, bool to_fr
513504
*
514505
* @param[in] response_expected indicates if a response is expected
515506
*/
516-
void handleTxSimulationOptIn(bool response_expected) {
507+
void handle_tx_simulation_opt_in(bool response_expected) {
517508
if (N_storage.w3c_opt_in) {
518509
// Web3 Checks already Opt-In
519510
PRINTF("Web3 Checks already Opt-in!\n");
@@ -530,16 +521,17 @@ void handleTxSimulationOptIn(bool response_expected) {
530521
/**
531522
* @brief Handle Tx Simulation APDU.
532523
*
533-
* @param[in] p1 APDU parameter 1
524+
* @param[in] p1 APDU parameter 1 (indicates Data payload or Opt-In request)
525+
* @param[in] p2 APDU parameter 2 (indicates if the payload is the first chunk)
534526
* @param[in] data buffer received
535527
* @param[in] length of the buffer
536528
* @return APDU Response code
537529
*/
538-
uint16_t handleTxSimulation(uint8_t p1,
539-
uint8_t p2,
540-
const uint8_t *data,
541-
uint8_t length,
542-
unsigned int *flags) {
530+
uint16_t handle_tx_simulation(uint8_t p1,
531+
uint8_t p2,
532+
const uint8_t *data,
533+
uint8_t length,
534+
unsigned int *flags) {
543535
uint16_t sw = APDU_RESPONSE_INTERNAL_ERROR;
544536

545537
switch (p1) {
@@ -558,7 +550,7 @@ uint16_t handleTxSimulation(uint8_t p1,
558550
break;
559551
case 0x01:
560552
// TX Simulation Opt-In
561-
handleTxSimulationOptIn(true);
553+
handle_tx_simulation_opt_in(true);
562554
*flags |= IO_ASYNCH_REPLY;
563555
sw = APDU_NO_RESPONSE;
564556
break;
@@ -574,8 +566,8 @@ uint16_t handleTxSimulation(uint8_t p1,
574566
* @brief Clear the TX Simulation parameters.
575567
*
576568
*/
577-
void clearTxSimulation(void) {
578-
explicit_bzero(&TX_SIMULATION, sizeof(tx_simulation_t));
569+
void clear_tx_simulation(void) {
570+
explicit_bzero(&TX_SIMULATION, sizeof(TX_SIMULATION));
579571
}
580572

581573
/**
@@ -585,7 +577,7 @@ void clearTxSimulation(void) {
585577
* @param[in] checkFromAddr flag to check the FROM address
586578
* @return whether it was successful
587579
*/
588-
bool checkTxSimulationParams(bool checkTxHash, bool checkFromAddr) {
580+
bool check_tx_simulation_params(bool checkTxHash, bool checkFromAddr) {
589581
uint8_t msg_sender[ADDRESS_LENGTH] = {0};
590582
uint64_t chain_id = get_tx_chain_id();
591583
uint8_t *hash = NULL;
@@ -703,13 +695,13 @@ bool checkTxSimulationParams(bool checkTxHash, bool checkFromAddr) {
703695
* @param[in] checkTxHash flag to check the TX_HASH
704696
* @param[in] checkFromAddr flag to check the FROM address
705697
*/
706-
void setTxSimuWarning(nbgl_warning_t *p_warning, bool checkTxHash, bool checkFromAddr) {
698+
void set_tx_simulation_warning(nbgl_warning_t *p_warning, bool checkTxHash, bool checkFromAddr) {
707699
if (!N_storage.w3c_enable) {
708700
// W3Checks disabled
709701
return;
710702
}
711703
// W3Checks enabled => Verify parameters of the Transaction
712-
checkTxSimulationParams(checkTxHash, checkFromAddr);
704+
check_tx_simulation_params(checkTxHash, checkFromAddr);
713705
switch (TX_SIMULATION.risk) {
714706
case RISK_UNKNOWN:
715707
p_warning->predefinedSet |= SET_BIT(W3C_ISSUE_WARN);
@@ -727,7 +719,7 @@ void setTxSimuWarning(nbgl_warning_t *p_warning, bool checkTxHash, bool checkFro
727719
break;
728720
}
729721
p_warning->reportProvider = PIC(TX_SIMULATION.partner);
730-
p_warning->providerMessage = getTxSimuCategoryStr();
722+
p_warning->providerMessage = get_tx_simulation_category_str();
731723
p_warning->reportUrl = PIC(TX_SIMULATION.tiny_url);
732724
}
733725

@@ -736,7 +728,7 @@ void setTxSimuWarning(nbgl_warning_t *p_warning, bool checkTxHash, bool checkFro
736728
*
737729
* @return risk as a string
738730
*/
739-
const char *getTxSimuRiskStr(void) {
731+
const char *get_tx_simulation_risk_str(void) {
740732
switch (TX_SIMULATION.risk) {
741733
case RISK_UNKNOWN:
742734
return "UNKNOWN (W3C Issue)";
@@ -757,7 +749,7 @@ const char *getTxSimuRiskStr(void) {
757749
*
758750
* @return category string
759751
*/
760-
const char *getTxSimuCategoryStr(void) {
752+
const char *get_tx_simulation_category_str(void) {
761753
// Unknown category string
762754
switch (TX_SIMULATION.risk) {
763755
case RISK_UNKNOWN:

0 commit comments

Comments
 (0)