@@ -23,10 +23,26 @@ use alloc::vec::Vec;
2323use core:: cmp:: max;
2424use fp_evm:: LinearCostPrecompile ;
2525use evm:: { ExitSucceed , ExitError } ;
26- use num:: { BigUint , Zero , One } ;
26+ use num:: { BigUint , Zero , One , ToPrimitive , FromPrimitive } ;
2727
2828pub struct Modexp ;
2929
30+ // ModExp expects the following as inputs:
31+ // 1) 32 bytes expressing the length of base
32+ // 2) 32 bytes expressing the length of exponent
33+ // 3) 32 bytes expressing the length of modulus
34+ // 4) base, size as described above
35+ // 5) exponent, size as described above
36+ // 6) modulus, size as described above
37+ //
38+ //
39+ // NOTE: input sizes are arbitrarily large (up to 256 bits), with the expectation
40+ // that gas limits would be applied before actual computation.
41+ //
42+ // maximum stack size will also prevent abuse.
43+ //
44+ // see: https://eips.ethereum.org/EIPS/eip-198
45+
3046impl LinearCostPrecompile for Modexp {
3147 const BASE : usize = 15 ;
3248 const WORD : usize = 3 ;
@@ -39,23 +55,39 @@ impl LinearCostPrecompile for Modexp {
3955 return Err ( ExitError :: Other ( "input must contain at least 96 bytes" . into ( ) ) ) ;
4056 } ;
4157
58+ // reasonable assumption: this must fit within the Ethereum EVM's max stack size
59+ let max_size_big = BigUint :: from_u32 ( 1024 ) . expect ( "can't create BigUint" ) ;
60+
4261 let mut buf = [ 0 ; 32 ] ;
4362 buf. copy_from_slice ( & input[ 0 ..32 ] ) ;
44- let mut len_bytes = [ 0u8 ; 8 ] ;
45- len_bytes. copy_from_slice ( & buf[ 24 ..] ) ;
46- let base_len = u64:: from_be_bytes ( len_bytes) as usize ;
63+ let base_len_big = BigUint :: from_bytes_be ( & buf) ;
64+ if base_len_big > max_size_big {
65+ return Err ( ExitError :: Other ( "unreasonably large base length" . into ( ) ) ) ;
66+ }
4767
48- buf = [ 0 ; 32 ] ;
4968 buf. copy_from_slice ( & input[ 32 ..64 ] ) ;
50- len_bytes = [ 0u8 ; 8 ] ;
51- len_bytes. copy_from_slice ( & buf[ 24 ..] ) ;
52- let exp_len = u64:: from_be_bytes ( len_bytes) as usize ;
69+ let exp_len_big = BigUint :: from_bytes_be ( & buf) ;
70+ if exp_len_big > max_size_big {
71+ return Err ( ExitError :: Other ( "unreasonably large exponent length" . into ( ) ) ) ;
72+ }
5373
54- buf = [ 0 ; 32 ] ;
5574 buf. copy_from_slice ( & input[ 64 ..96 ] ) ;
56- len_bytes = [ 0u8 ; 8 ] ;
57- len_bytes. copy_from_slice ( & buf[ 24 ..] ) ;
58- let mod_len = u64:: from_be_bytes ( len_bytes) as usize ;
75+ let mod_len_big = BigUint :: from_bytes_be ( & buf) ;
76+ if mod_len_big > max_size_big {
77+ return Err ( ExitError :: Other ( "unreasonably large exponent length" . into ( ) ) ) ;
78+ }
79+
80+ // bounds check handled above
81+ let base_len = base_len_big. to_usize ( ) . expect ( "base_len out of bounds" ) ;
82+ let exp_len = exp_len_big. to_usize ( ) . expect ( "exp_len out of bounds" ) ;
83+ let mod_len = mod_len_big. to_usize ( ) . expect ( "mod_len out of bounds" ) ;
84+
85+ // input length should be at least 96 + user-specified length of base + exp + mod
86+ let total_len = base_len + exp_len + mod_len + 96 ;
87+ if input. len ( ) < total_len {
88+ let err_msg = format ! ( "expected at least {} bytes but received {}" , total_len, input. len( ) ) ;
89+ return Err ( ExitError :: Other ( err_msg. into ( ) ) ) ;
90+ }
5991
6092 // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first.
6193 let r = if base_len == 0 && mod_len == 0 {
@@ -72,6 +104,8 @@ impl LinearCostPrecompile for Modexp {
72104 let mod_start = exp_start + exp_len;
73105 let modulus = BigUint :: from_bytes_be ( & input[ mod_start..mod_start + mod_len] ) ;
74106
107+ // TODO: computation should only proceed if sufficient gas has been provided
108+
75109 if modulus. is_zero ( ) || modulus. is_one ( ) {
76110 BigUint :: zero ( )
77111 } else {
@@ -120,15 +154,51 @@ mod tests {
120154 }
121155
122156 #[ test]
123- fn test_simple_inputs ( ) {
157+ fn test_insufficient_input ( ) -> std:: result:: Result < ( ) , ExitError > {
158+
159+ let input = hex:: decode (
160+ "0000000000000000000000000000000000000000000000000000000000000001\
161+ 0000000000000000000000000000000000000000000000000000000000000001\
162+ 0000000000000000000000000000000000000000000000000000000000000001")
163+ . expect ( "Decode failed" ) ;
164+
165+ let cost: usize = 1 ;
166+
167+ match Modexp :: execute ( & input, cost) {
168+ Ok ( ( _, _) ) => {
169+ panic ! ( "Test not expected to pass" ) ;
170+ } ,
171+ Err ( e) => {
172+ assert_eq ! ( e, ExitError :: Other ( "expected at least 99 bytes but received 96" . into( ) ) ) ;
173+ Ok ( ( ) )
174+ }
175+ }
176+ }
177+
178+ #[ test]
179+ fn test_excessive_input ( ) -> std:: result:: Result < ( ) , ExitError > {
180+
181+ let input = hex:: decode (
182+ "1000000000000000000000000000000000000000000000000000000000000001\
183+ 0000000000000000000000000000000000000000000000000000000000000001\
184+ 0000000000000000000000000000000000000000000000000000000000000001")
185+ . expect ( "Decode failed" ) ;
124186
125- // ModExp expects the following as inputs:
126- // 1) 32 bytes expressing the length of base
127- // 2) 32 bytes expressing the length of exponent
128- // 3) 32 bytes expressing the length of modulus
129- // 4) base, size as described above
130- // 5) exponent, size as described above
131- // 6) modulus, size as described above
187+ let cost: usize = 1 ;
188+
189+ match Modexp :: execute ( & input, cost) {
190+ Ok ( ( _, _) ) => {
191+ panic ! ( "Test not expected to pass" ) ;
192+ } ,
193+ Err ( e) => {
194+ assert_eq ! ( e, ExitError :: Other ( "unreasonably large base length" . into( ) ) ) ;
195+ Ok ( ( ) )
196+ }
197+ }
198+ }
199+
200+ #[ test]
201+ fn test_simple_inputs ( ) {
132202
133203 let input = hex:: decode (
134204 "0000000000000000000000000000000000000000000000000000000000000001\
@@ -183,4 +253,31 @@ mod tests {
183253 }
184254 }
185255 }
256+
257+ #[ test]
258+ fn test_large_computation ( ) {
259+
260+ let input = hex:: decode (
261+ "0000000000000000000000000000000000000000000000000000000000000001\
262+ 0000000000000000000000000000000000000000000000000000000000000020\
263+ 0000000000000000000000000000000000000000000000000000000000000020\
264+ 03\
265+ fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\
266+ fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f")
267+ . expect ( "Decode failed" ) ;
268+
269+ let cost: usize = 1 ;
270+
271+ match Modexp :: execute ( & input, cost) {
272+ Ok ( ( _, output) ) => {
273+ assert_eq ! ( output. len( ) , 32 ) ; // should be same length as mod
274+ let result = BigUint :: from_bytes_be ( & output[ ..] ) ;
275+ let expected = BigUint :: parse_bytes ( b"1" , 10 ) . unwrap ( ) ;
276+ assert_eq ! ( result, expected) ;
277+ } ,
278+ Err ( _) => {
279+ panic ! ( "Modexp::execute() returned error" ) ; // TODO: how to pass error on?
280+ }
281+ }
282+ }
186283}
0 commit comments