Still on the journey providing mentorship to the SANS/Ryerson/Rogers Cyber Secure Catalyst Program.
In this post, the ask was to explain integer overflow/underflow.
Keeping it simple! The basic ideas, in the case of a 32-bit system or code compiled as 32 bits, an integer signed or unsigned, will occupy 32 bits. If we calculate 2**32 we get 4,294,967,296 possible values. In the case of unsigned int, this means, we should be able to get 0 to 4,294,967,295 as the possible values, as unsigned int only allows for positive values. Signed int on the other hand, allows for negative numbers and have a range of −2,147,483,648 to 2,147,483,647. Note, this problem is not only about 32-bit integer the idea is basically applying a value larger to a datatype than it was designed to accommodate. Int is just the example used in this case but it could have been something else.
Let's define a small program to learn more:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <stdio.h>
int main()
{
// Define a variable
unsigned int my_num = 429496725;
// Get the size of an integer for this compiled code
printf("Size of my_num is :%d bytes or %d bits \n", sizeof(my_num), sizeof(my_num) * 8);
// Print the value of my number to the screen
printf("Current value for my_num is: %u \n", my_num);
return 0;
}
|
Let's compile and run this small program
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num is :4 bytes or 32 bits
Current value for my_num is: 429496725
|
Above we see the size of the my_num integer is 4 bytes or 32 bits. We also see the current value is set to the maximum value possible for a 32-bit unsigned int.
What would happen if we add 1 to this program.
Here is the updated code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <stdio.h>
int main()
{
// Define a variable
int my_num = 4294967295;
// Get the size of an integer for this compiled code
printf("Size of my_num is :%d bytes or %d bits \n", sizeof(my_num), sizeof(my_num) * 8);
// Print the value of my number to the screen
printf("%u + 1 = %u \n", my_num, my_num + 1);
return 0;
}
|
When compiled and run, we get:
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num is :4 bytes or 32 bits
4294967295 + 1 = 0
|
Realistically, that value should have been 4294967295 + 1 = 4294967296.
It looks like we hit the overflow.
What would happen if we update the code to add 10. Here we see:
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num is :4 bytes or 32 bits
4294967295 + 10 = 9
|
From above, we see we are wrapping around. Previously when we went over by 1 the number did 1-1 which gave a result of 0. When we went over by 10, our answer is 9.
Anyhow that's it for the unsigned int overflow.
Signed int
As discussed above, in both the signed and unsigned int perspective, the size is 4 bytes or 32 bits. The difference between signed and unsigned, is that signed can accommodate negative numbers. While the range for unsigned is 0 to 4,294,967,295 signed values are from −2,147,483,648 to 2,147,483,647. Let's take the max and - 0 from it.
Here is the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <stdio.h>
int main()
{
// Define a signed integer. Notice the "-"
signed int my_num_signed = -2147483648;
// Get the size of an signed integer for this compiled code
printf("Size of my_num_signed is :%d bytes or %d bits \n", sizeof(my_num_signed), sizeof(my_num_signed) * 8);
// Print the value of my number to the screen
printf("%d - 0 = %d \n", my_num_signed, my_num_signed -0);
return 0;
}
|
When compiled, here is what we have:
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num_signed is :4 bytes or 32 bits
-2147483648 - 0 = -2147483648
|
The above is expected as -2147483648 - 0 = -2147483648.
Let's modify the code to -1
1
2
3
4
5
6
7
8
9
10
11
12
13 | #include <stdio.h>
int main()
{
signed int my_num_signed = -2147483648;
// Get the size of an signed integer for this compiled code
printf("Size of my_num_signed is :%d bytes or %d bits \n", sizeof(my_num_signed), sizeof(my_num_signed) * 8);
// Print the value of my number to the screen
printf("%d - 1 = %d \n", my_num_signed, my_num_signed -1);
return 0;
}
|
When this is compiled we see
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num_signed is :4 bytes or 32 bits
-2147483648 - 1 = 2147483647
|
Oh! oh! oh! oh! What we expect is that -2147483648 -1 should equal -2,147,483,649. Therefore, above resulted in an underflow.
Similarly, if we -10 we see:
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num_signed is :4 bytes or 32 bits
-2147483648 - 10 = 2147483638
|
Let's wrap up with one more. This time, taking the max number on the positive side of the signed values.
Here is the code
1
2
3
4
5
6
7
8
9
10
11
12
13 | #include <stdio.h>
int main()
{
signed int my_num_signed = 2147483647;
// Get the size of an signed integer for this compiled code
printf("Size of my_num_signed is :%d bytes or %d bits \n", sizeof(my_num_signed), sizeof(my_num_signed) * 8);
// Print the value of my number to the screen
printf("%d + 1 = %d \n", my_num_signed, my_num_signed + 1);
return 0;
}
|
When compiled and run we see ...
1
2
3
4 | ┌──(kali㉿securitynik)-[~]
└─$ gcc intOverflow.c -o intOverflow -m32 && ./intOverflow
Size of my_num_signed is :4 bytes or 32 bits
2147483647 + 1 = -2147483648
|
The above is obviously wrong as the sum of two positive numbers, should never give you a negative number.
That's it for this post.
References:
No comments:
Post a Comment