NAZMUL HUSSAIN
NAZMUL HUSSAIN

Reputation: 49

How I can handle integer overflow?

I am trying to handle integer overflow. My code is :

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<limits.h>

int isInt (char *s)
{
 char *ep = NULL;
 long i = strtol (s, &ep, 10);
 if ((*ep == 0) || (!strcmp(ep,"\n")))
    return 1;  // it's an int

 return 0;  
} 

int main()
{
char *buffer = NULL;
size_t count = 0;
ssize_t ret;
//AMINO *a_acid;
int num;

for(;;)
{   
printf("Please enter an integer:");
if((ret = getline(&buffer, &count, stdin)) < 0)
{
    perror("getline: error\n");
    free(buffer);
    exit(EXIT_FAILURE);
}

if(!isInt(buffer))
{
    perror("you are not entering int , Try again:");
    continue;
}
sscanf(buffer, "%d",&num);
printf("%d\n", num);
if ((num > INT_MAX)|| (num < 0))
{
    perror("you overflowed int variable , Try again:\n ");
    continue;
}
break;
}

}

Now I was checking how this code is responding. And I saw something weird.When I am entering so big number, then it is detected. But sometimes is not getting detected. Here is my terminal view:

> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ gcc torson.c 
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$ ./a.out
> Please enter an integer:ksdjfjklh 
> you are not entering int , Try again:: Success
> Please enter an integer:338479759475637465765
> -1 
> you overflowed int variable , Try again:  : Numerical result out of  
> range 
> Please enter an integer:58678946895785 
> 1103697833
> nazmul@nazmul-Lenovo-G50-80:~/2nd_sem/biophysics$

*Why it is working for this number 338479759475637465765. But it is not working for 58678946895785. logic , I used in my program, is when it is out of bound, then int variable gives some -1 or negative value. I read many article, still it is not quite clear.

Upvotes: 2

Views: 203

Answers (4)

chux
chux

Reputation: 154169

If one must use sscanf() to detect int overflow rather than the robust strtol(), there is a cumbersome way.

Use a wider type and a width limit to prevent overflow when scanning.

bool isint_via_sscanf(const char *s) {
  long long y;
  int n = 0;
  if (sscanf(s, "18%lld %n", &y, &n) != 1) {  // Overflow not possible
    return false;  // Conversion failed 
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  if (s[n]) {
    return false;  // Trailing junk
  }

  return true;  
}

It is insufficient on rare platforms where INT_MAX > 1e18.

It also incorrectly returns input like "lots of leading space and/or lot of leading zeros 000123" as invalid.

More complex code using sscanf() can address these short-comings, yet the best approach is strto*().

Upvotes: 1

strtol converts the value to a long int, whose range might be distinct from int. Furthermore, it returns LONG_MAX or LONG_MIN if the value could be converted but is outside the range for long int. In that case, errno will be set to ERANGE (but not otherwise!) Also, in the case of matching failure the value returned is 0, but errno is not set; but the ep points to the beginning of the string.

int isInt (char *s)
{
   char *ep = NULL;

   // zero errno first!
   errno = 0;
   long i = strtol (s, &ep, 10);
   if (errno) {
       return 0;
   }

   // matching failure.
   if (ep == s) {
       return 0;
   }

   // garbage follows
   if (! ((*ep == 0) || (!strcmp(ep,"\n")))) {
      return 0;
   }

   // it is outside the range of `int`
   if (i < INT_MIN || i > INT_MAX) {
      return 0;
   }

   return 1; 
} 

What dbush says about the use of perror is correct, though. strtol sets an error only in case of long overflow, which is not the only possible failing case in your function, so perror could print anything like Is a directory or Multihop attempted.

Upvotes: 2

chux
chux

Reputation: 154169

sscanf(buffer, any_format_without_width, &anytype); is not sufficient to detect overflow.

if the result of the conversion cannot be represented in the object, the behavior is undefined. C11dr §7.21.6.2 10

Do not use *scanf() family to detect overflow. It may work in select cases, but not in general.


Instead use strto**() functions. Yet even OP's isInt() is mis-coded as it incorrectly assess isInt("\n"), isInt(""), isInt("999..various large values ...999") as good ints.

Alternative:

bool isint_alt(const char *s) {
  char *endptr;
  errno = 0;
  long y = strtol(s, &endptr, 10);

  if (s == endptr) {
    return false; // No conversion
  }

  if (errno == ERANGE) {
    return false; // Outside long range
  }

  if (y < INT_MIN || y > INT_MAX) {
    return false; // Outside int range
  }

  // Ignore trailing white space
  while (isspace((unsigned char)*endptr)) {
    endptr++;
  }

  if (*endptr) {
    return false; // Trailing junk
  }

  return true;
}

Upvotes: 2

dbush
dbush

Reputation: 224942

You're getting your types mixed up.

In the isInt function you use strtol, which return a long to check the value. Then in your main function you use sscanf with %d, which reads into an int.

On your system, it seems that a long is 64 bits while an int is 32 bits. So strtol fails to fully convert 338479759475637465765 because it is larger than a 64 bit variable can hold. Then you try to convert 58678946895785 which will fit in a 64 bit variable but not a 32 bit variable.

You should instead have sscanf read into a long. Then you can compare the value against INT_MAX:

long num;
...
sscanf(buffer, "%ld", &num);
printf("%ld\n", num);
if ((num > INT_MAX)|| (num < INT_MIN))
{
    printf("you overflowed int variable , Try again:\n ");
    continue;
}

Also note that it doesn't make sense to call perror here. You only use it right after calling a function which sets errno.

Upvotes: 1

Related Questions