@@ -317,19 +317,61 @@ ExecutionResult expmod_execute(
317
317
const uint8_t * input, size_t input_size, uint8_t * output, size_t output_size) noexcept
318
318
{
319
319
static constexpr auto LEN_SIZE = sizeof (intx::uint256);
320
+ static constexpr auto HEADER_SIZE = 3 * LEN_SIZE;
321
+ static constexpr auto LEN32_OFF = LEN_SIZE - sizeof (uint32_t );
320
322
321
323
// The output size equal to the modulus size.
324
+ const auto mod_len = output_size;
325
+
322
326
// Handle short incomplete input up front. The answer is 0 of the length of the modulus.
323
- if (output_size == 0 || input_size <= 3 * LEN_SIZE) [[unlikely]]
327
+ if (input_size <= HEADER_SIZE) [[unlikely]]
328
+ {
329
+ std::fill_n (output, output_size, 0 );
330
+ return {EVMC_SUCCESS, output_size};
331
+ }
332
+
333
+ const auto base_len = intx::be::unsafe::load<uint32_t >(&input[LEN32_OFF]);
334
+ const auto exp_len = intx::be::unsafe::load<uint32_t >(&input[LEN_SIZE + LEN32_OFF]);
335
+ assert (intx::be::unsafe::load<uint32_t >(&input[2 * LEN_SIZE + LEN32_OFF]) == mod_len);
336
+
337
+ const bytes_view payload{input + HEADER_SIZE, input_size - HEADER_SIZE};
338
+ const size_t mod_off = base_len + exp_len; // Cannot overflow if gas cost computed before.
339
+ const auto mod_explicit = payload.substr (std::min (mod_off, payload.size ()), mod_len);
340
+
341
+ // Handle the mod being zero early.
342
+ // This serves two purposes:
343
+ // - bigint libraries don't like zero modulus because division by 0 is not well-defined,
344
+ // - having non-zero modulus guarantees that base and exp aren't out-of-bounds.
345
+ if (mod_explicit.find_first_not_of (uint8_t {0 }) == bytes_view::npos) [[unlikely]]
324
346
{
347
+ // The modulus is zero, so the result is zero.
325
348
std::fill_n (output, output_size, 0 );
326
349
return {EVMC_SUCCESS, output_size};
327
350
}
328
351
352
+ const auto mod_requires_padding = mod_explicit.size () != mod_len;
353
+ if (mod_requires_padding) [[unlikely]]
354
+ {
355
+ // The modulus is the last argument and some of its bytes may be missing and be implicitly
356
+ // zero. In this case, copy the explict modulus bytes to the output buffer and pad the rest
357
+ // with zeroes. The output buffer is guaranteed to have exactly the modulus size.
358
+ const auto [_, output_p] = std::ranges::copy (mod_explicit, output);
359
+ std::fill (output_p, output + output_size, 0 );
360
+ }
361
+
362
+ const auto base = payload.substr (0 , base_len);
363
+ const auto exp = payload.substr (base_len, exp_len);
364
+ const auto mod = mod_requires_padding ? bytes_view{output, mod_len} : mod_explicit;
365
+
329
366
#ifdef EVMONE_PRECOMPILES_SILKPRE
367
+ (void )base;
368
+ (void )exp ;
369
+ (void )mod;
370
+ // For Silkpre use the raw input for compatibility.
330
371
return silkpre_expmod_execute (input, input_size, output, output_size);
331
372
#else
332
- return expmod_stub (input, input_size, output, output_size);
373
+ expmod_stub (base, exp , mod, output);
374
+ return {EVMC_SUCCESS, mod.size ()};
333
375
#endif
334
376
}
335
377
0 commit comments