Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How you avoid implicit conversion from short to integer during addition?

Tags:

c

I'm doing a few integer for myself, where I'm trying to fully understand integer overflow.

I kept reading about how it can be dangerous to mix integer types of different sizes. For that reason i wanted to have an example where a short would overflow much faster than a int. Here is the snippet:

unsigned int longt;
longt = 65530;
unsigned short shortt;
shortt = 65530;

 if (longt > (shortt+10)){
     printf("it is bigger");
 }

But the if-statement here is not being run, which must mean that the short is not overflowing. Thus I conclude that in the expression shortt+10 a conversion happens from short to integer.

This is a bit strange to me, when the if statement evaluates expressions, does it then have the freedom to assign a new integer type as it pleases?

I then thought that if I was adding two short's then that would surely evaluate to a short:

unsigned int longt;
longt = 65530;
unsigned short shortt;
shortt = 65530;
shortt = shortt;
short tmp = 10;


 if (longt > (shortt+tmp)){
     printf("Ez bigger");
 }

But alas, the proporsition still evaluates to false.

I then try do do something where I am completely explicit, where I actually do the addition into a short type, this time forcing it to overflow:

unsigned int longt;
longt = 65530;
unsigned short shortt;
shortt = 65530;
shortt = shortt;
short tmp = shortt + 10;

 if (longt > tmp){
     printf("Ez bigger");
 }

Finally this worked, which also would be really annoying if it did'nt.

This flusters me a little bit though, and it reminds me of a ctf exercise that I did a while back, where I had to exploit this code snippet:

#include <stdio.h>
int main() {
    int impossible_number;
    FILE *flag;
    char c;
    if (scanf("%d", &impossible_number)) {
        if (impossible_number > 0 && impossible_number > (impossible_number + 1)) {
            flag = fopen("flag.txt","r");
            while((c = getc(flag)) != EOF) {
                printf("%c",c);
            }
        }
    }
    return 0;
}

Here, youre supposed to trigger a overflow of the "impossible_number" variable which was actually possible on the server that it was deployed upon, but would make issues when run locally.

    int impossible_number;
    FILE *flag;
    char c;
    if (scanf("%d", &impossible_number)) {
        if (impossible_number > 0 && impossible_number > (impossible_number + 1)) {
            flag = fopen("flag.txt","r");
            while((c = getc(flag)) != EOF) {
                printf("%c",c);
            }
        }
    }
    return 0;

You should be able to give "2147483647" as input, and then overflow and hit the if statement. However this does not happen when run locally, or running at an online compiler. I don't get it, how do you get an expression to actually overflow the way that is is actually supossed to do, like in this example from 247ctf?

I hope someone has a answer for this

like image 415
Grazosi Avatar asked Dec 26 '21 23:12

Grazosi


People also ask

What types can int be implicitly converted to?

Any integral numeric type is implicitly convertible to any floating-point numeric type. There are no implicit conversions to the byte and sbyte types. There are no implicit conversions from the double and decimal types. There are no implicit conversions between the decimal type and the float or double types.

What happens in implicit conversion?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.

Can float be implicitly converted to int?

Real floating-integer conversionsA finite value of any real floating type can be implicitly converted to any integer type.

Can there be loss of data as a result of an implicit conversion?

"It is possible for implicit conversions to lose information, signs can be lost (when signed is implicitly converted to unsigned), and overflow can occur (when long long is implicitly converted to float)."


3 Answers

How you avoid implicit conversion from short to integer during addition?

You don't.

C has no arithmetic operations on integer types narrower than int and unsigned int. There is no + operator for type short.

Whenever an expression of type short is used as the operand of an arithmetic operator, it is implicitly converted to int.

For example:

short s = 1;
s = s + s;

In s + s, s is promoted from short to int and the addition is done in type int. The assignment then implicitly converts the result of the addition from int to short.

Some compilers might have an option to enable a warning for the narrowing conversion from int to short, but there's no way to avoid it.

like image 162
Keith Thompson Avatar answered Oct 26 '22 23:10

Keith Thompson


What you're seeing is a result of integer promotions. What this basically means it that anytime an integer type smaller than int is used in an expression it is converted to int.

This is detailed in section 6.3.1.1p2 of the C standard:

The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.
  • A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions

That is what's happening here. So let's look at the first expression:

if (longt > (shortt+10)){

Here we have a unsigned short with value 65530 being added to the constant 10 which has type int. The unsigned short value is converted to an int value, so now we have the int value 65530 being added to the int value 10 which results in the int value 65540. We now have 65530 > 65540 which is false.

The same happens in the second case where both operands of the + operator are first promoted from unsigned short to int.

In the third case, the difference happens here:

short tmp = shortt + 10;

On the right side of the assignment, we still have the int value 65540 as before, but now this value needs to be assigned back to a short. This undergoes an implementation defined conversion to short, which is detailed in section 6.3.1.3:

1 When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.

2 Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

Paragraph 3 takes effect in this particular case. In most implementations you're likely to come across, this will typically mean "wraparound" of the value.

So how do you work with this? The closest thing you can do is either what you did, i.e. assign the intermediate result to a variable of the desired type, or cast the intermediate result:

if (longt > (short)(shortt+10)) {

As for the "impossible" input in the CTF example, that actually causes signed integer overflow as a result of the the addition, and that triggers undefined behavior. For example, when I ran it on my machine, I got into the if block if I compiled with -O0 or -O1 but not with -O2.

like image 21
dbush Avatar answered Oct 27 '22 00:10

dbush


How you avoid implicit conversion from short to integer during addition?

Not really avoidable.

On 16-bit and wider machines, the conversion short to int and unsigned short to unsigned does not affect the value. But addition overflow and the implicit conversion from int to unsigned renders a different result in 16-but vs. 32-bit for OP's values. For in 16-bit land, unsigned short to int does not implicitly occur. Instead, code does unsigned short to unsigned.

int/unsigned as 16-bit

If int/unsigned were 16-bit -common on many embedded processors, then shortt would not convert to an int, but to unsigned.

// Given 16-bit int/unsigned
unsigned int longt;
longt = 65530;  // 32-bit long constant assigned to 16-bit unsigned - no value change as value in range.
unsigned short shortt;
shortt = 65530; // 32-bit long constant assigned to 16-bit unsigned short - no value change as value in range.

// (shortt+10)
// shortt+10 is a unsigned short + int
// unsigned short promotes to unsigned - no value change.
// Then since unsigned + int, the int 10 converts to unsigned 10 - no value change.
// unsigned  65530 + unsigned 10 exceeds unsigned range so 65536 subtracted.
// Sum is 4.

// Statment is true.
if (longt > (shortt+10)){
  printf("it is bigger");
 }
like image 25
chux - Reinstate Monica Avatar answered Oct 26 '22 23:10

chux - Reinstate Monica