1

SafeMath doesn't prevent overflow/underflow from user input.

Example:

function sub(uint8 a, uint8 b) public returns(uint8) {
   require(b <= a);
   uint8 c = a - b;
   return c;
}

Say the user inputs a == 30 and b == 260, it will pass, since by the time the scope of the function is executed, b has already been overflown to 4.

Is there no way of preventing that, other than preventing that input on the client side?

My point is that SafeMath is not nearly safe when you are dealing with user input. But checking that on the client side isn't safe either, since it's always possible to interact with a contract "directly", bypassing any front-end application.

What am I missing here?

7
  • 1
    by the time the scope of the function is executed - what does that mean??? The function is executed as a single transaction, start to end. Commented Jan 31, 2019 at 17:06
  • Sorry for being unclear. I mean that b will be "squeezed" into a uint8 before the 'require(b <= a);' line is executed. When b is received as a parameter that has to be a uint8, it will immediately overflow (260 in a uint8 will become 4, because a uint8 cannot be greater than 255). Commented Jan 31, 2019 at 17:12
  • Yes, I removed that part of my comment after deciphering that part (before you added yours). Commented Jan 31, 2019 at 17:16
  • But think, where exactly in your code can b overflow to 4? If anything, it would be c, since b is "untouched". Did you perhaps think that the value of b might change on the side of the caller? If yes, then please note that b is a local variable here (a copy of whatever value was sent from the other side, if you will). Commented Jan 31, 2019 at 17:19
  • Hum, maybe you're right. I tested it with Remix and assumed the EVM was causing the overflow when trying to accept b as a uint8 type parameter. If that is what happend, b is not untouched. But I'll try to check what you said. Maybe Remix is the cause of the overflow. Commented Jan 31, 2019 at 17:22

2 Answers 2

2

The point is that if you write ‘260’ for b you are not writing uint8 as in your example, but at least uint9.

This is not an overflow, but a wrong intended input, which maps in a normal input for the function.

From the point of view of the function nothing happened.

Any limited size uint representation is intended as limited to the representable universe for that representation.

It make not sense to worry about if a 20 bit address does not represent properly a 32 bit address: it is obvious.

You are worried about the fact that uint8 cannot map properly any uint9.

So what is wrong here?

The overflow (underflow in this case) should be the case of a:

uint(30) - uint(260) = ?

where both 30 and 260 are valid number in uint256 representation (I.e. uint).

6
  • Yeah, I guess you have a point. From the point of view of the function, nothing is wrong. Except it is accepting an out-of-range value as parameter (and the overflow happens 'naturally', so that it becomes in-range). Still, the problem remains. So, I gather it's just not possible to solve this problem 100%, it will always be possible to overflow using user input. I'll just wait a few more minutes to see if a new answer appears. Thanks. Commented Jan 31, 2019 at 17:18
  • 1
    You are welcome. As I said in the answer, you are worried about the fact that you are not able to proper represent any uint9 in uint8 representation. At that point why not be worried about to represent 262001 or 512987600 in uint8? It does not make sense from a strict technical point of view.
    – Rick Park
    Commented Jan 31, 2019 at 17:20
  • Fair enough, Rick. But really, why not? My concern is with user input (so 262001 or 512987600 would be equally worrying), but I'm already coming to terms with there being no silver bullet here. Commented Jan 31, 2019 at 17:27
  • The point is that you can have two equivalent 9 bit pattern that, if you omit the first one, are the same number. And four 10 bit pattern that, if you omit the first two, are the same number. And eight... etc etc. this is not a problem of underflow or overflow, but of wrong representation. It does not affect the uint8 functions in any way, because they have to manage properly any uint8 input, or equivalent input. Nobody can understand x-rays modulation using human eye: if you watch a red flag and someone overflow you by modulated x-rays you shall see a red flag only and you are right in seeing
    – Rick Park
    Commented Jan 31, 2019 at 17:35
  • The x-ray example was great, it actually made me understand the whole issue in another level. Maybe I could even see a purple flag after having been overflown, supposing that's what red + x-ray would map to in my representation universe. Awesome. Commented Jan 31, 2019 at 17:59
0

I believe you are simply reading it wrong.

The values are compared prior to any execution of subtraction.

Line 2 above, require(b <= a); will throw prior to the subtraction on line 3.

2
  • 1
    But when the number 260 is fit into a uint8, it overflows and becomes 4. By the time the require line is executed, b is already 4 (because it had to fit into a uint8 to be a valid parameter of the function). Isn't that what happens? Commented Jan 31, 2019 at 17:03
  • The latest version of SafeMath uses uint256. Commented Jan 31, 2019 at 17:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.