Skip to content

Commit 40d5d8a

Browse files
Merge pull request #949 from LedgerHQ/bbo/new_fuzzers
Add fuzzing harnesses
2 parents 59bb2cb + 2cb2075 commit 40d5d8a

File tree

17 files changed

+1232
-12
lines changed

17 files changed

+1232
-12
lines changed

src/features/generic_tx_parser/gtp_field.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ static bool handle_param_constraint(const s_tlv_data *data, s_field_ctx *context
124124
PRINTF("Error: CONSTRAINT present but VISIBLE is not MUST_BE or IF_NOT_IN!\n");
125125
return false;
126126
}
127+
if (data->length == 0 || data->value == NULL) {
128+
PRINTF("Error: Empty constraint value!\n");
129+
return false;
130+
}
127131
// Allocate new constraint node
128132
s_field_constraint *node = NULL;
129133
if (mem_buffer_allocate((void **) &node, sizeof(s_field_constraint)) == false) {

src/plugins/erc1155/erc1155_ui.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ static void set_batch_transfer_ui(ethQueryContractUI_t *msg, erc1155_context_t *
134134
void handle_query_contract_ui_1155(ethQueryContractUI_t *msg) {
135135
erc1155_context_t *context = (erc1155_context_t *) msg->pluginContext;
136136

137+
if (msg->item1 == NULL) {
138+
msg->result = ETH_PLUGIN_RESULT_ERROR;
139+
return;
140+
}
141+
137142
msg->result = ETH_PLUGIN_RESULT_OK;
138143
switch (context->selectorIndex) {
139144
case SET_APPROVAL_FOR_ALL:

src/plugins/eth2/eth2_plugin.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,7 @@ void eth2_plugin_call(eth_plugin_msg_t message, void *parameters) {
103103

104104
case 4 + (32 * 5): // deposit pubkey 1
105105
{
106-
// Copy the first 32 bytes.
107-
memcpy(context->deposit_address,
108-
msg->parameter,
109-
sizeof(context->deposit_address));
106+
memcpy(context->deposit_address, msg->parameter, PARAMETER_LENGTH);
110107
msg->result = ETH_PLUGIN_RESULT_OK;
111108
break;
112109
}
@@ -141,7 +138,7 @@ void eth2_plugin_call(eth_plugin_msg_t message, void *parameters) {
141138

142139
case 4 + (32 * 8): // withdrawal credentials
143140
{
144-
uint8_t tmp[48];
141+
uint8_t tmp[48] = {0};
145142
uint32_t withdrawalKeyPath[4];
146143
withdrawalKeyPath[0] = WITHDRAWAL_KEY_PATH_1;
147144
withdrawalKeyPath[1] = WITHDRAWAL_KEY_PATH_2;

tests/fuzzing/harness/fuzz_eip7702.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#include <setjmp.h>
12
#include "fuzz_utils.h"
3+
#include "mocks.h"
24

35
int fuzzEIP7702(const uint8_t *data, size_t size) {
46
size_t offset = 0;
@@ -17,11 +19,10 @@ int fuzzEIP7702(const uint8_t *data, size_t size) {
1719
return 0;
1820
}
1921

20-
/* Main fuzzing handler called by libfuzzer */
2122
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
2223
init_fuzzing_environment();
24+
if (sigsetjmp(fuzz_exit_jump_ctx.jmp_buf, 1)) return 0;
2325

24-
// Run the harness
2526
fuzzEIP7702(data, size);
2627

2728
return 0;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "fuzz_utils.h"
2+
#include "cmd_get_gating.h"
3+
4+
int fuzzGating(const uint8_t *data, size_t size) {
5+
size_t offset = 0;
6+
size_t len = 0;
7+
uint8_t p1;
8+
uint8_t p2;
9+
10+
while (size - offset > 4) {
11+
if (data[offset++] == 0) break;
12+
p1 = data[offset++];
13+
p2 = data[offset++];
14+
len = data[offset++];
15+
if (size - offset < len) return 0;
16+
if (handle_gating(p1, p2, data + offset, len) != SWO_SUCCESS) return 0;
17+
offset += len;
18+
}
19+
set_gating_warning();
20+
return 0;
21+
}
22+
23+
/* Main fuzzing handler called by libfuzzer */
24+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
25+
init_fuzzing_environment();
26+
27+
fuzzGating(data, size);
28+
29+
return 0;
30+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Fuzzing harness for the EIP-7002 internal plugin (withdrawal requests)
3+
*/
4+
5+
#include "fuzz_utils.h"
6+
#include "eip7002_plugin.h"
7+
8+
// Buffer sizes for UI queries
9+
#define NAME_LENGTH 32
10+
#define VERSION_LENGTH 32
11+
#define TITLE_LENGTH 32
12+
#define MSG_LENGTH 79
13+
14+
static int fuzz_eip7002_plugin(const uint8_t *data, size_t size) {
15+
ethPluginInitContract_t init_contract = {0};
16+
ethPluginProvideParameter_t provide_param = {0};
17+
ethPluginFinalize_t finalize = {0};
18+
ethPluginProvideInfo_t provide_info = {0};
19+
ethQueryContractID_t query_id = {0};
20+
ethQueryContractUI_t query_ui = {0};
21+
txContent_t content = {0};
22+
23+
uint8_t plugin_context[PLUGIN_CONTEXT_SIZE] = {0};
24+
25+
const uint8_t address[ADDRESS_LENGTH] = {0};
26+
27+
char name[NAME_LENGTH] = {0};
28+
char version[VERSION_LENGTH] = {0};
29+
char title[TITLE_LENGTH] = {0};
30+
char msg[MSG_LENGTH] = {0};
31+
32+
extraInfo_t item1 = {0};
33+
extraInfo_t item2 = {0};
34+
35+
// Need at least selector (4 bytes)
36+
if (size < SELECTOR_SIZE) {
37+
return 0;
38+
}
39+
40+
// Initialize content from fuzzed data if available
41+
if (size >= SELECTOR_SIZE + sizeof(txContent_t)) {
42+
memcpy(&content, data + SELECTOR_SIZE, sizeof(txContent_t));
43+
}
44+
45+
// Setup init contract
46+
init_contract.interfaceVersion = ETH_PLUGIN_INTERFACE_VERSION_LATEST;
47+
init_contract.selector = data;
48+
init_contract.txContent = &content;
49+
init_contract.pluginContext = plugin_context;
50+
init_contract.pluginContextLength = sizeof(plugin_context);
51+
init_contract.dataSize = size;
52+
53+
eip7002_plugin_call(ETH_PLUGIN_INIT_CONTRACT, &init_contract);
54+
if (init_contract.result != ETH_PLUGIN_RESULT_OK) {
55+
return 0;
56+
}
57+
58+
// Provide parameters
59+
size_t offset = SELECTOR_SIZE;
60+
if (size >= SELECTOR_SIZE + sizeof(txContent_t)) {
61+
offset += sizeof(txContent_t);
62+
}
63+
64+
while (size - offset >= PARAMETER_LENGTH) {
65+
provide_param.parameter = data + offset;
66+
provide_param.parameterOffset = offset;
67+
provide_param.parameter_size = PARAMETER_LENGTH;
68+
provide_param.pluginContext = plugin_context;
69+
provide_param.txContent = &content;
70+
71+
eip7002_plugin_call(ETH_PLUGIN_PROVIDE_PARAMETER, &provide_param);
72+
if (provide_param.result != ETH_PLUGIN_RESULT_OK) {
73+
return 0;
74+
}
75+
offset += PARAMETER_LENGTH;
76+
}
77+
78+
// Handle remaining bytes if any (last parameter may be smaller)
79+
if (size - offset > 0) {
80+
provide_param.parameter = data + offset;
81+
provide_param.parameterOffset = offset;
82+
provide_param.parameter_size = size - offset;
83+
provide_param.pluginContext = plugin_context;
84+
provide_param.txContent = &content;
85+
86+
eip7002_plugin_call(ETH_PLUGIN_PROVIDE_PARAMETER, &provide_param);
87+
if (provide_param.result != ETH_PLUGIN_RESULT_OK) {
88+
return 0;
89+
}
90+
}
91+
92+
// Finalize
93+
finalize.pluginContext = plugin_context;
94+
finalize.address = address;
95+
finalize.txContent = &content;
96+
97+
eip7002_plugin_call(ETH_PLUGIN_FINALIZE, &finalize);
98+
if (finalize.result != ETH_PLUGIN_RESULT_OK) {
99+
return 0;
100+
}
101+
102+
// Provide token info if requested
103+
if (finalize.tokenLookup1 || finalize.tokenLookup2) {
104+
provide_info.pluginContext = plugin_context;
105+
provide_info.txContent = &content;
106+
if (finalize.tokenLookup1) {
107+
provide_info.item1 = &item1;
108+
}
109+
if (finalize.tokenLookup2) {
110+
provide_info.item2 = &item2;
111+
}
112+
113+
eip7002_plugin_call(ETH_PLUGIN_PROVIDE_INFO, &provide_info);
114+
if (provide_info.result != ETH_PLUGIN_RESULT_OK &&
115+
provide_info.result != ETH_PLUGIN_RESULT_FALLBACK) {
116+
return 0;
117+
}
118+
}
119+
120+
// Query contract ID
121+
query_id.pluginContext = plugin_context;
122+
query_id.txContent = &content;
123+
query_id.name = name;
124+
query_id.nameLength = sizeof(name);
125+
query_id.version = version;
126+
query_id.versionLength = sizeof(version);
127+
128+
eip7002_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, &query_id);
129+
if (query_id.result != ETH_PLUGIN_RESULT_OK) {
130+
return 0;
131+
}
132+
133+
// Query contract UI for each screen
134+
uint8_t total_screens = finalize.numScreens + provide_info.additionalScreens;
135+
for (uint8_t i = 0; i < total_screens; i++) {
136+
query_ui.pluginContext = plugin_context;
137+
query_ui.txContent = &content;
138+
query_ui.title = title;
139+
query_ui.titleLength = sizeof(title);
140+
query_ui.msg = msg;
141+
query_ui.msgLength = sizeof(msg);
142+
query_ui.screenIndex = i;
143+
144+
eip7002_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_UI, &query_ui);
145+
if (query_ui.result != ETH_PLUGIN_RESULT_OK) {
146+
return 0;
147+
}
148+
}
149+
150+
return 0;
151+
}
152+
153+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
154+
init_fuzzing_environment();
155+
fuzz_eip7002_plugin(data, size);
156+
return 0;
157+
}

0 commit comments

Comments
 (0)