Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overflow occurs, but errno is not ERANGE. How does this happen?

Tags:

c

overflow

errno

I'm trying to make use of errno to detect if I've performed an operation that causes overflow. However, although I've written a function which deliberately overflows, errno == ERANGE is false. What's going on here?

Here's the code:

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0;
    int i;

    for (i = 0; i< 300; i++) {
        errno = 0;
        c = c + 1;
        if (errno == ERANGE) {// we have a range error
            printf("Overflow. c = %u\n", c);
        } else {
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}

I expected this to give an overflow error at the point where we added one to 255, but there is no error. Here's the (truncated) output:

No error. c = 245
No error. c = 246
No error. c = 247
No error. c = 248
No error. c = 249
No error. c = 250
No error. c = 251
No error. c = 252
No error. c = 253
No error. c = 254
No error. c = 255
No error. c = 0
No error. c = 1
No error. c = 2
No error. c = 3
No error. c = 4
No error. c = 5
No error. c = 6
No error. c = 7
No error. c = 8
No error. c = 9

Can someone explain why it doesn't detect the error, and how I might change it or otherwise make a function that can detect if my value has overflowed? Note: ultimately I want to be doing this with long ints, so it's not possible to simply convert it to a larger data type.

EDIT:

I've since found some simple functions to detect overflow from addition and multiplication of integral data types, respectively. It doesn't cover all situations, but it covers a lot of them.

Multiplication:

int multOK(long x, long y)
/* Returns 1 if x and y can multiply without overflow, 0 otherwise */
{
    long p = x*y;

    return !x || p/x == y;
}

Signed addition:

int addOK(long x, long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    long sum = x+y;
    int neg_over = (x < 0) && (y < 0) && (sum >= 0);
    int pos_over = (x >= 0) && (y >= 0) && (sum < 0);
    return !neg_over && !pos_over;
}

Unsigned addition:

int unsignedAddOK(unsigned long x, unsigned long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    unsigned long sum = x+y;

    return sum > x && sum > y;
}
like image 333
limp_chimp Avatar asked Oct 13 '12 01:10

limp_chimp


People also ask

How does integer overflow happen?

An integer overflow occurs when you attempt to store inside an integer variable a value that is larger than the maximum value the variable can hold. The C standard defines this situation as undefined behavior (meaning that anything might happen).

What happens when data overflow?

In general, a data type overflow error is when the data type used to store data was not large enough to hold the data. Furthermore, some data types can only store numbers up to a certain size. An overflow error will be produced, for example, if a data type is a single byte and the data to be stored is greater than 256.

How integer overflow will occur when you will work with 64 bits integer data?

But in a 64-bit build, the compiler upcasts the unsigned int to a signed 64-bit type, meaning there is no integer overflow and the expected path in the program is executed.

What happens when an unsigned integer variable overflows?

In general, when an unsigned int overflows, it rolls over to zero. So UINT_MAX + 5 rolls over and becomes 4. It would be the difference between the max uint value and the value of what would have been the overflow value.


2 Answers

Errno is not an processor-set variable, it is only used by system calls and some standard functions to repport errors.

I don't know any technique for detecting what you ask, exept from verifying if c < c + 1 on each loop iteration.

Edit : After some research, I found this wikipedia about status registers, which are CPU flags that indicates such errors.

like image 117
tomahh Avatar answered Sep 29 '22 07:09

tomahh


From what I understand, errno is set by functions. It wouldn't detect addition overflow errors unless you create your own function that does. You can test to see if the result is less than one of your operands.

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0, result = 0;
    int i;

    for (i = 0; i< 300; i++) {
        result = c + 1;
        if (result < c) {// we have a range error
            printf("Overflow. c = %u\n", result);
        } else {
            c = result;
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}
like image 23
NullEntity Avatar answered Sep 29 '22 08:09

NullEntity