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

Time:12-27

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

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:

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");
 }

CodePudding user response:

It is called an implicit conversion.

From C standard:

Several operators convert operand values from one type to another automatically. This subclause specifies the result required from such an implicit conversion, as well as those that result from a cast operation (an explicit conversion ). The list in 6.3.1.8 summarizes the conversions performed by most ordinary operators; it is supplemented as required by the discussion of each operator in 6.5

Every integer type has an integer conversion rank defined as follows:

  • No two signed integer types shall have the same rank, even if they have the same representation.
  • The rank of a signed integer type shall be greater than the rank of any signed integer type with less precision.
  • The rank of long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater than the rank of short int, which shall be greater than the rank of signed char.
  • The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.
  • The rank of any standard integer type shall be greater than the rank of any extended integer type with the same width.
  • The rank of char shall equal the rank of signed char and unsigned char.
  • The rank of _Bool shall be less than the rank of all other standard integer types.
  • The rank of any enumerated type shall equal the rank of the compatible integer type (see 6.7.2.2).
  • The rank of any extended signed integer type relative to another extended signed integer type with the same precision is implementation-defined, but still subject to the other rules for determining the integer conversion rank.
  • For all integer types T1, T2, and T3, if T1 has greater rank than T2 and T2 has greater rank than T3, then T1 has greater rank than T3.
  1. 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 v alues 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.58) All other types are unchanged by the integer promotions.
  1. The integer promotions preserve value including sign. As discussed earlier, whether a ‘‘plain’’ char is treated as signed is implementation-defined.

You cant avoid implicit conversion but you can cast the result of the operation to the required type

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

https://godbolt.org/z/39Exa8E7K

But this conversion invokes Undefined Behaviour as your short integer overflows. You have to be very careful doing it as it can be a source of very hard to find and debug errors.

  •  Tags:  
  • c
  • Related