|
| 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