Skip to content

Commit

Permalink
fix fuzz 18: modexp - handling of infinitely right-padded inputs lead…
Browse files Browse the repository at this point in the history
…ing to buffer overflow or stack overflow (#264)
  • Loading branch information
mratsim authored Sep 6, 2023
1 parent 4e0ca43 commit c85ffb0
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 10 deletions.
49 changes: 39 additions & 10 deletions constantine/ethereum_evm_precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func eth_evm_ecadd*(r: var openArray[byte], inputs: openarray[byte]): CttEVMStat

# Auto-pad with zero
var padded: array[128, byte]
padded.rawCopy(0, inputs, 0, min(inputs.len, 128))
padded.rawCopy(0, inputs, 0, min(inputs.len, padded.len))

var P{.noInit.}, Q{.noInit.}, R{.noInit.}: ECP_ShortW_Jac[Fp[BN254_Snarks], G1]

Expand Down Expand Up @@ -173,8 +173,8 @@ func eth_evm_ecmul*(r: var openArray[byte], inputs: openarray[byte]): CttEVMStat
return cttEVM_InvalidOutputSize

# Auto-pad with zero
var padded: array[128, byte]
padded.rawCopy(0, inputs, 0, min(inputs.len, 128))
var padded: array[96, byte]
padded.rawCopy(0, inputs, 0, min(inputs.len, padded.len))

var P{.noInit.}: ECP_ShortW_Jac[Fp[BN254_Snarks], G1]

Expand Down Expand Up @@ -401,10 +401,15 @@ func eth_evm_modexp*(r: var openArray[byte], inputs: openArray[byte]): CttEVMSta

# Input parse sizes
# -----------------

# Auto-pad with zero
var paddedLengths: array[96, byte]
paddedLengths.rawCopy(0, inputs, 0, min(inputs.len, paddedLengths.len))

let
bL = BigInt[256].unmarshal(inputs.toOpenArray(0, 31), bigEndian)
eL = BigInt[256].unmarshal(inputs.toOpenArray(32, 63), bigEndian)
mL = BigInt[256].unmarshal(inputs.toOpenArray(64, 95), bigEndian)
bL = BigInt[256].unmarshal(paddedLengths.toOpenArray(0, 31), bigEndian)
eL = BigInt[256].unmarshal(paddedLengths.toOpenArray(32, 63), bigEndian)
mL = BigInt[256].unmarshal(paddedLengths.toOpenArray(64, 95), bigEndian)

maxSize = BigInt[256].fromUint(high(uint)) # A CPU can only address up to high(uint)

Expand Down Expand Up @@ -433,13 +438,20 @@ func eth_evm_modexp*(r: var openArray[byte], inputs: openArray[byte]): CttEVMSta

# Special cases
# ----------------------
if paddedLengths.len + baseByteLen + exponentByteLen >= inputs.len:
# Modulus value is in the infinitely right padded zeros input, hence is zero.
r.setZero()
return cttEVM_Success

if modulusByteLen == 0:
r.setZero()
return cttEVM_Success

if exponentByteLen == 0:
r.setZero()
r[r.len-1] = byte 1 # 0^0 = 1 and x^0 = 1
return cttEVM_Success

if baseByteLen == 0:
r.setZero()
return cttEVM_Success
Expand All @@ -448,25 +460,42 @@ func eth_evm_modexp*(r: var openArray[byte], inputs: openArray[byte]): CttEVMSta
# ---------------------

# Inclusive stops
let baseStart = 96
# Due to special-case checks and early returns,
# only the modulus can require right-padding with zeros here
# inputs[expStop] cannot buffer overflow
let baseStart = paddedLengths.len
let baseStop = baseStart+baseByteLen-1
let expStart = baseStop+1
let expStop = expStart+exponentByteLen-1
let modStart = expStop+1
let modStop = modStart+modulusByteLen-1

# We assume that gas checks prevent numbers too big for stack allocation.
var baseBuf = allocStackArray(SecretWord, baseWordLen)
var modulusBuf = allocStackArray(SecretWord, modulusWordLen)
var outputBuf = allocStackArray(SecretWord, modulusWordLen)

template base(): untyped = baseBuf.toOpenArray(0, baseWordLen-1)
template exponent(): untyped = inputs.toOpenArray(expStart, expStop)
template modulus(): untyped = modulusBuf.toOpenArray(0, modulusWordLen-1)
template output(): untyped = outputBuf.toOpenArray(0, modulusWordLen-1)

# Base deserialization
base.toOpenArray(0, baseWordLen-1).unmarshal(inputs.toOpenArray(baseStart, baseStop), WordBitWidth, bigEndian)
modulus.toOpenArray(0, modulusWordLen-1).unmarshal(inputs.toOpenArray(modStart, modStop), WordBitWidth, bigEndian)
template exponent(): untyped =
inputs.toOpenArray(expStart, expStop)

# Modulus deserialization
let realLen = paddedLengths.len + baseByteLen + exponentByteLen + modulusByteLen
let overflowLen = realLen - inputs.len
if overflowLen > 0:
let physLen = inputs.len-modStart # Length of data physically present (i.e. excluding padded zeros)
var paddedModBuf = allocStackArray(byte, modulusByteLen)
template paddedMod(): untyped = paddedModBuf.toOpenArray(0, modulusByteLen-1)

paddedMod.rawCopy(0, inputs, modStart, physLen)
zeroMem(paddedMod[physLen].addr, overflowLen)
modulus.unmarshal(paddedMod, WordBitWidth, bigEndian)
else:
modulus.unmarshal(inputs.toOpenArray(modStart, modStop), WordBitWidth, bigEndian)

# Computation
# ---------------------
Expand Down
36 changes: 36 additions & 0 deletions tests/t_ethereum_evm_modexp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,40 @@ suite "EVM ModExp precompile (EIP-198)":

var r = newSeq[byte](56)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success

test "Audit #18 - Handling of inputs infinitely right-padded with zeros (read past buffers or stack overflow for temporaries)":
let input = [
# Base length
uint8 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
# Exponent length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xa8, 0xfd,
# Modulus length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,

0xc1, 0x00, 0x00, 0x00, 0x51, 0x00, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x00, 0x50, 0x50, 0x50,
0x50, 0x50, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0xbc, 0x9b, 0xa0, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x00, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00,
0xa0]
var r = newSeq[byte](1)
let status = r.eth_evm_modexp(input)
doAssert status == cttEVM_Success

0 comments on commit c85ffb0

Please sign in to comment.